BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
ProcessedSample.cpp
Go to the documentation of this file.
1 // ************************************************************************** //
2 //
3 // BornAgain: simulate and fit scattering at grazing incidence
4 //
5 //! @file Core/Computation/ProcessedSample.cpp
6 //! @brief Implements class ProcessedSample.
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 
26 
27 namespace
28 {
29 std::unique_ptr<IFresnelMap> CreateFresnelMap(const MultiLayer& sample,
30  const std::vector<Slice>& slices,
31  const SimulationOptions& options);
32 bool ContainsMagneticMaterial(const MultiLayer& sample);
33 bool ContainsMagneticSlice(const std::vector<Slice>& slices);
34 bool CheckRegions(const std::vector<HomogeneousRegion>& regions);
35 std::vector<Slice>
36 CreateAverageMaterialSlices(const std::vector<Slice>& slices,
37  const std::map<size_t, std::vector<HomogeneousRegion>>& region_map);
38 } // namespace
39 
40 ProcessedSample::ProcessedSample(const MultiLayer& sample, const SimulationOptions& options)
41  : m_slices{}, m_top_z{0.0}, m_polarized{false}, m_crossCorrLength{sample.crossCorrLength()},
42  m_ext_field{sample.externalField()}
43 {
44  initSlices(sample, options);
45  mP_fresnel_map = CreateFresnelMap(sample, m_slices, options);
46  initBFields();
47  initLayouts(sample);
48  initFresnelMap(options);
49 }
50 
51 ProcessedSample::~ProcessedSample() = default;
52 
53 size_t ProcessedSample::numberOfSlices() const
54 {
55  return m_slices.size();
56 }
57 
58 const std::vector<Slice>& ProcessedSample::slices() const
59 {
60  return m_slices;
61 }
62 
63 const std::vector<Slice>& ProcessedSample::averageSlices() const
64 {
65  return mP_fresnel_map->slices();
66 }
67 
68 const std::vector<ProcessedLayout>& ProcessedSample::layouts() const
69 {
70  return m_layouts;
71 }
72 
73 const IFresnelMap* ProcessedSample::fresnelMap() const
74 {
75  return mP_fresnel_map.get();
76 }
77 
78 double ProcessedSample::crossCorrelationLength() const
79 {
80  return m_crossCorrLength;
81 }
82 
83 kvector_t ProcessedSample::externalField() const
84 {
85  return m_ext_field;
86 }
87 
88 const LayerRoughness* ProcessedSample::bottomRoughness(size_t i) const
89 {
90  if (i + 2 > m_slices.size())
91  throw std::runtime_error("ProcessedSample::bottomRoughness: "
92  "index out of bounds.");
93  return m_slices[i + 1].topRoughness();
94 }
95 
96 double ProcessedSample::sliceTopZ(size_t i) const
97 {
98  if (i == 0)
99  return m_top_z;
100  return sliceBottomZ(i - 1);
101 }
102 
103 double ProcessedSample::sliceBottomZ(size_t i) const
104 {
105  if (numberOfSlices() < 2)
106  return m_top_z;
107  // Last slice has no bottom:
108  if (i + 2 > numberOfSlices())
109  i = numberOfSlices() - 2;
110  auto z = m_top_z;
111  for (size_t j = 1; j <= i; ++j)
112  z -= m_slices[j].thickness();
113  return z;
114 }
115 
116 bool ProcessedSample::containsMagneticMaterial() const
117 {
118  return m_polarized;
119 }
120 
121 bool ProcessedSample::hasRoughness() const
122 {
123  for (auto& slice : m_slices) {
124  if (slice.topRoughness())
125  return true;
126  }
127  return false;
128 }
129 
130 double ProcessedSample::crossCorrSpectralFun(const kvector_t kvec, size_t j, size_t k) const
131 {
132  if (m_crossCorrLength <= 0.0)
133  return 0.0;
134  double z_j = sliceBottomZ(j);
135  double z_k = sliceBottomZ(k);
136  const LayerRoughness* rough_j = bottomRoughness(j);
137  const LayerRoughness* rough_k = bottomRoughness(k);
138  if (!rough_j || !rough_k)
139  return 0.0;
140  double sigma_j = rough_j->getSigma();
141  double sigma_k = rough_k->getSigma();
142  if (sigma_j <= 0 || sigma_k <= 0)
143  return 0.0;
144  double corr = 0.5
145  * ((sigma_k / sigma_j) * rough_j->getSpectralFun(kvec)
146  + (sigma_j / sigma_k) * rough_k->getSpectralFun(kvec))
147  * std::exp(-1 * std::abs(z_j - z_k) / m_crossCorrLength);
148  return corr;
149 }
150 
151 // Creates a array of slices with the correct thickness, roughness and material
152 void ProcessedSample::initSlices(const MultiLayer& sample, const SimulationOptions& options)
153 {
154  if (sample.numberOfLayers() == 0)
155  return;
156  bool use_slicing = options.useAvgMaterials() && sample.numberOfLayers() > 1;
157  auto layer_limits = MultiLayerUtils::ParticleRegions(sample, use_slicing);
158  for (size_t i = 0; i < sample.numberOfLayers(); ++i) {
159  auto p_layer = sample.layer(i);
160  auto n_slices = p_layer->numberOfSlices();
161  const ZLimits& slice_limits = layer_limits[i];
162  double tl = p_layer->thickness();
163  const Material* p_material = p_layer->material();
164  auto p_roughness = MultiLayerUtils::LayerTopRoughness(sample, i);
165  if (p_roughness && p_roughness->getSigma() <= 0)
166  p_roughness = nullptr;
167  // if no slicing is needed, create single slice for the layer
168  if (!slice_limits.isFinite() || n_slices == 0) {
169  if (i == sample.numberOfLayers() - 1)
170  tl = 0.0;
171  addSlice(tl, *p_material, p_roughness);
172  continue;
173  }
174  double top = slice_limits.upperLimit().m_value;
175  double bottom = slice_limits.lowerLimit().m_value;
176  // top layer
177  if (i == 0) {
178  if (top <= 0)
179  throw std::runtime_error("ProcessedSample::ProcessedSample: "
180  "top limit for top layer must be > 0.");
181  addSlice(0.0, *p_material);
182  addNSlices(n_slices, top - bottom, *p_material);
183  if (bottom > 0) {
184  addSlice(bottom, *p_material);
185  }
186  m_top_z = top;
187  }
188  // middle or bottom layer
189  else {
190  if (top < 0) {
191  addSlice(-top, *p_material, p_roughness);
192  addNSlices(n_slices, top - bottom, *p_material);
193  } else {
194  addNSlices(n_slices, top - bottom, *p_material, p_roughness);
195  }
196  // middle layer
197  if (i < sample.numberOfLayers() - 1 && bottom > -tl) {
198  addSlice(bottom + tl, *p_material);
199  }
200  // bottom layer
201  if (i == sample.numberOfLayers() - 1) {
202  addSlice(0.0, *p_material);
203  }
204  }
205  }
206 }
207 
208 void ProcessedSample::initLayouts(const MultiLayer& sample)
209 {
210  double z_ref = -m_top_z;
211  m_polarized = ContainsMagneticMaterial(sample);
212  for (size_t i = 0; i < sample.numberOfLayers(); ++i) {
213  if (i > 1)
214  z_ref -= MultiLayerUtils::LayerThickness(sample, i - 1);
215  auto p_layer = sample.layer(i);
216  for (auto p_layout : p_layer->layouts()) {
217  m_layouts.emplace_back(*p_layout, m_slices, z_ref, mP_fresnel_map.get(), m_polarized);
218  mergeRegionMap(m_layouts.back().regionMap());
219  }
220  }
221 }
222 
223 void ProcessedSample::addSlice(double thickness, const Material& material,
224  const LayerRoughness* p_roughness)
225 {
226  if (p_roughness) {
227  m_slices.emplace_back(thickness, material, *p_roughness);
228  } else {
229  m_slices.emplace_back(thickness, material);
230  }
231 }
232 
233 void ProcessedSample::addNSlices(size_t n, double thickness, const Material& material,
234  const LayerRoughness* p_roughness)
235 {
236  if (thickness <= 0.0)
237  return;
238  if (n == 0)
239  throw std::runtime_error("ProcessedSample::addNSlices: number of slices should be "
240  "bigger than zero.");
241  double slice_thickness = thickness / n;
242  addSlice(slice_thickness, material, p_roughness);
243  for (size_t i = 1; i < n; ++i) {
244  addSlice(slice_thickness, material);
245  }
246 }
247 
248 void ProcessedSample::initBFields()
249 {
250  if (m_slices.empty())
251  return;
252  double m_z0 = m_slices[0].material().magnetization().z();
253  double b_z = Slice::Magnetic_Permeability * (m_ext_field.z() + m_z0);
254  for (size_t i = 0; i < m_slices.size(); ++i) {
255  m_slices[i].initBField(m_ext_field, b_z);
256  }
257 }
258 
259 void ProcessedSample::mergeRegionMap(
260  const std::map<size_t, std::vector<HomogeneousRegion>>& region_map)
261 {
262  for (auto& entry : region_map) {
263  size_t i = entry.first;
264  auto& regions = entry.second;
265  m_region_map[i].insert(m_region_map[i].begin(), regions.begin(), regions.end());
266  }
267 }
268 
269 void ProcessedSample::initFresnelMap(const SimulationOptions& sim_options)
270 {
271  if (sim_options.useAvgMaterials()) {
272  mP_fresnel_map->setSlices(CreateAverageMaterialSlices(m_slices, m_region_map));
273  } else {
274  mP_fresnel_map->setSlices(m_slices);
275  }
276 }
277 
278 namespace
279 {
280 std::unique_ptr<IFresnelMap> CreateFresnelMap(const MultiLayer& sample,
281  const std::vector<Slice>& slices,
282  const SimulationOptions& options)
283 {
284  std::unique_ptr<IFresnelMap> P_result;
285  if (ContainsMagneticSlice(slices))
286  P_result = std::make_unique<MatrixFresnelMap>(SpecularStrategyBuilder::build(sample, true));
287  else
288  P_result =
289  std::make_unique<ScalarFresnelMap>(SpecularStrategyBuilder::build(sample, false));
290  if (options.isIntegrate())
291  P_result->disableCaching();
292  return P_result;
293 }
294 
295 bool ContainsMagneticMaterial(const MultiLayer& sample)
296 {
297  for (const Material* mat : sample.containedMaterials())
298  if (mat->isMagneticMaterial())
299  return true;
300  return false;
301 }
302 
303 bool ContainsMagneticSlice(const std::vector<Slice>& slices)
304 {
305  for (size_t i = 0; i < slices.size(); ++i) {
306  if (slices[i].material().isMagneticMaterial())
307  return true;
308  }
309  return false;
310 }
311 
312 bool CheckRegions(const std::vector<HomogeneousRegion>& regions)
313 {
314  double total_fraction = 0.0;
315  for (auto& region : regions)
316  total_fraction += region.m_volume;
317  return (total_fraction >= 0 && total_fraction <= 1);
318 }
319 
320 std::vector<Slice>
321 CreateAverageMaterialSlices(const std::vector<Slice>& slices,
322  const std::map<size_t, std::vector<HomogeneousRegion>>& region_map)
323 {
324  std::vector<Slice> result = slices;
325  auto last_slice_index = slices.size() - 1;
326  for (auto& entry : region_map) {
327  auto i_slice = entry.first;
328  if (i_slice == 0 || i_slice == last_slice_index)
329  continue; // skip semi-infinite layers
330  auto slice_mat = slices[i_slice].material();
331  if (!CheckRegions(entry.second))
332  throw std::runtime_error("CreateAverageMaterialSlices: "
333  "total volumetric fraction of particles exceeds 1!");
334  auto new_material = createAveragedMaterial(slice_mat, entry.second);
335  result[i_slice].setMaterial(new_material);
336  }
337  return result;
338 }
339 } // namespace
Defines struct HomogeneousRegion, and declares fct createAveragedMaterial.
Defines class LayerRoughness.
Defines class Layer.
Defines class MatrixFresnelMap.
Defines helper functions for MultiLayer objects.
double LayerThickness(const MultiLayer &multilayer, size_t i)
Returns thickness of layer.
Defines class ProcessedLayout.
Defines class ProcessedSample.
Defines class ScalarFresnelMap.
Defines class SimulationOptions.
Defines class SpecularStrategyBuilder.
Defines class ZLimits.
T z() const
Returns z-component in cartesian coordinate system.
Definition: BasicVector3D.h:68
Holds the necessary information to calculate the radiation wavefunction in every layer for different ...
Definition: IFresnelMap.h:30
std::vector< const Material * > containedMaterials() const
Returns set of unique materials contained in this ISample.
Definition: ISample.cpp:23
A roughness of interface between two layers.
double getSigma() const
Returns rms of roughness.
double getSpectralFun(const kvector_t kvec) const
Returns power spectral density of the surface roughness.
A wrapper for underlying material implementation.
Definition: Material.h:29
Our sample model: a stack of layers one below the other.
Definition: MultiLayer.h:42
const Layer * layer(size_t i_layer) const
Returns layer with given index.
Definition: MultiLayer.cpp:88
double crossCorrSpectralFun(const kvector_t kvec, size_t j, size_t k) const
Fourier transform of the correlation function of roughnesses between the interfaces.
Collect the different options for simulation.
Class that contains upper and lower limits of the z-coordinate for the slicing of form factors.
Definition: ZLimits.h:41
Material createAveragedMaterial(const Material &layer_mat, const std::vector< HomogeneousRegion > &regions)
Creates averaged material.