BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
DepthProbeSimulation.cpp
Go to the documentation of this file.
1 // ************************************************************************** //
2 //
3 // BornAgain: simulate and fit scattering at grazing incidence
4 //
5 //! @file Core/Simulation/DepthProbeSimulation.cpp
6 //! @brief Implements class DepthProbeSimulation
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 
29 
30 namespace
31 {
32 const RealLimits alpha_limits = RealLimits::limited(0.0, M_PI_2);
33 const double zero_phi_i = 0.0;
34 const double zero_alpha_i = 0.0;
35 } // namespace
36 
37 DepthProbeSimulation::DepthProbeSimulation() : Simulation()
38 {
39  initialize();
40 }
41 
42 DepthProbeSimulation::~DepthProbeSimulation() = default;
43 
44 DepthProbeSimulation* DepthProbeSimulation::clone() const
45 {
46  return new DepthProbeSimulation(*this);
47 }
48 
49 size_t DepthProbeSimulation::numberOfSimulationElements() const
50 {
51  return getAlphaAxis()->size();
52 }
53 
55 {
56  validityCheck();
57  auto data = createIntensityData();
58  return SimulationResult(*data, *createUnitConverter());
59 }
60 
61 void DepthProbeSimulation::setBeamParameters(double lambda, int nbins, double alpha_i_min,
62  double alpha_i_max, const IFootprintFactor* beam_shape)
63 {
64  FixedBinAxis axis("alpha_i", static_cast<size_t>(nbins), alpha_i_min, alpha_i_max);
65  setBeamParameters(lambda, axis, beam_shape);
66 }
67 
68 void DepthProbeSimulation::setZSpan(size_t n_bins, double z_min, double z_max)
69 {
70  if (z_max <= z_min)
71  throw std::runtime_error("Error in DepthProbeSimulation::setZSpan: maximum on-axis value "
72  "is less or equal to the minimum one");
73  m_z_axis = std::make_unique<FixedBinAxis>("z", n_bins, z_min, z_max);
74 }
75 
77 {
78  if (!m_alpha_axis)
79  throw std::runtime_error("Error in DepthProbeSimulation::getAlphaAxis: incident angle axis "
80  "was not initialized.");
81  return m_alpha_axis.get();
82 }
83 
85 {
86  if (!m_z_axis)
87  throw std::runtime_error("Error in DepthProbeSimulation::getZAxis: position axis "
88  "was not initialized.");
89  return m_z_axis.get();
90 }
91 
93 {
94  if (!m_z_axis || !m_alpha_axis)
95  throw std::runtime_error("Error in DepthProbeSimulation::intensityMapSize: attempt to "
96  "access non-initialized data.");
97  return m_z_axis->size() * m_alpha_axis->size();
98 }
99 
100 std::unique_ptr<IUnitConverter> DepthProbeSimulation::createUnitConverter() const
101 {
102  return std::make_unique<DepthProbeConverter>(instrument().getBeam(), *m_alpha_axis, *m_z_axis);
103 }
104 
105 DepthProbeSimulation::DepthProbeSimulation(const DepthProbeSimulation& other)
106  : Simulation(other), m_sim_elements(other.m_sim_elements), m_cache(other.m_cache)
107 {
108  if (other.m_alpha_axis)
109  m_alpha_axis.reset(other.m_alpha_axis->clone());
110  if (other.m_z_axis)
111  m_z_axis.reset(other.m_z_axis->clone());
112  for (auto iter = m_sim_elements.begin(); iter != m_sim_elements.end(); ++iter)
113  iter->setZPositions(m_alpha_axis.get());
114  initialize();
115 }
116 
117 void DepthProbeSimulation::setBeamParameters(double lambda, const IAxis& alpha_axis,
118  const IFootprintFactor* beam_shape)
119 {
120  if (lambda <= 0.0)
121  throw std::runtime_error(
122  "Error in DepthProbeSimulation::setBeamParameters: wavelength must be positive.");
123  if (alpha_axis.getMin() < 0.0)
124  throw std::runtime_error(
125  "Error in DepthProbeSimulation::setBeamParameters: minimum value on "
126  "angle axis is negative.");
127  if (alpha_axis.getMin() >= alpha_axis.getMax())
128  throw std::runtime_error(
129  "Error in DepthProbeSimulation::setBeamParameters: maximal value on "
130  "angle axis is less or equal to the minimal one.");
131  if (alpha_axis.size() == 0)
132  throw std::runtime_error(
133  "Error in DepthProbeSimulation::setBeamParameters: angle axis is empty");
134 
135  SpecularDetector1D detector(alpha_axis);
136  instrument().setDetector(detector);
137  m_alpha_axis.reset(alpha_axis.clone());
138 
139  // beam is initialized with zero-valued angles
140  // Zero-valued incident alpha is required for proper
141  // taking into account beam resolution effects
142  instrument().setBeamParameters(lambda, zero_alpha_i, zero_phi_i);
143 
144  if (beam_shape)
145  instrument().getBeam().setFootprintFactor(*beam_shape);
146 }
147 
148 void DepthProbeSimulation::initSimulationElementVector()
149 {
150  const auto& beam = instrument().getBeam();
151  m_sim_elements = generateSimulationElements(beam);
152 
153  if (!m_cache.empty())
154  return;
155  m_cache.resize(m_sim_elements.size(), std::valarray<double>(0.0, getZAxis()->size()));
156 }
157 
158 std::vector<DepthProbeElement> DepthProbeSimulation::generateSimulationElements(const Beam& beam)
159 {
160  std::vector<DepthProbeElement> result;
161 
162  const double wavelength = beam.getWavelength();
163  const double angle_shift = beam.getAlpha();
164 
165  const size_t axis_size = getAlphaAxis()->size();
166  result.reserve(axis_size);
167  for (size_t i = 0; i < axis_size; ++i) {
168  double result_angle = incidentAngle(i) + angle_shift;
169  result.emplace_back(wavelength, -result_angle, getZAxis());
170  if (!alpha_limits.isInRange(result_angle))
171  result.back().setCalculationFlag(false); // false = exclude from calculations
172  }
173  return result;
174 }
175 
176 std::unique_ptr<IComputation>
177 DepthProbeSimulation::generateSingleThreadedComputation(size_t start, size_t n_elements)
178 {
179  ASSERT(start < m_sim_elements.size() && start + n_elements <= m_sim_elements.size());
180  const auto& begin = m_sim_elements.begin() + static_cast<long>(start);
181  return std::make_unique<DepthProbeComputation>(*sample(), options(), progress(), begin,
182  begin + static_cast<long>(n_elements));
183 }
184 
185 void DepthProbeSimulation::validityCheck() const
186 {
187  const MultiLayer* current_sample = sample();
188  if (!current_sample)
189  throw std::runtime_error(
190  "Error in DepthProbeSimulation::validityCheck: no sample found in the simulation.");
191 
192  const size_t data_size = m_sim_elements.size();
193  if (data_size != getAlphaAxis()->size())
194  throw std::runtime_error(
195  "Error in DepthProbeSimulation::validityCheck: length of simulation "
196  "element vector is not equal to the number of inclination angles");
197 }
198 
199 void DepthProbeSimulation::checkCache() const
200 {
201  if (m_sim_elements.size() != m_cache.size())
202  throw std::runtime_error("Error in DepthProbeSimulation: the sizes of simulation element "
203  "vector and of its cache are different");
204 }
205 
206 void DepthProbeSimulation::validateParametrization(const ParameterDistribution& par_distr) const
207 {
208  const bool zero_mean = par_distr.getDistribution()->getMean() == 0.0;
209  if (zero_mean)
210  return;
211 
212  std::unique_ptr<ParameterPool> parameter_pool(createParameterTree());
213  const std::vector<RealParameter*> names =
214  parameter_pool->getMatchedParameters(par_distr.getMainParameterName());
215  for (const auto par : names)
216  if (par->getName().find("InclinationAngle") != std::string::npos && !zero_mean)
217  throw std::runtime_error("Error in DepthProbeSimulation: parameter distribution of "
218  "beam inclination angle should have zero mean.");
219 }
220 
221 void DepthProbeSimulation::initialize()
222 {
223  setName("DepthProbeSimulation");
224 
225  // allow for negative inclinations in the beam of specular simulation
226  // it is required for proper averaging in the case of divergent beam
227  auto inclination = instrument().getBeam().parameter("InclinationAngle");
228  inclination->setLimits(RealLimits::limited(-M_PI_2, M_PI_2));
229 }
230 
231 void DepthProbeSimulation::normalize(size_t start_ind, size_t n_elements)
232 {
233  const double beam_intensity = getBeamIntensity();
234  if (beam_intensity == 0.0)
235  return; // no normalization when beam intensity is zero
236  for (size_t i = start_ind, stop_point = start_ind + n_elements; i < stop_point; ++i) {
237  auto& element = m_sim_elements[i];
238  const double alpha_i = -element.getAlphaI();
239  const auto footprint = instrument().getBeam().footprintFactor();
240  double intensity_factor = beam_intensity;
241  if (footprint != nullptr)
242  intensity_factor = intensity_factor * footprint->calculate(alpha_i);
243  element.setIntensities(element.getIntensities() * intensity_factor);
244  }
245 }
246 
247 void DepthProbeSimulation::addBackgroundIntensity(size_t, size_t)
248 {
249  if (background())
250  throw std::runtime_error(
251  "Error: nonzero background is not supported by DepthProbeSimulation");
252 }
253 
254 void DepthProbeSimulation::addDataToCache(double weight)
255 {
256  checkCache();
257  for (size_t i = 0, size = m_sim_elements.size(); i < size; ++i)
258  m_cache[i] += m_sim_elements[i].getIntensities() * weight;
259 }
260 
261 void DepthProbeSimulation::moveDataFromCache()
262 {
263  checkCache();
264  for (size_t i = 0, size = m_sim_elements.size(); i < size; ++i)
265  m_sim_elements[i].setIntensities(std::move(m_cache[i]));
266  m_cache.clear();
267  m_cache.shrink_to_fit();
268 }
269 
270 double DepthProbeSimulation::incidentAngle(size_t index) const
271 {
272  return m_alpha_axis->getBin(index).getMidPoint();
273 }
274 
275 std::unique_ptr<OutputData<double>> DepthProbeSimulation::createIntensityData() const
276 {
277  std::unique_ptr<OutputData<double>> result = std::make_unique<OutputData<double>>();
278  result->addAxis(*getAlphaAxis());
279  result->addAxis(*getZAxis());
280 
281  std::vector<double> rawData;
282  rawData.reserve(getAlphaAxis()->size() * getZAxis()->size());
283  for (size_t i = 0, size = m_sim_elements.size(); i < size; ++i) {
284  const std::valarray<double>& fixed_angle_result = m_sim_elements[i].getIntensities();
285  rawData.insert(rawData.end(), std::begin(fixed_angle_result), std::end(fixed_angle_result));
286  }
287  result->setRawDataVector(rawData);
288 
289  return result;
290 }
291 
292 std::vector<double> DepthProbeSimulation::rawResults() const
293 {
294  validityCheck();
295  const size_t z_size = getZAxis()->size();
296  const size_t alpha_size = getAlphaAxis()->size();
297 
298  std::vector<double> result;
299  result.reserve(alpha_size * z_size);
300  for (size_t i = 0; i < alpha_size; ++i) {
301  if (m_sim_elements[i].size() != z_size)
302  throw std::runtime_error("Error in DepthProbeSimulation::rawResults: simulation "
303  "element size is not equal to the size of the position axis");
304  const auto& intensities = m_sim_elements[i].getIntensities();
305  result.insert(result.end(), std::begin(intensities), std::end(intensities));
306  }
307 
308  return result;
309 }
310 
311 void DepthProbeSimulation::setRawResults(const std::vector<double>& raw_results)
312 {
313  validityCheck();
314  const size_t z_size = getZAxis()->size();
315  const size_t alpha_size = getAlphaAxis()->size();
316 
317  if (raw_results.size() != z_size * alpha_size)
318  throw std::runtime_error(
319  "Error in DepthProbeSimulation::setRawResults: the vector to set is of invalid size");
320 
321  const double* raw_array = raw_results.data();
322  for (size_t i = 0; i < alpha_size; ++i) {
323  std::valarray<double> fixed_angle_result(raw_array, z_size);
324  m_sim_elements[i].setIntensities(std::move(fixed_angle_result));
325  raw_array += z_size;
326  }
327 }
Declares class DepthProbeComputation.
Defines class DepthProbeSimulation.
Defines classes representing one-dimensional distributions.
Defines class Histogram1D.
Defines interface IBackground.
Defines class IFootprintFactor.
Defines pure virtual base class ISampleBuilder.
Declares functions in namespace MaterialUtils.
Defines M_PI and some more mathematical constants.
Defines class MultiLayer.
Defines class ParameterPool.
Defines class RealParameter.
Defines interface UnitConverterSimple and its subclasses.
Defines a detector for specular simulations.
Beam defined by wavelength, direction and intensity.
Definition: Beam.h:27
const IFootprintFactor * footprintFactor() const
Returns footprint factor.
Definition: Beam.cpp:89
void setFootprintFactor(const IFootprintFactor &shape_factor)
Sets footprint factor to the beam.
Definition: Beam.cpp:94
const IAxis * getAlphaAxis() const
Returns a pointer to incident angle axis.
SimulationResult result() const override
Returns the results of the simulation in a format that supports unit conversion and export to numpy a...
void setBeamParameters(double lambda, int nbins, double alpha_i_min, double alpha_i_max, const IFootprintFactor *beam_shape=nullptr)
Sets beam parameters with alpha_i of the beam defined in the range.
void setZSpan(size_t n_bins, double z_min, double z_max)
Set z positions for intensity calculations.
const IAxis * getZAxis() const
Returns a pointer to z-position axis.
size_t intensityMapSize() const override
Returns the total number of the intensity values in the simulation result.
Axis with fixed bin size.
Definition: FixedBinAxis.h:24
Interface for one-dimensional axes.
Definition: IAxis.h:25
virtual IAxis * clone() const =0
clone function
virtual double getMin() const =0
Returns value of first point of axis.
virtual size_t size() const =0
retrieve the number of bins
virtual double getMax() const =0
Returns value of last point of axis.
virtual double getMean() const =0
Returns the distribution-specific mean.
Abstract base for classes that calculate the beam footprint factor.
virtual double calculate(double alpha) const =0
Calculate footprint correction coefficient from the beam incident angle alpha.
ParameterPool * createParameterTree() const
Creates new parameter pool, with all local parameters and those of its children.
Definition: INode.cpp:116
RealParameter * parameter(const std::string &name) const
Returns parameter with given 'name'.
void setBeamParameters(double wavelength, double alpha_i, double phi_i)
Sets the beam wavelength and incoming angles.
Definition: Instrument.cpp:87
void setDetector(const IDetector &detector)
Sets the detector (axes can be overwritten later)
Definition: Instrument.cpp:48
Our sample model: a stack of layers one below the other.
Definition: MultiLayer.h:42
A parametric distribution function, for use with any model parameter.
std::string getMainParameterName() const
get the main parameter's name
Limits for a real fit parameter.
Definition: RealLimits.h:25
static RealLimits limited(double left_bound_value, double right_bound_value)
Creates an object bounded from the left and right.
Definition: RealLimits.cpp:123
Wrapper around OutputData<double> that also provides unit conversions.
Pure virtual base class of OffSpecularSimulation, GISASSimulation and SpecularSimulation.
Definition: Simulation.h:38
1D detector for specular simulations.