BornAgain  1.19.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 reflection and scattering
4 //
5 //! @file Core/Simulation/SpecularSimulation.cpp
6 //! @brief Implements class OffSpecularSimulation.
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 
27 
28 namespace {
29 
30 // TODO: remove when pointwise resolution is implemented
31 std::unique_ptr<AngularSpecScan> mangledScan(const AngularSpecScan& scan, const Beam& beam)
32 {
33  const double wl = beam.wavelength();
34  const double angle_shift = beam.direction().alpha();
35  std::vector<double> angles = scan.coordinateAxis()->binCenters();
36  for (auto& val : angles)
37  val += angle_shift;
38  auto* result = new AngularSpecScan(wl, PointwiseAxis("alpha_i", std::move(angles)));
39  result->setFootprintFactor(scan.footprintFactor());
40  result->setWavelengthResolution(*scan.wavelengthResolution());
41  result->setAngleResolution(*scan.angleResolution());
42  return std::unique_ptr<AngularSpecScan>(result);
43 }
44 
45 std::vector<SpecularSimulationElement> generateSimulationElements(const Instrument& instrument,
46  const ISpecularScan& scan)
47 {
48  // TODO: remove if statement when pointwise resolution is implemented
49  if (const auto* aScan = dynamic_cast<const AngularSpecScan*>(&scan))
50  return mangledScan(*aScan, instrument.beam())->generateSimulationElements(instrument);
51 
52  return scan.generateSimulationElements(instrument);
53 }
54 
55 } // namespace
56 
57 // ************************************************************************************************
58 // class SpecularSimulation
59 // ************************************************************************************************
60 
62 {
63  initialize();
66 }
67 
69  : ISimulation(other)
70  , m_scan(other.m_scan ? other.m_scan->clone() : nullptr)
71  , m_sim_elements(other.m_sim_elements)
72  , m_cache(other.m_cache)
73 {
74  initialize();
75 }
76 
78 
80 {
81  return new SpecularSimulation(*this);
82 }
83 
85 {
86  if (detector().dimension() != 1) // detector must have only one axis
87  throw std::runtime_error("Error in SpecularSimulation::prepareSimulation: the detector was "
88  "not properly configured.");
91 }
92 
94 {
95  return m_scan->numberOfSimulationElements();
96 }
97 
99 {
100  OutputData<double> data;
101  data.addAxis(*coordinateAxis());
102 
103  if (!m_sim_elements.empty())
104  data.setRawDataVector(m_scan->createIntensities(m_sim_elements));
105  else
106  data.setAllTo(0.0);
107 
108  auto converter = UnitConverter1D::createUnitConverter(*m_scan);
109  return SimulationResult(data, *converter);
110 }
111 
113 {
114  if (m_scan)
115  throw std::runtime_error("Error in SpecularSimulation::setScan: Scan cannot be set twice");
116 
117  // TODO: move inside AngularSpecScan when pointwise resolution is implemented
118  if (scan.coordinateAxis()->lowerBound() < 0.0)
119  throw std::runtime_error(
120  "Error in SpecularSimulation::setScan: minimum value on coordinate axis is negative.");
121 
122  m_scan.reset(scan.clone());
123 
124  if(instrument().detector().dimension() > 0)
125  throw std::runtime_error("Error in SpecularSimulation::setScan: Axis already set");
126 
128 
129  // TODO: remove when pointwise resolution is implemented
130  if (const auto* aScan = dynamic_cast<const AngularSpecScan*>(&scan))
131  instrument().setBeamParameters(aScan->wavelength(), 0.0, 0.0);
132 }
133 
135 {
136  if (!m_scan || !m_scan->coordinateAxis())
137  throw std::runtime_error(
138  "Error in SpecularSimulation::getAlphaAxis: coordinate axis was not initialized.");
139  return m_scan->coordinateAxis();
140 }
141 
143 {
144  return m_scan->footprintFactor();
145 }
146 
148 {
149  return m_scan->coordinateAxis()->size();
150 }
151 
153 {
154  if (!m_scan)
155  throw std::runtime_error("Error in SpecularSimulation: beam parameters were not set.");
156  m_sim_elements = generateSimulationElements(instrument(), *m_scan);
157 
158  if (!m_cache.empty())
159  return;
160  m_cache.resize(m_sim_elements.size(), 0);
161 }
162 
163 std::unique_ptr<IComputation>
165 {
166  ASSERT(start < m_sim_elements.size() && start + n_elements <= m_sim_elements.size());
167  const auto& begin = m_sim_elements.begin() + static_cast<long>(start);
168  const auto polarized =
170  return std::make_unique<SpecularComputation>(*sample(), options(), progress(), begin,
171  begin + static_cast<long>(n_elements), polarized);
172 }
173 
175 {
176  if (m_sim_elements.size() != m_cache.size())
177  throw std::runtime_error("Error in SpecularSimulation: the sizes of simulation element "
178  "vector and of its cache are different");
179 }
180 
182 {
183  const bool zero_mean = par_distr.getDistribution()->getMean() == 0.0;
184  if (zero_mean)
185  return;
186 
187  std::unique_ptr<ParameterPool> parameter_pool(createParameterTree());
188  const std::vector<RealParameter*> names =
189  parameter_pool->getMatchedParameters(par_distr.getMainParameterName());
190  for (const auto par : names)
191  if (par->getName().find("InclinationAngle") != std::string::npos && !zero_mean)
192  throw std::runtime_error("Error in SpecularSimulation: parameter distribution of "
193  "beam inclination angle should have zero mean.");
194 }
195 
197 {
198  setName("SpecularSimulation");
199 
200  // allow for negative inclinations in the beam of specular simulation
201  // it is required for proper averaging in the case of divergent beam
202  instrument()
203  .beam()
204  .parameter("InclinationAngle")
206 }
207 
208 void SpecularSimulation::normalize(size_t start_ind, size_t n_elements)
209 {
210  const double beam_intensity = beam().intensity();
211 
212  std::vector<double> footprints;
213  // TODO: use just m_scan when pointwise resolution is implemented
214  if (const auto* aScan = dynamic_cast<const AngularSpecScan*>(m_scan.get()))
215  footprints = mangledScan(*aScan, beam())->footprint(start_ind, n_elements);
216  else
217  footprints = m_scan->footprint(start_ind, n_elements);
218 
219  for (size_t i = start_ind, k = 0; i < start_ind + n_elements; ++i, ++k) {
220  auto& element = m_sim_elements[i];
221  element.setIntensity(element.intensity() * beam_intensity * footprints[k]);
222  }
223 }
224 
225 void SpecularSimulation::addBackgroundIntensity(size_t start_ind, size_t n_elements)
226 {
227  if (!background())
228  return;
229  for (size_t i = start_ind, stop_point = start_ind + n_elements; i < stop_point; ++i) {
230  auto& element = m_sim_elements[i];
231  element.setIntensity(background()->addBackground(element.intensity()));
232  }
233 }
234 
236 {
237  checkCache();
238  for (size_t i = 0, size = m_sim_elements.size(); i < size; ++i)
239  m_cache[i] += m_sim_elements[i].intensity() * weight;
240 }
241 
243 {
244  checkCache();
245  for (size_t i = 0, size = m_sim_elements.size(); i < size; ++i)
246  m_sim_elements[i].setIntensity(m_cache[i]);
247  m_cache.clear();
248  m_cache.shrink_to_fit();
249 }
250 
251 std::vector<double> SpecularSimulation::rawResults() const
252 {
253  std::vector<double> result;
254  result.resize(m_sim_elements.size());
255  for (unsigned i = 0; i < m_sim_elements.size(); ++i)
256  result[i] = m_sim_elements[i].intensity();
257  return result;
258 }
259 
260 void SpecularSimulation::setRawResults(const std::vector<double>& raw_data)
261 {
263  if (raw_data.size() != m_sim_elements.size())
264  throw std::runtime_error("SpecularSimulation::setRawResults: size of vector passed as "
265  "argument doesn't match number of elements in this simulation");
266  for (unsigned i = 0; i < raw_data.size(); i++)
267  m_sim_elements[i].setIntensity(raw_data[i]);
269 }
Declares AngularSpecScan class.
#define ASSERT(condition)
Definition: Assert.h:31
#define M_PI_2
Definition: Constants.h:45
Defines classes representing one-dimensional distributions.
Defines interface IBackground.
Defines interface IFootprintFactor.
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.
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
kvector_t analyzerDirection() const
Retrieve the analyzer characteristics.
double alpha() const
Definition: Direction.h:29
Interface for one-dimensional axes.
Definition: IAxis.h:25
virtual std::vector< double > binCenters() const
Definition: IAxis.cpp:22
virtual double lowerBound() const =0
Returns value of first point of axis.
void addAxis(const IAxis &axis)
Definition: IDetector.cpp:41
const DetectionProperties & detectionProperties() const
Returns detection properties.
Definition: IDetector.h:82
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: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
virtual void prepareSimulation()
Put into a clean state for running a simulation.
IDetector & detector()
Definition: ISimulation.h:63
virtual void transferResultsToIntensityMap()
Creates the appropriate data structure (e.g.
Definition: ISimulation.h:114
Beam & beam()
Definition: ISimulation.h:58
Abstract base class for all types of specular scans.
Definition: ISpecularScan.h:32
virtual const IAxis * coordinateAxis() const =0
Returns coordinate axis assigned to the data holder.
virtual std::vector< SpecularSimulationElement > generateSimulationElements(const Instrument &instrument) const =0
Generates simulation elements for specular simulations.
ISpecularScan * clone() const override=0
Assembles beam, detector and their relative positions with respect to the sample.
Definition: Instrument.h:32
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
IDetector & detector()
Definition: Instrument.cpp:107
void initDetector()
init detector with beam settings
Definition: Instrument.cpp:59
Beam & beam()
Definition: Instrument.h:43
void setAllTo(const T &value)
Sets content of output data to specific value.
Definition: OutputData.h:476
void addAxis(const IAxis &new_axis)
Definition: OutputData.h:295
void setRawDataVector(const std::vector< T > &data_vector)
Sets new values to raw data vector.
Definition: OutputData.h:556
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:37
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.
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 ISimulation 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.
matrixFFVector_t polarized(const SimulationElement &sim_element, const std::vector< FormFactorCoherentSum > &ff_wrappers)