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