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 
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 
52 
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 
74 {
75  return mP_fresnel_map.get();
76 }
77 
79 {
80  return m_crossCorrLength;
81 }
82 
84 {
85  return m_ext_field;
86 }
87 
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 
117 {
118  return m_polarized;
119 }
120 
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 
209 {
210  double z_ref = -m_top_z;
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 
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 
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 
270 {
271  if (sim_options.useAvgMaterials()) {
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 
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.
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
size_t numberOfLayers() const
Definition: MultiLayer.h:53
void initLayouts(const MultiLayer &sample)
const std::vector< Slice > & slices() const
std::vector< Slice > m_slices
std::map< size_t, std::vector< HomogeneousRegion > > m_region_map
ProcessedSample(const MultiLayer &sample, const SimulationOptions &options)
void initSlices(const MultiLayer &sample, const SimulationOptions &options)
bool hasRoughness() const
double sliceTopZ(size_t i) const
kvector_t externalField() const
void addSlice(double thickness, const Material &material, const LayerRoughness *p_roughness=nullptr)
void addNSlices(size_t n, double thickness, const Material &material, const LayerRoughness *p_roughness=nullptr)
void mergeRegionMap(const std::map< size_t, std::vector< HomogeneousRegion >> &region_map)
double sliceBottomZ(size_t i) const
void initFresnelMap(const SimulationOptions &sim_options)
bool containsMagneticMaterial() const
std::unique_ptr< IFresnelMap > mP_fresnel_map
const IFresnelMap * fresnelMap() const
double m_crossCorrLength
kvector_t m_ext_field
size_t numberOfSlices() const
double crossCorrSpectralFun(const kvector_t kvec, size_t j, size_t k) const
Fourier transform of the correlation function of roughnesses between the interfaces.
const std::vector< Slice > & averageSlices() const
const std::vector< ProcessedLayout > & layouts() const
double crossCorrelationLength() const
const LayerRoughness * bottomRoughness(size_t i) const
std::vector< ProcessedLayout > m_layouts
Collect the different options for simulation.
bool isIntegrate() const
bool useAvgMaterials() const
static constexpr double Magnetic_Permeability
Definition: Slice.h:58
static std::unique_ptr< ISpecularStrategy > build(const MultiLayer &sample, const bool magnetic)
Class that contains upper and lower limits of the z-coordinate for the slicing of form factors.
Definition: ZLimits.h:41
OneSidedLimit lowerLimit() const
Definition: ZLimits.cpp:39
OneSidedLimit upperLimit() const
Definition: ZLimits.cpp:44
bool isFinite() const
Definition: ZLimits.cpp:32
Material createAveragedMaterial(const Material &layer_mat, const std::vector< HomogeneousRegion > &regions)
Creates averaged material.
const LayerRoughness * LayerTopRoughness(const MultiLayer &multilayer, size_t i)
Returns top roughness of layer.
std::vector< ZLimits > ParticleRegions(const MultiLayer &multilayer, bool use_slicing)
Calculate z-regions occupied by particles.
double LayerThickness(const MultiLayer &multilayer, size_t i)
Returns thickness of layer.
std::unique_ptr< IFresnelMap > CreateFresnelMap(const MultiLayer &sample, const std::vector< Slice > &slices, const SimulationOptions &options)
bool ContainsMagneticMaterial(const MultiLayer &sample)
std::vector< Slice > CreateAverageMaterialSlices(const std::vector< Slice > &slices, const std::map< size_t, std::vector< HomogeneousRegion >> &region_map)
bool ContainsMagneticSlice(const std::vector< Slice > &slices)
bool CheckRegions(const std::vector< HomogeneousRegion > &regions)
double m_value
Definition: ZLimits.h:32