BornAgain  1.19.0
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/coregui/Views/RealSpaceWidgets/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 
18 #include <cmath>
19 #include <random>
20 
21 namespace {
22 std::vector<std::vector<double>> Generate2DLatticePoints(double l1, double l2, double alpha,
23  double xi, unsigned n1, unsigned n2);
24 }
25 
27 
28 std::vector<std::vector<double>> IPositionBuilder::generatePositions(double layer_size,
29  double density) const
30 {
31  std::vector<std::vector<double>> positions = generatePositionsImpl(layer_size, density);
32  double pos_var = positionVariance();
33  if (pos_var > 0.0) {
34  // random generator and distribution
35  std::random_device rd; // Will be used to obtain a seed for the random number engine
36  std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()
37  std::normal_distribution<double> dis(0.0, std::sqrt(pos_var));
38  for (auto& position : positions) {
39  for (auto& coordinate : position)
40  coordinate += dis(gen);
41  }
42  }
43  return positions;
44 }
45 
47 
49 
50 std::vector<std::vector<double>> DefaultPositionBuilder::generatePositionsImpl(double, double) const
51 {
52  std::vector<double> origin = {0.0, 0.0};
53  return {origin};
54 }
55 
57 {
58  return 0.0;
59 }
60 
62 
64 
65 std::vector<std::vector<double>> RandomPositionBuilder::generatePositionsImpl(double layer_size,
66  double density) const
67 {
68  std::vector<std::vector<double>> lattice_positions;
69  std::vector<double> position;
70 
71  // to compute total number of particles we use the total particle density
72  // and multiply by the area of the layer
73  int num_particles = static_cast<int>(density * (2 * layer_size) * (2 * layer_size));
74 
75  // random generator and distribution
76  std::random_device rd; // Will be used to obtain a seed for the random number engine
77  std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()
78  std::uniform_real_distribution<double> dis(0.0, 1.0);
79 
80  for (int i = 1; i <= num_particles; ++i) {
81  // generate random x and y coordinates
82  position.push_back(dis(gen) * 2 * layer_size - layer_size); // x
83  position.push_back(dis(gen) * 2 * layer_size - layer_size); // y
84 
85  lattice_positions.push_back(position);
86  position.clear();
87  }
88  return lattice_positions;
89 }
90 
92 {
93  return 0.0; // no need for extra randomness here
94 }
95 
97  : m_iff(p_iff->clone())
98 {
99 }
100 
102 
103 std::vector<std::vector<double>> Lattice1DPositionBuilder::generatePositionsImpl(double layer_size,
104  double) const
105 {
106  const double length = m_iff->getLength();
107  const double xi = m_iff->getXi();
108 
109  // Take the maximum possible integer multiple of the lattice vector required
110  // for populating particles correctly within the 3D model's boundaries
111  unsigned n1 =
112  length == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / length);
113 
114  return Generate2DLatticePoints(length, 0.0, 0.0, xi, n1, 1u);
115 }
116 
118 {
119  return m_iff->positionVariance();
120 }
121 
123  : m_iff(p_iff->clone())
124 {
125 }
126 
128 
129 std::vector<std::vector<double>> Lattice2DPositionBuilder::generatePositionsImpl(double layer_size,
130  double) const
131 {
132  auto& lattice = m_iff->lattice();
133  double l1 = lattice.length1();
134  double l2 = lattice.length2();
135  double alpha = lattice.latticeAngle();
136  double xi = lattice.rotationAngle();
137 
138  // Estimate the limits n1 and n2 of the maximum integer multiples of the lattice vectors
139  // required for populating particles correctly within the 3D model's boundaries
140  unsigned n1, n2;
141  double sina = std::abs(std::sin(alpha));
142  if (sina <= 1e-4) {
143  n1 = l1 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l1);
144  n2 = l2 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l2);
145  } else {
146  n1 = l1 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l1 / sina);
147  n2 = l2 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l2 / sina);
148  }
149  return Generate2DLatticePoints(l1, l2, alpha, xi, n1, n2);
150 }
151 
153 {
154  return m_iff->positionVariance();
155 }
156 
159  : m_iff(p_iff->clone())
160 {
161 }
162 
164 
165 std::vector<std::vector<double>>
167 {
169  layer_size);
170 }
171 
173 {
174  return m_iff->positionVariance();
175 }
176 
179  : m_iff(p_iff->clone())
180 {
181 }
182 
184 
185 std::vector<std::vector<double>>
187 {
188  auto& lattice = m_iff->lattice();
189  double l1 = lattice.length1();
190  double l2 = lattice.length2();
191  double alpha = lattice.latticeAngle();
192  double xi = lattice.rotationAngle();
193 
194  unsigned n1, n2;
195  double sina = std::abs(std::sin(alpha));
196  if (sina <= 1e-4) {
197  n1 = l1 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l1);
198  n2 = l2 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l2);
199  } else {
200  n1 = l1 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l1 / sina);
201  n2 = l2 == 0.0 ? 2 : static_cast<unsigned>(2.0 * layer_size * std::sqrt(2.0) / l2 / sina);
202  }
203  n1 = std::min(n1, m_iff->numberUnitCells1());
204  n2 = std::min(n2, m_iff->numberUnitCells2());
205 
206  return Generate2DLatticePoints(l1, l2, alpha, xi, n1, n2);
207 }
208 
210 {
211  return m_iff->positionVariance();
212 }
213 
216  : m_iff(p_iff->clone())
217 {
218 }
219 
221 
222 std::vector<std::vector<double>>
224 {
225  std::vector<std::vector<double>> lattice_positions;
226 
227  double distance = m_iff->peakDistance();
228 
229  // Estimate the limit n of the integer multiple i of the peakDistance required
230  // for populating particles correctly within the 3D model's boundaries
231  int n = distance <= 0.0 ? 1 : static_cast<int>(layer_size * std::sqrt(2.0) / distance);
232 
233  lattice_positions.resize(static_cast<size_t>(2 * n + 1));
234  for (auto& it : lattice_positions) {
235  it.resize(2);
236  }
237 
238  lattice_positions[0][0] = 0.0; // x coordinate of reference particle - at the origin
239  lattice_positions[0][1] = 0.0; // y coordinate of reference particle - at the origin
240 
241  for (int i = 1; i <= n; ++i) {
242  // positions of particles located along +x (store at odd index)
243  unsigned i_left = static_cast<unsigned>(std::max(0, 2 * i - 3));
244 
245  double offset = m_iff->randomSample();
246  lattice_positions[static_cast<size_t>(2 * i - 1)][0] =
247  lattice_positions[i_left][0] + distance + offset;
248  lattice_positions[static_cast<size_t>(2 * i - 1)][1] = 0.0;
249 
250  // positions of particles located along -x (store at even index)
251  unsigned i_right = static_cast<unsigned>(2 * (i - 1));
252 
253  offset = m_iff->randomSample();
254  lattice_positions[static_cast<size_t>(2 * i)][0] =
255  lattice_positions[i_right][0] - distance + offset;
256  lattice_positions[static_cast<size_t>(2 * i)][1] = 0.0;
257  }
258  return lattice_positions;
259 }
260 
262 {
263  return m_iff->positionVariance();
264 }
265 
266 namespace {
267 std::vector<std::vector<double>> Generate2DLatticePoints(double l1, double l2, double alpha,
268  double xi, unsigned n1, unsigned n2)
269 {
270  std::vector<std::vector<double>> lattice_positions;
271  std::vector<double> position;
272 
273  unsigned nn1 = std::max(1u, n1);
274  unsigned nn2 = std::max(1u, n2);
275  int n1m = -static_cast<int>((nn1 - 1) / 2);
276  int n1M = static_cast<int>(nn1 / 2);
277  int n2m = -static_cast<int>((nn2 - 1) / 2);
278  int n2M = static_cast<int>(nn2 / 2);
279 
280  for (int i = n1m; i <= n1M; ++i) {
281  for (int j = n2m; j <= n2M; ++j) {
282  // For calculating lattice position vector v, we use: v = i*l1 + j*l2
283  // where l1 and l2 are the lattice vectors,
284  position.push_back(i * l1 * std::cos(xi)
285  + j * l2 * std::cos(alpha + xi)); // x coordinate
286  position.push_back(i * l1 * std::sin(xi)
287  + j * l2 * std::sin(alpha + xi)); // y coordinate
288 
289  lattice_positions.push_back(position);
290  position.clear();
291  }
292  }
293  return lattice_positions;
294 }
295 } // namespace
Declares interface IPositionBuilder and subclasses.
Includes all interference function definitions.
Defines RealSpaceBuilderUtils namespace.
std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const override
double positionVariance() const override
~DefaultPositionBuilder() override
std::unique_ptr< InterferenceFunctionFinite2DLattice > m_iff
std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const override
Finite2DLatticePositionBuilder(const InterferenceFunctionFinite2DLattice *p_iff)
~Finite2DLatticePositionBuilder() override
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
Interference function of a 1D lattice.
Interference function of a 2D lattice.
Interference function of a 2D paracrystal.
Interference function of a finite 2D lattice.
Interference function of radial paracrystal.
std::unique_ptr< InterferenceFunction1DLattice > m_iff
Lattice1DPositionBuilder(const InterferenceFunction1DLattice *p_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 InterferenceFunction2DLattice *p_iff)
std::unique_ptr< InterferenceFunction2DLattice > m_iff
std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const override
double positionVariance() const override
double positionVariance() const override
std::vector< std::vector< double > > generatePositionsImpl(double layer_size, double density=0.0) const override
~ParaCrystal2DPositionBuilder() override
std::unique_ptr< InterferenceFunction2DParaCrystal > m_iff
ParaCrystal2DPositionBuilder(const InterferenceFunction2DParaCrystal *p_iff)
std::unique_ptr< InterferenceFunctionRadialParaCrystal > m_iff
RadialParacrystalPositionBuilder(const InterferenceFunctionRadialParaCrystal *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 > > Compute2DParacrystalLatticePositions(const InterferenceFunction2DParaCrystal *, double layer_size)