BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
IPositionBuilder.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/Realspace/IPositionBuilder.cpp
6 //! @brief Implements subclasses of IPositionBuilder
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 
17 #include "Sample/Aggregate/Interferences.h"
18 #include <cmath>
19 #include <random>
20 
21 namespace {
22 
23 std::vector<std::vector<double>> Generate2DLatticePoints(double l1, double l2, double alpha,
24  double xi, unsigned n1, unsigned n2);
25 }
26 
28 
29 std::vector<std::vector<double>> IPositionBuilder::generatePositions(double layer_size,
30  double density) const
31 {
32  std::vector<std::vector<double>> positions = generatePositionsImpl(layer_size, density);
33  double pos_var = positionVariance();
34  if (pos_var > 0.0) {
35  // random generator and distribution
36  std::random_device rd; // Will be used to obtain a seed for the random number engine
37  std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()
38  std::normal_distribution<double> dis(0.0, std::sqrt(pos_var));
39  for (auto& position : positions) {
40  for (auto& coordinate : position)
41  coordinate += dis(gen);
42  }
43  }
44  return positions;
45 }
46 
48 
50 
51 std::vector<std::vector<double>> DefaultPositionBuilder::generatePositionsImpl(double, double) const
52 {
53  std::vector<double> origin = {0.0, 0.0};
54  return {origin};
55 }
56 
58 {
59  return 0.0;
60 }
61 
63 
65 
66 std::vector<std::vector<double>> RandomPositionBuilder::generatePositionsImpl(double layer_size,
67  double density) const
68 {
69  std::vector<std::vector<double>> lattice_positions;
70  std::vector<double> position;
71 
72  // to compute total number of particles we use the total particle density
73  // and multiply by the area of the layer
74  int num_particles = static_cast<int>(density * (2 * layer_size) * (2 * layer_size));
75 
76  // random generator and distribution
77  std::random_device rd; // Will be used to obtain a seed for the random number engine
78  std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()
79  std::uniform_real_distribution<double> dis(0.0, 1.0);
80 
81  for (int i = 1; i <= num_particles; ++i) {
82  // generate random x and y coordinates
83  position.push_back(dis(gen) * 2 * layer_size - layer_size); // x
84  position.push_back(dis(gen) * 2 * layer_size - layer_size); // y
85 
86  lattice_positions.push_back(position);
87  position.clear();
88  }
89  return lattice_positions;
90 }
91 
93 {
94  return 0.0; // no need for extra randomness here
95 }
96 
97 Lattice1DPositionBuilder::Lattice1DPositionBuilder(const Interference1DLattice* p_iff)
98  : m_iff(p_iff->clone())
99 {
100 }
101 
103 
104 std::vector<std::vector<double>> Lattice1DPositionBuilder::generatePositionsImpl(double layer_size,
105  double) const
106 {
107  const double length = m_iff->length();
108  const double xi = m_iff->xi();
109 
110  // Take the maximum possible integer multiple of the lattice vector required
111  // for populating particles correctly within the 3D model's boundaries
112  unsigned n1 =
113  length == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / length);
114 
115  return Generate2DLatticePoints(length, 0.0, 0.0, xi, n1, 1u);
116 }
117 
119 {
120  return m_iff->positionVariance();
121 }
122 
123 Lattice2DPositionBuilder::Lattice2DPositionBuilder(const Interference2DLattice* p_iff)
124  : m_iff(p_iff->clone())
125 {
126 }
127 
129 
130 std::vector<std::vector<double>> Lattice2DPositionBuilder::generatePositionsImpl(double layer_size,
131  double) const
132 {
133  const auto& lattice = m_iff->lattice();
134  double l1 = lattice.length1();
135  double l2 = lattice.length2();
136  double alpha = lattice.latticeAngle();
137  double xi = lattice.rotationAngle();
138 
139  // Estimate the limits n1 and n2 of the maximum integer multiples of the lattice vectors
140  // required for populating particles correctly within the 3D model's boundaries
141  unsigned n1, n2;
142  double sina = std::abs(std::sin(alpha));
143  if (sina <= 1e-4) {
144  n1 = l1 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l1);
145  n2 = l2 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l2);
146  } else {
147  n1 = l1 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l1 / sina);
148  n2 = l2 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l2 / sina);
149  }
150  return Generate2DLatticePoints(l1, l2, alpha, xi, n1, n2);
151 }
152 
154 {
155  return m_iff->positionVariance();
156 }
157 
158 ParaCrystal2DPositionBuilder::ParaCrystal2DPositionBuilder(const Interference2DParaCrystal* p_iff)
159  : m_iff(p_iff->clone())
160 {
161 }
162 
164 
165 std::vector<std::vector<double>>
167 {
168  return GUI::RealSpace::Paracrystal2D::latticePositions(m_iff.get(), layer_size);
169 }
170 
172 {
173  return m_iff->positionVariance();
174 }
175 
177  const InterferenceFinite2DLattice* p_iff)
178  : m_iff(p_iff->clone())
179 {
180 }
181 
183 
184 std::vector<std::vector<double>>
186 {
187  const auto& lattice = m_iff->lattice();
188  double l1 = lattice.length1();
189  double l2 = lattice.length2();
190  double alpha = lattice.latticeAngle();
191  double xi = lattice.rotationAngle();
192 
193  unsigned n1, n2;
194  double sina = std::abs(std::sin(alpha));
195  if (sina <= 1e-4) {
196  n1 = l1 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l1);
197  n2 = l2 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l2);
198  } else {
199  n1 = l1 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l1 / sina);
200  n2 = l2 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l2 / sina);
201  }
202  n1 = std::min(n1, m_iff->numberUnitCells1());
203  n2 = std::min(n2, m_iff->numberUnitCells2());
204 
205  return Generate2DLatticePoints(l1, l2, alpha, xi, n1, n2);
206 }
207 
209 {
210  return m_iff->positionVariance();
211 }
212 
214  const InterferenceRadialParaCrystal* p_iff)
215  : m_iff(p_iff->clone())
216 {
217 }
218 
220 
221 std::vector<std::vector<double>>
223 {
224  std::vector<std::vector<double>> lattice_positions;
225 
226  double distance = m_iff->peakDistance();
227 
228  // Estimate the limit n of the integer multiple i of the peakDistance required
229  // for populating particles correctly within the 3D model's boundaries
230  int n = distance <= 0.0 ? 1 : static_cast<int>(layer_size * std::sqrt(2.0) / distance);
231 
232  lattice_positions.resize(2 * n + 1);
233  for (auto& it : lattice_positions)
234  it.resize(2);
235 
236  lattice_positions[0][0] = 0.0; // x coordinate of reference particle - at the origin
237  lattice_positions[0][1] = 0.0; // y coordinate of reference particle - at the origin
238 
239  for (int i = 1; i <= n; ++i) {
240  // positions of particles located along +x (store at odd index)
241  unsigned i_left = static_cast<unsigned>(std::max(0, 2 * i - 3));
242 
243  double offset = m_iff->randomSample();
244  lattice_positions[2 * i - 1][0] = lattice_positions[i_left][0] + distance + offset;
245  lattice_positions[2 * i - 1][1] = 0.0;
246 
247  // positions of particles located along -x (store at even index)
248  offset = m_iff->randomSample();
249  lattice_positions[2 * i][0] = lattice_positions[2 * (i - 1)][0] - distance + offset;
250  lattice_positions[2 * i][1] = 0.0;
251  }
252  return lattice_positions;
253 }
254 
256 {
257  return m_iff->positionVariance();
258 }
259 
260 namespace {
261 
262 std::vector<std::vector<double>> Generate2DLatticePoints(double l1, double l2, double alpha,
263  double xi, unsigned n1, unsigned n2)
264 {
265  std::vector<std::vector<double>> lattice_positions;
266  std::vector<double> position;
267 
268  unsigned nn1 = std::max(1u, n1);
269  unsigned nn2 = std::max(1u, n2);
270  int n1m = -static_cast<int>((nn1 - 1) / 2);
271  int n1M = static_cast<int>(nn1 / 2);
272  int n2m = -static_cast<int>((nn2 - 1) / 2);
273  int n2M = static_cast<int>(nn2 / 2);
274 
275  for (int i = n1m; i <= n1M; ++i) {
276  for (int j = n2m; j <= n2M; ++j) {
277  // For calculating lattice position vector v, we use: v = i*l1 + j*l2
278  // where l1 and l2 are the lattice vectors,
279  position.push_back(i * l1 * std::cos(xi)
280  + j * l2 * std::cos(alpha + xi)); // x coordinate
281  position.push_back(i * l1 * std::sin(xi)
282  + j * l2 * std::sin(alpha + xi)); // y coordinate
283 
284  lattice_positions.push_back(position);
285  position.clear();
286  }
287  }
288  return lattice_positions;
289 }
290 
291 } // namespace
Declares interface IPositionBuilder and subclasses.
Defines namespace GUI::RealSpace::Paracrystal2D.
std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const override
double positionVariance() const override
~DefaultPositionBuilder() override
std::unique_ptr< InterferenceFinite2DLattice > m_iff
std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const override
~Finite2DLatticePositionBuilder() override
Finite2DLatticePositionBuilder(const InterferenceFinite2DLattice *p_iff)
double positionVariance() const override
virtual ~IPositionBuilder()
virtual std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const =0
std::vector< std::vector< double > > generatePositions(double layer_size, double density=0.0) const
virtual double positionVariance() const =0
Lattice1DPositionBuilder(const Interference1DLattice *p_iff)
std::unique_ptr< Interference1DLattice > m_iff
~Lattice1DPositionBuilder() override
std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const override
double positionVariance() const override
~Lattice2DPositionBuilder() override
Lattice2DPositionBuilder(const Interference2DLattice *p_iff)
std::unique_ptr< Interference2DLattice > m_iff
std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const override
double positionVariance() const override
std::unique_ptr< Interference2DParaCrystal > m_iff
double positionVariance() const override
std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const override
ParaCrystal2DPositionBuilder(const Interference2DParaCrystal *p_iff)
~ParaCrystal2DPositionBuilder() override
std::unique_ptr< InterferenceRadialParaCrystal > m_iff
RadialParacrystalPositionBuilder(const InterferenceRadialParaCrystal *p_iff)
std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const override
double positionVariance() const override
~RandomPositionBuilder() override
std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const override
double positionVariance() const override
std::vector< std::vector< double > > latticePositions(const Interference2DParaCrystal *, double layer_size)