BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
RealSpace2DParacrystalUtils.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/Realspace/RealSpace2DParacrystalUtils.cpp
6 //! @brief Defines 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/Util/Assert.h"
17 #include "Sample/Aggregate/Interference2DParaCrystal.h"
18 
19 namespace {
20 
21 void ResizeLatticePositions(std::vector<std::vector<double>>& lattice_positions, double l1,
22  double l2, double layer_size)
23 {
24  // Estimate the limit n1 and n2 of the integer multiple j and i of the lattice vectors l1 and l2
25  // required for populating particles correctly within the 3D model's boundaries
26  int n1 = 0, n2 = 0;
27  n1 = l1 == 0.0 ? 2 : static_cast<int>(layer_size * 2 / l1);
28  n2 = l2 == 0.0 ? 2 : static_cast<int>(layer_size * 2 / l2);
29 
30  n1 = std::max(n1, n2);
31 
32  lattice_positions.resize((2 * n1 + 1) * (2 * n1 + 1));
33  for (auto& it : lattice_positions)
34  it.resize(2);
35 
36  lattice_positions[0][0] = 0.0; // x coordinate of reference particle - at the origin
37  lattice_positions[0][1] = 0.0; // y coordinate of reference particle - at the origin
38 }
39 
40 void FindLatticePositionsIndex(size_t& index, size_t& index_prev, int i, int j, int size,
41  double l_alpha)
42 {
43  int newindex = i * (2 * size + 1) + j;
44  ASSERT(newindex > 0);
45  index = newindex;
46 
47  if (std::sin(l_alpha) == 0) { // along l1
48  // particles along +l1 stored every odd iter (e.g. 1,3,5...) index of lattice_positions
49  // particles along -l1 stored every even iter (e.g. 2,4,6...) index of lattice_positions
50  index_prev = static_cast<size_t>(i * (2 * size + 1));
51  if (j - 2 > 0)
52  index_prev = index - 2;
53  } else { // along l2
54  // particles along +l2/-l2 stored every (odd/even iter)*(2*n1+1) index of lattice_positions
55  index_prev = static_cast<size_t>(j);
56  if (i - 2 > 0)
57  index_prev = index - static_cast<size_t>(2 * (2 * size + 1));
58  }
59 }
60 
61 std::pair<double, double> ComputePositionAlongPositiveLatticeVector(
62  const size_t index_prev, std::vector<std::vector<double>>& lattice_positions,
63  const IProfile2D* pdf, double l, double l_xi, double l_alpha)
64 {
65  double gamma_pdf = pdf->gamma();
66  std::pair<double, double> sampleXYpdf = pdf->createSampler()->randomSample();
67 
68  double offset_x_pdf = sampleXYpdf.first;
69  double offset_y_pdf = sampleXYpdf.second;
70 
71  double x = lattice_positions[index_prev][0] + l * std::cos(l_xi + l_alpha)
72  + offset_x_pdf * std::cos(gamma_pdf + l_xi)
73  + offset_y_pdf * std::cos(M_PI_2 + gamma_pdf + l_xi); // x coordinate
74  double y = lattice_positions[index_prev][1] + l * std::sin(l_xi + l_alpha)
75  + offset_x_pdf * std::sin(gamma_pdf + l_xi)
76  + offset_y_pdf * std::sin(M_PI_2 + gamma_pdf + l_xi); // y coordinate
77 
78  return std::make_pair(x, y);
79 }
80 
81 std::pair<double, double> ComputePositionAlongNegativeLatticeVector(
82  const size_t index_prev, std::vector<std::vector<double>>& lattice_positions,
83  const IProfile2D* pdf, double l, double l_xi, double l_alpha)
84 {
85  double gamma_pdf = pdf->gamma();
86  std::pair<double, double> sampleXYpdf = pdf->createSampler()->randomSample();
87 
88  double offset_x_pdf = sampleXYpdf.first;
89  double offset_y_pdf = sampleXYpdf.second;
90 
91  double x = lattice_positions[index_prev][0] - l * std::cos(l_xi + l_alpha)
92  + offset_x_pdf * std::cos(gamma_pdf + l_xi)
93  + offset_y_pdf * std::cos(M_PI_2 + gamma_pdf + l_xi); // x coordinate
94  double y = lattice_positions[index_prev][1] - l * std::sin(l_xi + l_alpha)
95  + offset_x_pdf * std::sin(gamma_pdf + l_xi)
96  + offset_y_pdf * std::sin(M_PI_2 + gamma_pdf + l_xi); // y coordinate
97 
98  return std::make_pair(x, y);
99 }
100 
101 std::pair<double, double>
102 ComputeLatticePosition(const size_t index_prev, int i, int j,
103  std::vector<std::vector<double>>& lattice_positions, const IProfile2D* pdf,
104  double l, double l_xi, double l_alpha)
105 {
106  if (std::sin(l_alpha) == 0) {
107  if (!(j % 2 == 0)) // along +l1
108  return ComputePositionAlongPositiveLatticeVector(index_prev, lattice_positions, pdf, l,
109  l_xi, 0);
110  // else along -l1
111  return ComputePositionAlongNegativeLatticeVector(index_prev, lattice_positions, pdf, l,
112  l_xi, 0);
113  }
114  if (!(i % 2 == 0)) // along +l2
115  return ComputePositionAlongPositiveLatticeVector(index_prev, lattice_positions, pdf, l,
116  l_xi, l_alpha);
117  // else along -l2
118  return ComputePositionAlongNegativeLatticeVector(index_prev, lattice_positions, pdf, l, l_xi,
119  l_alpha);
120 }
121 
122 void ComputePositionsAlongLatticeVectorAxes(std::vector<std::vector<double>>& lattice_positions,
123  const IProfile2D* pdf, double l, double l_xi,
124  double l_alpha)
125 {
126  int n = static_cast<int>((std::sqrt(lattice_positions.size()) - 1) / 2);
127 
128  size_t index = 0; // lattice_positions index for current particle
129  size_t index_prev = 0; // lattice_positions index for previous particle
130 
131  std::pair<double, double> xy;
132 
133  for (int iter = 1; iter <= 2 * n; ++iter) {
134  int iterl1, iterl2;
135 
136  if (std::sin(l_alpha) == 0) {
137  iterl1 = iter;
138  iterl2 = 0;
139  } else {
140  iterl1 = 0;
141  iterl2 = iter;
142  }
143 
144  // The 2*n+1 particles that are situated ONLY along the l1 axis (both +/- axes)
145  // are stored in i = 1,2,3,...2*n1 indices of lattice_positions
146 
147  // The 2*n+1 particles that are situated ONLY along the l2 axis (both +/- axes)
148  // are stored every i*(2*n1+1) index of lattice_positions
149 
150  FindLatticePositionsIndex(index, index_prev, iterl2, iterl1, n, l_alpha);
151  xy = ComputeLatticePosition(index_prev, iterl2, iterl1, lattice_positions, pdf, l, l_xi,
152  l_alpha);
153 
154  lattice_positions[index][0] = xy.first; // x coordinate
155  lattice_positions[index][1] = xy.second; // y coordinate
156  }
157 }
158 
159 void ComputePositionsInsideLatticeQuadrants(std::vector<std::vector<double>>& lattice_positions,
160  const IProfile2D* pdf1, const IProfile2D* pdf2,
161  double l1, double l2, double l_xi, double l_alpha)
162 {
163  int n = static_cast<int>((std::sqrt(lattice_positions.size()) - 1) / 2);
164 
165  size_t index = 0; // lattice_positions index for current particle
166  size_t index_prev = 0; // lattice_positions index for previous particle
167 
168  std::pair<double, double> xy_l1, xy_l2;
169 
170  for (int i = 1; i <= 2 * n; ++i) {
171  for (int j = 1; j <= 2 * n; ++j) {
172  FindLatticePositionsIndex(index, index_prev, i, j, n, 0);
173  xy_l1 = ComputeLatticePosition(index_prev, i, j, lattice_positions, pdf1, l1, l_xi, 0);
174 
175  FindLatticePositionsIndex(index, index_prev, i, j, n, l_alpha);
176  xy_l2 = ComputeLatticePosition(index_prev, i, j, lattice_positions, pdf2, l2, l_xi,
177  l_alpha);
178 
179  lattice_positions[index][0] = (xy_l1.first + xy_l2.first) / 2;
180  lattice_positions[index][1] = (xy_l1.second + xy_l2.second) / 2;
181  }
182  }
183 }
184 
185 } // namespace
186 
187 
188 // ************************************************************************************************
189 // implement namespace GUI::RealSpace::Paracrystal2D
190 // ************************************************************************************************
191 
192 std::vector<std::vector<double>>
193 GUI::RealSpace::Paracrystal2D::latticePositions(const Interference2DParaCrystal* p_iff,
194  double layer_size)
195 {
196  const auto& lattice = p_iff->lattice();
197  double l1 = lattice.length1();
198  double l2 = lattice.length2();
199  double alpha = lattice.latticeAngle();
200  double xi = lattice.rotationAngle();
201 
202  std::vector<std::vector<double>> lattice_positions;
203  ResizeLatticePositions(lattice_positions, l1, l2, layer_size);
204 
205  ComputePositionsAlongLatticeVectorAxes(lattice_positions, p_iff->pdf1(), l1, xi, 0);
206 
207  ComputePositionsAlongLatticeVectorAxes(lattice_positions, p_iff->pdf2(), l2, xi, alpha);
208 
209  ComputePositionsInsideLatticeQuadrants(lattice_positions, p_iff->pdf1(), p_iff->pdf2(), l1, l2,
210  xi, alpha);
211 
212  return lattice_positions;
213 }
Defines namespace GUI::RealSpace::Paracrystal2D.
std::vector< std::vector< double > > latticePositions(const Interference2DParaCrystal *, double layer_size)