BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
SpecularSimulation.cpp
Go to the documentation of this file.
1 // ************************************************************************** //
2 //
3 // BornAgain: simulate and fit scattering at grazing incidence
4 //
5 //! @file Core/Simulation/SpecularSimulation.cpp
6 //! @brief Implements class OffSpecSimulation.
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 
28 
29 namespace
30 {
31 
32 // TODO: remove when pointwise resolution is implemented
33 std::unique_ptr<AngularSpecScan> mangledScan(const AngularSpecScan& scan, const Beam& beam)
34 {
35  const double wl = beam.getWavelength();
36  const double angle_shift = beam.getAlpha();
37  std::vector<double> angles = scan.coordinateAxis()->getBinCenters();
38  for (auto& val : angles)
39  val += angle_shift;
40  auto* result = new AngularSpecScan(wl, PointwiseAxis("alpha_i", std::move(angles)));
41  result->setFootprintFactor(scan.footprintFactor());
42  result->setWavelengthResolution(*scan.wavelengthResolution());
43  result->setAngleResolution(*scan.angleResolution());
44  return std::unique_ptr<AngularSpecScan>(result);
45 }
46 
47 std::vector<SpecularSimulationElement> generateSimulationElements(const Instrument& instrument,
48  const ISpecularScan& scan)
49 {
50  std::vector<SpecularSimulationElement> result;
51 
52  // TODO: remove if-else statement when pointwise resolution is implemented
53  if (const auto* aScan = dynamic_cast<const AngularSpecScan*>(&scan))
54  result = mangledScan(*aScan, instrument.getBeam())->generateSimulationElements();
55  else
56  result = scan.generateSimulationElements();
57 
58  // add polarization and analyzer operators
59  const auto& polarization = instrument.getBeam().getPolarization();
60  const auto& analyzer = instrument.detector().detectionProperties().analyzerOperator();
61 
62  for (auto& elem : result)
63  elem.setPolarizationHandler({polarization, analyzer});
64 
65  return result;
66 }
67 
68 } // namespace
69 
70 // ************************************************************************** //
71 // class SpecularSimulation
72 // ************************************************************************** //
73 
75 {
76  initialize();
77 }
78 
80  : Simulation(other), m_scan(other.m_scan ? other.m_scan->clone() : nullptr),
81  m_sim_elements(other.m_sim_elements), m_cache(other.m_cache)
82 {
83  initialize();
84 }
85 
87 
89 {
90  return new SpecularSimulation(*this);
91 }
92 
94 {
95  if (instrument().getDetectorDimension() != 1) // detector must have only one axis
96  throw std::runtime_error("Error in SpecularSimulation::prepareSimulation: the detector was "
97  "not properly configured.");
100 }
101 
103 {
104  return m_scan->numberOfSimulationElements();
105 }
106 
108 {
109  OutputData<double> data;
110  data.addAxis(*coordinateAxis());
111 
112  if (!m_sim_elements.empty())
113  data.setRawDataVector(m_scan->createIntensities(m_sim_elements));
114  else
115  data.setAllTo(0.0);
116 
117  auto converter = UnitConverter1D::createUnitConverter(*m_scan);
118  return SimulationResult(data, *converter);
119 }
120 
122 {
123  // TODO: move inside AngularSpecScan when pointwise resolution is implemented
124  if (scan.coordinateAxis()->getMin() < 0.0)
125  throw std::runtime_error(
126  "Error in SpecularSimulation::setScan: minimum value on coordinate axis is negative.");
127 
128  m_scan.reset(scan.clone());
129 
130  SpecularDetector1D detector(*scan.coordinateAxis());
131  instrument().setDetector(detector);
132 
133  // TODO: remove when pointwise resolution is implemented
134  if (const auto* aScan = dynamic_cast<const AngularSpecScan*>(&scan))
135  instrument().setBeamParameters(aScan->wavelength(), 0.0, 0.0);
136 }
137 
139 {
140  if (!m_scan || !m_scan->coordinateAxis())
141  throw std::runtime_error(
142  "Error in SpecularSimulation::getAlphaAxis: coordinate axis was not initialized.");
143  return m_scan->coordinateAxis();
144 }
145 
147 {
148  return m_scan->footprintFactor();
149 }
150 
152 {
153  return m_scan->coordinateAxis()->size();
154 }
155 
157 {
158  if (!m_scan)
159  throw std::runtime_error("Error in SpecularSimulation: beam parameters were not set.");
161 
162  if (!m_cache.empty())
163  return;
164  m_cache.resize(m_sim_elements.size(), 0);
165 }
166 
167 std::unique_ptr<IComputation>
169 {
170  ASSERT(start < m_sim_elements.size() && start + n_elements <= m_sim_elements.size());
171  const auto& begin = m_sim_elements.begin() + static_cast<long>(start);
172  return std::make_unique<SpecularComputation>(*sample(), options(), progress(), begin,
173  begin + static_cast<long>(n_elements));
174 }
175 
177 {
178  if (m_sim_elements.size() != m_cache.size())
179  throw std::runtime_error("Error in SpecularSimulation: the sizes of simulation element "
180  "vector and of its cache are different");
181 }
182 
184 {
185  const bool zero_mean = par_distr.getDistribution()->getMean() == 0.0;
186  if (zero_mean)
187  return;
188 
189  std::unique_ptr<ParameterPool> parameter_pool(createParameterTree());
190  const std::vector<RealParameter*> names =
191  parameter_pool->getMatchedParameters(par_distr.getMainParameterName());
192  for (const auto par : names)
193  if (par->getName().find("InclinationAngle") != std::string::npos && !zero_mean)
194  throw std::runtime_error("Error in SpecularSimulation: parameter distribution of "
195  "beam inclination angle should have zero mean.");
196 }
197 
199 {
200  setName("SpecularSimulation");
201 
202  // allow for negative inclinations in the beam of specular simulation
203  // it is required for proper averaging in the case of divergent beam
204  instrument()
205  .getBeam()
206  .parameter("InclinationAngle")
208 }
209 
210 void SpecularSimulation::normalize(size_t start_ind, size_t n_elements)
211 {
212  const double beam_intensity = getBeamIntensity();
213  if (beam_intensity == 0.0)
214  return; // no normalization when beam intensity is zero
215 
216  std::vector<double> footprints;
217  // TODO: use just m_scan when pointwise resolution is implemented
218  if (const auto* aScan = dynamic_cast<const AngularSpecScan*>(m_scan.get()))
219  footprints = mangledScan(*aScan, instrument().getBeam())->footprint(start_ind, n_elements);
220  else
221  footprints = m_scan->footprint(start_ind, n_elements);
222 
223  for (size_t i = start_ind, k = 0; i < start_ind + n_elements; ++i, ++k) {
224  auto& element = m_sim_elements[i];
225  element.setIntensity(element.getIntensity() * beam_intensity * footprints[k]);
226  }
227 }
228 
229 void SpecularSimulation::addBackgroundIntensity(size_t start_ind, size_t n_elements)
230 {
231  if (!background())
232  return;
233  for (size_t i = start_ind, stop_point = start_ind + n_elements; i < stop_point; ++i) {
234  auto& element = m_sim_elements[i];
235  element.setIntensity(background()->addBackground(element.getIntensity()));
236  }
237 }
238 
240 {
241  checkCache();
242  for (size_t i = 0, size = m_sim_elements.size(); i < size; ++i)
243  m_cache[i] += m_sim_elements[i].getIntensity() * weight;
244 }
245 
247 {
248  checkCache();
249  for (size_t i = 0, size = m_sim_elements.size(); i < size; ++i)
250  m_sim_elements[i].setIntensity(m_cache[i]);
251  m_cache.clear();
252  m_cache.shrink_to_fit();
253 }
254 
255 std::vector<double> SpecularSimulation::rawResults() const
256 {
257  std::vector<double> result;
258  result.resize(m_sim_elements.size());
259  for (unsigned i = 0; i < m_sim_elements.size(); ++i)
260  result[i] = m_sim_elements[i].getIntensity();
261  return result;
262 }
263 
264 void SpecularSimulation::setRawResults(const std::vector<double>& raw_data)
265 {
267  if (raw_data.size() != m_sim_elements.size())
268  throw std::runtime_error("SpecularSimulation::setRawResults: size of vector passed as "
269  "argument doesn't match number of elements in this simulation");
270  for (unsigned i = 0; i < raw_data.size(); i++)
271  m_sim_elements[i].setIntensity(raw_data[i]);
273 }
Declares AngularSpecScan class.
#define ASSERT(condition)
Definition: Assert.h:26
Defines classes representing one-dimensional distributions.
Defines class Histogram1D.
Defines interface IBackground.
Defines class IFootprintFactor.
#define M_PI_2
Definition: MathConstants.h:40
Defines class ParameterPool.
Defines class PointwiseAxis.
Defines class RealParameter.
Defines class SpecularComputation.
Defines a detector for specular simulations.
Declares the class SpecularSimulationElement.
Defines class SpecularSimulation.
Defines UnitConverter1D class and derived classes.
Scan type with inclination angles as coordinate values and a unique wavelength.
virtual const IFootprintFactor * footprintFactor() const override
Returns IFootprintFactor object pointer.
const ScanResolution * wavelengthResolution() const
const ScanResolution * angleResolution() const
virtual const IAxis * coordinateAxis() const override
Returns coordinate axis assigned to the data holder.
Beam defined by wavelength, direction and intensity.
Definition: Beam.h:27
double getWavelength() const
Definition: Beam.h:69
Eigen::Matrix2cd getPolarization() const
Returns the polarization density matrix (in spin basis along z-axis)
Definition: Beam.cpp:123
double getAlpha() const
Definition: Beam.h:70
Eigen::Matrix2cd analyzerOperator() const
Return the polarization density matrix (in spin basis along z-axis)
Interface for one-dimensional axes.
Definition: IAxis.h:25
virtual double getMin() const =0
Returns value of first point of axis.
virtual std::vector< double > getBinCenters() const
Definition: IAxis.cpp:23
const DetectionProperties & detectionProperties() const
Returns detection properties.
Definition: IDetector.h:93
virtual double getMean() const =0
Returns the distribution-specific mean.
Abstract base for classes that calculate the beam footprint factor.
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 setName(const std::string &name)
Pure virtual base class for all types of specular scans.
Definition: ISpecularScan.h:31
virtual std::vector< SpecularSimulationElement > generateSimulationElements() const =0
Generates simulation elements for specular simulations.
virtual const IAxis * coordinateAxis() const =0
Returns coordinate axis assigned to the data holder.
ISpecularScan * clone() const override=0
Assembles beam, detector and their relative positions with respect to the sample.
Definition: Instrument.h:34
Beam & getBeam()
Definition: Instrument.h:44
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
IDetector & detector()
Definition: Instrument.cpp:133
void initDetector()
init detector with beam settings
Definition: Instrument.cpp:55
void setAllTo(const T &value)
Sets content of output data to specific value.
Definition: OutputData.h:479
void addAxis(const IAxis &new_axis)
Definition: OutputData.h:289
void setRawDataVector(const std::vector< T > &data_vector)
Sets new values to raw data vector.
Definition: OutputData.h:559
A parametric distribution function, for use with any model parameter.
const IDistribution1D * getDistribution() const
std::string getMainParameterName() const
get the main parameter's name
Axis containing arbitrary (non-equidistant) coordinate values.
Definition: PointwiseAxis.h:33
static RealLimits limited(double left_bound_value, double right_bound_value)
Creates an object bounded from the left and right.
Definition: RealLimits.cpp:123
RealParameter & setLimits(const RealLimits &limits)
Wrapper around OutputData<double> that also provides unit conversions.
Pure virtual base class of OffSpecularSimulation, GISASSimulation and SpecularSimulation.
Definition: Simulation.h:38
const Instrument & instrument() const
Definition: Simulation.h:55
ProgressHandler & progress()
Definition: Simulation.h:121
const IBackground * background() const
Definition: Simulation.h:75
virtual void prepareSimulation()
Put into a clean state for running a simulation.
Definition: Simulation.cpp:189
virtual void transferResultsToIntensityMap()
Creates the appropriate data structure (e.g.
Definition: Simulation.h:110
const MultiLayer * sample() const
Definition: Simulation.cpp:247
double getBeamIntensity() const
Definition: Simulation.cpp:178
const SimulationOptions & options() const
Definition: Simulation.h:120
1D detector for specular simulations.
Main class to run a specular simulation.
std::vector< double > rawResults() const override
const IAxis * coordinateAxis() const
Returns a pointer to coordinate axis.
void initialize()
Initializes simulation.
SimulationResult result() const override
Returns the results of the simulation in a format that supports unit conversion and export to numpy a...
void setRawResults(const std::vector< double > &raw_data) override
SpecularSimulation * clone() const override
std::vector< double > m_cache
void addDataToCache(double weight) override
std::vector< SpecularSimulationElement > m_sim_elements
size_t intensityMapSize() const override
Returns the total number of the intensity values in the simulation result.
std::unique_ptr< ISpecularScan > m_scan
~SpecularSimulation() override
void initSimulationElementVector() override
Initializes the vector of Simulation elements.
void setScan(const ISpecularScan &scan)
Sets chosen specular scan to the simulation.
void addBackgroundIntensity(size_t start_ind, size_t n_elements) override
void prepareSimulation() override
Put into a clean state for running a simulation.
size_t numberOfSimulationElements() const override
Gets the number of elements this simulation needs to calculate.
void moveDataFromCache() override
void validateParametrization(const ParameterDistribution &par_distr) const override
Checks the distribution validity for simulation.
const IFootprintFactor * footprintFactor() const
Returns a pointer to footprint factor holder.
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::unique_ptr< IComputation > generateSingleThreadedComputation(size_t start, size_t n_elements) override
Generate a single threaded computation for a given range of simulation elements.
static std::unique_ptr< UnitConverter1D > createUnitConverter(const ISpecularScan &handler)
Factory function to create unit converter for particular type of specular data.
std::unique_ptr< AngularSpecScan > mangledScan(const AngularSpecScan &scan, const Beam &beam)
std::vector< SpecularSimulationElement > generateSimulationElements(const Instrument &instrument, const ISpecularScan &scan)