BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
RealSpaceBuilderUtils.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/Realspace/RealSpaceBuilderUtils.cpp
6 //! @brief Implements GUI::RealSpace::BuilderUtils namespace
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
16 #include "Base/Const/Units.h"
17 #include "Base/Util/Assert.h"
29 #include "Resample/Particle/ReParticle.h"
30 #include "Sample/Particle/MesoCrystal.h"
31 #include "Sample/Particle/Particle.h"
32 #include "Sample/Particle/ParticleCoreShell.h"
33 
34 namespace {
35 
36 const double layerBorderWidth = 10.0;
37 
38 const IFormFactor* getUnderlyingFormFactor(const IFormFactor* ff)
39 {
40  // TRUE as long as ff is of ReParticle (or its derived) type
41  while (dynamic_cast<const ReParticle*>(ff))
42  ff = dynamic_cast<const ReParticle*>(ff)->formfactor_at_bottom();
43  const auto* ffb = dynamic_cast<const IFormFactor*>(ff);
44  ASSERT(ffb);
45  return ffb;
46 }
47 
48 R3 to_kvector(const QVector3D& origin)
49 {
50  return R3(static_cast<double>(origin.x()), static_cast<double>(origin.y()),
51  static_cast<double>(origin.z()));
52 }
53 
54 } // namespace
55 
56 
58  std::function<QColor(const QString&)> fnColorFromMaterialName)
59  : m_fnColorFromMaterialName(fnColorFromMaterialName)
60 {
61  ASSERT(fnColorFromMaterialName);
62 }
63 
64 // compute cumulative abundances of particles
65 QVector<double>
67 {
68  // Retrieving abundances of particles
69  double total_abundance = 0.0;
70  QVector<double> cumulative_abundances;
71 
72  for (auto* particle : layoutItem.particles()) {
73  total_abundance += particle->abundance();
74 
75  cumulative_abundances.append(total_abundance);
76  }
77 
78  return cumulative_abundances;
79 }
80 
82  const std::vector<std::vector<double>>& lattice_positions,
83  const std::vector<Particle3DContainer>& particle3DContainer_vector,
84  GUI::RealSpace::Model* model, const SceneGeometry& sceneGeometry,
85  const RealSpaceBuilder* builder3D)
86 {
87  double layer_size = sceneGeometry.layerSize;
88  double layer_thickness = sceneGeometry.topOrBottomLayerThickness;
89 
90  for (std::vector<double> position : lattice_positions) {
91  // for random selection of particles based on their abundances
92  double rand_num = (rand() / static_cast<double>(RAND_MAX)); // (between 0 and 1)
93  int k = 0;
94 
95  for (const auto& particle3DContainer : particle3DContainer_vector) {
96  if (rand_num <= particle3DContainer.cumulativeAbundance()) {
97  // lattice position + location (TO BE ADDED)
98  double pos_x = position[0];
99  double pos_y = position[1];
100  double pos_z = 0;
101 
102  if (std::abs(pos_x) <= layer_size - layerBorderWidth
103  && std::abs(pos_y) <= layer_size - layerBorderWidth
104  && std::abs(pos_z) <= layer_thickness) {
106  model, particle3DContainer,
107  QVector3D(static_cast<float>(position[0]), static_cast<float>(position[1]),
108  static_cast<float>(0)));
109  }
110  break;
111  }
112  ++k;
113  }
114  }
115 }
116 
117 // Implement Rotation of a 3D particle using parameters from IRotation Object
120 {
121  double alpha = 0.0;
122  double beta = 0.0;
123  double gamma = 0.0;
124 
125  if (const auto* rotX = dynamic_cast<const RotationX*>(rotation)) {
126  beta = rotX->angle(); // about x-axis
127  } else if (const auto* rotY = dynamic_cast<const RotationY*>(rotation)) {
128  alpha = Units::deg2rad(90.0);
129  beta = rotY->angle(); // about y-axis
130  gamma = Units::deg2rad(-90.0);
131  } else if (const auto* rotZ = dynamic_cast<const RotationZ*>(rotation)) {
132  alpha = rotZ->angle(); // about z-axis
133  } else if (const auto* rotEuler = dynamic_cast<const RotationEuler*>(rotation)) {
134  alpha = rotEuler->alpha();
135  beta = rotEuler->beta();
136  gamma = rotEuler->gamma();
137  }
138  return GUI::RealSpace::Vector3D(static_cast<float>(alpha), static_cast<float>(beta),
139  static_cast<float>(gamma));
140 }
141 
143  const Particle& particle, GUI::RealSpace::Particles::Particle& particle3D, const R3& origin)
144 {
145  // rotation
146  GUI::RealSpace::Vector3D particle_rotate;
147  const IRotation* rotation = particle.rotation();
148 
149  if (rotation)
150  particle_rotate = implementParticleRotationfromIRotation(rotation);
151 
152  // translation
153  float x = static_cast<float>(particle.particlePosition().x());
154  float y = static_cast<float>(particle.particlePosition().y());
155  float z = static_cast<float>(particle.particlePosition().z());
156  GUI::RealSpace::Vector3D position(x + static_cast<float>(origin.x()),
157  y + static_cast<float>(origin.y()),
158  z + static_cast<float>(origin.z()));
159 
160  // If the particle belongs to a particle composition, along with the particle's
161  // intrinsic transformations, position() and rotation() methods also account for the
162  // translation and rotation (if present) of the particle composition as the
163  // particleComposition's decompose() method already does this
164 
165  particle3D.addTransform(particle_rotate, position);
166 }
167 
169  const Particle& particle, GUI::RealSpace::Particles::Particle& particle3D,
170  const ParticleCoreShell& particleCoreShell, const R3& origin)
171 {
172  std::unique_ptr<Particle> P_clone(particle.clone()); // clone of the current particle
173 
174  // rotation
175  GUI::RealSpace::Vector3D particle_rotate;
176  const IRotation* rotationCoreShell = particleCoreShell.rotation();
177 
178  if (rotationCoreShell)
179  P_clone->rotate(*rotationCoreShell);
180 
181  const IRotation* rotation = P_clone->rotation();
182 
183  if (rotation)
184  particle_rotate = implementParticleRotationfromIRotation(rotation);
185 
186  // translation
187  R3 positionCoreShell = particleCoreShell.particlePosition();
188 
189  P_clone->translate(positionCoreShell);
190 
191  GUI::RealSpace::Vector3D position(
192  static_cast<float>(P_clone->particlePosition().x() + origin.x()),
193  static_cast<float>(P_clone->particlePosition().y() + origin.y()),
194  static_cast<float>(P_clone->particlePosition().z() + origin.z()));
195 
196  particle3D.transform(particle_rotate, position);
197 }
198 
200  const Particle& particle, GUI::RealSpace::Particles::Particle& particle3D, double alpha)
201 {
202  // assign correct color to the particle from the knowledge of its material
203  const Material* particle_material = particle.material();
204  auto color =
205  m_fnColorFromMaterialName(QString::fromStdString(particle_material->materialName()));
206  color.setAlphaF(alpha);
207  particle3D.color = color;
208 }
209 
210 std::vector<Particle3DContainer>
212  const QVector3D& origin)
213 {
214  std::vector<Particle3DContainer> particle3DContainer_vector;
215 
216  double total_abundance = computeCumulativeAbundances(layoutItem).last();
217 
218  double cumulative_abundance = 0;
219 
220  for (auto* particleItem : layoutItem.particles()) {
221  Particle3DContainer particle3DContainer;
222 
223  if (const auto* pItem = dynamic_cast<ParticleItem*>(particleItem)) {
224  auto particle = pItem->createParticle();
225  particle3DContainer = singleParticle3DContainer(*particle, total_abundance, origin);
226  } else if (const auto* coreShellItem = dynamic_cast<ParticleCoreShellItem*>(particleItem)) {
227  // If there is no CORE or SHELL to populate inside ParticleCoreShellItem
228  if (!coreShellItem->core() || !coreShellItem->shell())
229  continue;
230  auto particleCoreShell = coreShellItem->createParticleCoreShell();
231  particle3DContainer =
232  particleCoreShell3DContainer(*particleCoreShell, total_abundance, origin);
233  } else if (const auto* compositionItem =
234  dynamic_cast<ParticleCompositionItem*>(particleItem)) {
235  // If there is no particle to populate inside ParticleCompositionItem
236  if (!compositionItem->particles().empty())
237  continue;
238  auto particleComposition = compositionItem->createParticleComposition();
239  particle3DContainer =
240  particleComposition3DContainer(*particleComposition, total_abundance, origin);
241  } else if (const auto* mesoCrystalItem = dynamic_cast<MesoCrystalItem*>(particleItem)) {
242  // If there is no particle to populate inside MesoCrystalItem
243  if (!mesoCrystalItem->basisParticle())
244  continue;
246  *mesoCrystalItem, total_abundance, origin);
247  }
248 
249  cumulative_abundance += particle3DContainer.cumulativeAbundance();
250  particle3DContainer.setCumulativeAbundance(cumulative_abundance);
251  particle3DContainer_vector.emplace_back(particle3DContainer);
252  }
253 
254  return particle3DContainer_vector;
255 }
256 
258  const Particle& particle, double total_abundance, const QVector3D& origin)
259 {
260  std::unique_ptr<Particle> P_clone(particle.clone()); // clone of the particle
261 
262  const IFormFactor* ff = getUnderlyingFormFactor(P_clone->formfactor_at_bottom());
263 
265  applyParticleTransformations(*P_clone, *particle3D, to_kvector(origin));
266  applyParticleColor(*P_clone, *particle3D);
267 
268  Particle3DContainer singleParticle3DContainer;
269  singleParticle3DContainer.addParticle(particle3D.release(), false);
270  singleParticle3DContainer.setCumulativeAbundance(P_clone->abundance() / total_abundance);
271  singleParticle3DContainer.setParticleType("Particle");
272 
273  return singleParticle3DContainer;
274 }
275 
277  const ParticleCoreShell& particleCoreShell, double total_abundance, const QVector3D& origin)
278 {
279  // clone of the particleCoreShell
280  std::unique_ptr<ParticleCoreShell> PCS_clone(particleCoreShell.clone());
281 
282  const auto* coreff = getUnderlyingFormFactor(PCS_clone->coreParticle()->formfactor_at_bottom());
283  const auto* shellff =
284  getUnderlyingFormFactor(PCS_clone->shellParticle()->formfactor_at_bottom());
285 
286  auto coreParticle3D = GUI::View::TransformTo3D::createParticlefromFormfactor(coreff);
287  auto shellParticle3D = GUI::View::TransformTo3D::createParticlefromFormfactor(shellff);
288 
289  // core
290  applyParticleCoreShellTransformations(*PCS_clone->coreParticle(), *coreParticle3D, *PCS_clone,
291  to_kvector(origin));
292  applyParticleColor(*PCS_clone->coreParticle(), *coreParticle3D);
293 
294  // shell (set an alpha value of 0.5 for transparency)
295  applyParticleCoreShellTransformations(*PCS_clone->shellParticle(), *shellParticle3D, *PCS_clone,
296  to_kvector(origin));
297  applyParticleColor(*PCS_clone->shellParticle(), *shellParticle3D, 0.5);
298 
299  Particle3DContainer particleCoreShell3DContainer;
300 
301  particleCoreShell3DContainer.addParticle(coreParticle3D.release(), false); // index 0
302  particleCoreShell3DContainer.addParticle(shellParticle3D.release(), true); // index 1
303  particleCoreShell3DContainer.setCumulativeAbundance(PCS_clone->abundance() / total_abundance);
304  particleCoreShell3DContainer.setParticleType("ParticleCoreShell");
305 
306  return particleCoreShell3DContainer;
307 }
308 
310  const ParticleComposition& particleComposition, double total_abundance, const QVector3D& origin)
311 {
312  // clone of the particleComposition
313  std::unique_ptr<ParticleComposition> PC_clone(particleComposition.clone());
314 
315  Particle3DContainer result;
316 
317  for (const auto& pc_particle : PC_clone->decompose()) {
318  ASSERT(pc_particle);
319  Particle3DContainer particle3DContainer;
320  // no abundances are associated with the individual components of ParticleComposition
321  if (const auto* p = dynamic_cast<const ParticleCoreShell*>(pc_particle.get())) {
322  particle3DContainer = particleCoreShell3DContainer(*p, 1.0, origin);
323  } else if (dynamic_cast<const MesoCrystal*>(pc_particle.get())) {
324  // TODO: Implement method to populate MesoCrystal from CORE and NOT from MesoCrystalItem
325  // as it is done currently in GUI::RealSpace::BuilderUtils::mesoCrystal3DContainer
326  std::ostringstream ostr;
327  ostr << "Sorry, MesoCrystal inside ParticleComposition not yet implemented";
328  ostr << "\n\nStay tuned!";
329  throw std::runtime_error(ostr.str());
330  } else if (const auto* p = dynamic_cast<const Particle*>(pc_particle.get()))
331  particle3DContainer = singleParticle3DContainer(*p, 1.0, origin);
332  else
333  ASSERT(0);
334 
335  // add particles from 3Dcontainer of core-shell/particle into result
336  for (size_t i = 0; i < particle3DContainer.containerSize(); ++i) {
337  result.addParticle(particle3DContainer.createParticle(i).release(),
338  particle3DContainer.particle3DBlend(i));
339  }
340  }
341  // set the correct abundance for the entire ParticleComposition
342  result.setCumulativeAbundance(PC_clone->abundance() / total_abundance);
343  result.setParticleType("ParticleComposition");
344  return result;
345 }
346 
348  const MesoCrystalItem& mesoCrystalItem, double total_abundance, const QVector3D& origin)
349 {
350  RealSpaceMesoCrystal mesoCrystalUtils(&mesoCrystalItem, total_abundance, origin,
351  m_fnColorFromMaterialName);
352 
353  Particle3DContainer mesoCrystal3DContainer = mesoCrystalUtils.populateMesoCrystal();
354 
355  return mesoCrystal3DContainer;
356 }
Defines class DoubleDescriptor.
Defines class MesoCrystalItem.
Implements namespace GUI::View::TransformTo3D.
Defines class ParticleCompositionItem.
Defines class ParticleCoreShellItem.
Defines class ParticleItem.
Defines class ParticleLayoutItem.
Defines GUI::RealSpace::BuilderUtils namespace.
Defines class RealSpaceBuilder.
Defines class RealSpaceCanvas.
Defines GUI::RealSpace::BuilderUtils namespace.
Defines namespace GUI::View::TransformTo3D.
GUI::RealSpace::Vector3D implementParticleRotationfromIRotation(const IRotation *&rotation)
Particle3DContainer mesoCrystal3DContainer(const MesoCrystalItem &mesoCrystalItem, double total_abundance=1.0, const QVector3D &origin={})
void applyParticleCoreShellTransformations(const Particle &particle, GUI::RealSpace::Particles::Particle &particle3D, const ParticleCoreShell &particleCoreShell, const R3 &origin=R3(0, 0, 0))
Apply transformations (translation, rotation) to a particle (core/shell) in a ParticleCoreShell.
void applyParticleColor(const Particle &particle, GUI::RealSpace::Particles::Particle &particle3D, double alpha=1)
Particle3DContainer particleCoreShell3DContainer(const ParticleCoreShell &particleCoreShell, double total_abundance=1.0, const QVector3D &origin={})
std::vector< Particle3DContainer > particle3DContainerVector(const ParticleLayoutItem &layoutItem, const QVector3D &origin={})
QVector< double > computeCumulativeAbundances(const ParticleLayoutItem &layoutItem)
BuilderUtils(std::function< QColor(const QString &)> fnColorFromMaterialName)
void populateParticlesAtLatticePositions(const std::vector< std::vector< double >> &lattice_positions, const std::vector< Particle3DContainer > &particle3DContainer_vector, GUI::RealSpace::Model *model, const SceneGeometry &sceneGeometry, const RealSpaceBuilder *builder3D)
void applyParticleTransformations(const Particle &particle, GUI::RealSpace::Particles::Particle &particle3D, const R3 &origin=R3(0, 0, 0))
Apply transformations (translation, rotation) to a 3D Particle or to a particle belonging to a Partic...
Particle3DContainer singleParticle3DContainer(const Particle &particle, double total_abundance=1.0, const QVector3D &origin={})
Particle3DContainer particleComposition3DContainer(const ParticleComposition &particleComposition3DContainer, double total_abundance=1.0, const QVector3D &origin={})
void transform(Vector3D rotate, Vector3D translate)
Definition: particles.cpp:72
void addTransform(Vector3D rotate, Vector3D translate)
Definition: particles.cpp:82
std::unique_ptr< GUI::RealSpace::Particles::Particle > createParticle(const size_t &index) const
double cumulativeAbundance() const
void setCumulativeAbundance(double cumulativeAbundance)
void addParticle(GUI::RealSpace::Particles::Particle *particle3D, bool blend)
size_t containerSize() const
void setParticleType(QString particleType)
bool particle3DBlend(const size_t &index) const
QVector< ItemWithParticles * > particles() const
The particles this layout contains.
void populateParticleFromParticle3DContainer(RealSpace::Model *model, const Particle3DContainer &particle3DContainer, const QVector3D &lattice_position={}) const
Particle3DContainer populateMesoCrystal()
std::unique_ptr< GUI::RealSpace::Particles::Particle > createParticlefromFormfactor(const IFormFactor *ff)
Size and thickness information of layers.
double topOrBottomLayerThickness