BornAgain  1.19.79
Open-source research software to simulate and fit neutron and x-ray reflectometry and grazing-incidence small-angle scattering
SimDataPair.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Sim/Fitting/SimDataPair.cpp
6 //! @brief Defines class SimDataPair.
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 
16 #include "Base/Axis/Frame.h"
17 #include "Base/Axis/IAxis.h"
18 #include "Base/Math/Numeric.h"
19 #include "Base/Util/Assert.h"
22 #include "Device/Data/DataUtils.h"
23 #include "Device/Data/Datafield.h"
25 #include "Sim/Scan/ISpecularScan.h"
27 #include <utility>
28 
29 namespace {
30 
31 [[noreturn]] void throwInitializationException(std::string method)
32 {
33  std::stringstream ss;
34  ss << "Error in SimDataPair::" << method << ": Trying access non-initialized data\n";
35  throw std::runtime_error(ss.str());
36 }
37 
38 std::unique_ptr<Datafield> initUserWeights(const Datafield& shape, double value)
39 {
40  auto result = std::make_unique<Datafield>(shape.frame().cloned_axes());
41  result->setAllTo(value);
42  return result;
43 }
44 
45 bool haveSameSizes(const IDetector& detector, const Datafield& data)
46 {
47  if (data.rank() != detector.rank())
48  return false;
49 
50  for (size_t i = 0; i < detector.rank(); ++i)
51  if (data.axis(i).size() != detector.axis(i).size())
52  return false;
53 
54  return true;
55 }
56 
57 //! Convert user data to SimulationResult object for later drawing in various axes units.
58 //! User data will be cropped to the ROI defined in the simulation, amplitudes in areas
59 //! corresponding to the masked areas of the detector will be set to zero.
60 
61 SimulationResult convertData(const ISimulation2D& simulation, const Datafield& data)
62 {
63  const ICoordSystem* coordSystem = simulation.createCoordSystem();
64  auto roi_data = std::make_unique<Datafield>(coordSystem->defaultAxes());
65 
66  if (roi_data->hasSameSizes(data)) {
67  // data is already cropped to ROI
69  (*roi_data)[it.roiIndex()] = data[it.roiIndex()];
70  });
71  } else if (haveSameSizes(simulation.detector(), data)) {
72  // experimental data has same shape as the detector, we have to copy the original
73  // data to a smaller roi map
75  (*roi_data)[it.roiIndex()] = data[it.detectorIndex()];
76  });
77  } else
78  throw std::runtime_error(
79  "FitObject::init_dataset: Detector and experimental data have different shape");
80 
81  return SimulationResult(*roi_data, std::move(coordSystem));
82 }
83 
84 } // namespace
85 
86 // ************************************************************************************************
87 // class implementation
88 // ************************************************************************************************
89 
91  std::unique_ptr<Datafield>&& raw_stdv, double user_weight)
92  : m_simulation_builder(std::move(builder))
93  , m_raw_data(raw_data.clone())
94  , m_raw_uncertainties(std::move(raw_stdv))
95 {
96  m_raw_user_weights = initUserWeights(*m_raw_data, user_weight);
97  validate();
98 }
99 
101  std::unique_ptr<Datafield>&& raw_stdv,
102  std::unique_ptr<Datafield>&& user_weights)
103  : m_simulation_builder(std::move(builder))
104  , m_raw_data(raw_data.clone())
105  , m_raw_uncertainties(std::move(raw_stdv))
106  , m_raw_user_weights(std::move(user_weights))
107 {
108  if (!m_raw_user_weights)
109  m_raw_user_weights = initUserWeights(*m_raw_data, 1.0);
110  validate();
111 }
112 
114  : m_simulation_builder(std::move(other.m_simulation_builder))
115  , m_sim_data(std::move(other.m_sim_data))
116  , m_exp_data(std::move(other.m_exp_data))
117  , m_uncertainties(std::move(other.m_uncertainties))
118  , m_user_weights(std::move(other.m_user_weights))
119  , m_raw_data(std::move(other.m_raw_data))
120  , m_raw_uncertainties(std::move(other.m_raw_uncertainties))
121  , m_raw_user_weights(std::move(other.m_raw_user_weights))
122 {
123  validate();
124 }
125 
126 SimDataPair::~SimDataPair() = default;
127 
129 {
130  std::unique_ptr<ISimulation> simulation = m_simulation_builder(params);
131  ASSERT(simulation);
132  m_sim_data = simulation->simulate();
133 
135  return;
136 
137  if (m_sim_data.empty())
138  throwInitializationException("initResultArrays");
139 
140  auto* const sim2d = dynamic_cast<ISimulation2D*>(simulation.get());
141  if (sim2d) {
142  m_exp_data = convertData(*sim2d, *m_raw_data);
143  m_user_weights = convertData(*sim2d, *m_raw_user_weights);
144  } else {
145  const ICoordSystem& converter = m_sim_data.converter();
146  m_exp_data = SimulationResult(*m_raw_data, converter);
148  }
149 
150  if (sim2d && containsUncertainties())
151  m_uncertainties = convertData(*sim2d, *m_raw_uncertainties);
152  else {
153  const ICoordSystem& converter = m_sim_data.converter();
154  auto dummy_array = std::make_unique<Datafield>(converter.defaultAxes());
155  m_uncertainties = SimulationResult(*dummy_array, converter);
156  }
157 }
158 
160 {
161  return static_cast<bool>(m_raw_uncertainties);
162 }
163 
165 {
166  if (m_sim_data.empty())
167  throwInitializationException("simulationResult");
168  return m_sim_data;
169 }
170 
172 {
173  if (m_exp_data.empty())
174  throwInitializationException("experimentalData");
175  return m_exp_data;
176 }
177 
179 {
180  if (m_uncertainties.empty())
181  throwInitializationException("uncertainties");
182  return m_uncertainties;
183 }
184 
185 //! Returns the user uncertainties cut to the ROI area.
187 {
188  if (m_user_weights.empty())
189  throwInitializationException("userWeights");
190  return m_user_weights;
191 }
192 
193 //! Returns relative difference between simulation and experimental data.
194 
196 {
197  if (m_sim_data.empty() || m_exp_data.empty())
198  throwInitializationException("relativeDifference");
199 
200  SimulationResult result = m_sim_data;
201  for (size_t i = 0, size = result.size(); i < size; ++i)
202  result[i] = Numeric::relativeDifference(result[i], m_exp_data[i]);
203 
204  return result;
205 }
206 
208 {
209  if (m_sim_data.empty() || m_exp_data.empty())
210  throwInitializationException("absoluteDifference");
211 
212  SimulationResult result = m_sim_data;
213  for (size_t i = 0, size = result.size(); i < size; ++i)
214  result[i] = Numeric::GetAbsoluteDifference(result[i], m_exp_data[i]);
215 
216  return result;
217 }
218 
219 std::vector<double> SimDataPair::experimental_array() const
220 {
221  if (m_exp_data.empty())
222  throwInitializationException("experimental_array");
223  return m_exp_data.datafield()->flatVector();
224 }
225 
226 std::vector<double> SimDataPair::simulation_array() const
227 {
228  if (m_sim_data.empty())
229  throwInitializationException("simulation_array");
230  return m_sim_data.datafield()->flatVector();
231 }
232 
233 std::vector<double> SimDataPair::uncertainties_array() const
234 {
235  if (m_uncertainties.empty())
236  throwInitializationException("uncertainties_array");
238 }
239 
240 std::vector<double> SimDataPair::user_weights_array() const
241 {
242  if (m_user_weights.empty())
243  throwInitializationException("user_weights_array");
244  return m_user_weights.datafield()->flatVector();
245 }
246 
248 {
250  throw std::runtime_error("Error in SimDataPair: simulation builder is empty");
251 
252  if (!m_raw_data)
253  throw std::runtime_error("Error in SimDataPair: passed experimental data array is empty");
254 
255  if (m_raw_uncertainties && !m_raw_uncertainties->hasSameShape(*m_raw_data))
256  throw std::runtime_error(
257  "Error in SimDataPair: experimental data and uncertainties have different shape.");
258 
259  if (!m_raw_user_weights || !m_raw_user_weights->hasSameShape(*m_raw_data))
260  throw std::runtime_error(
261  "Error in SimDataPair: user weights are not initialized or have invalid shape");
262 }
Defines the macro ASSERT.
#define ASSERT(condition)
Definition: Assert.h:45
Defines CoordSystem1D class and derived classes.
Defines interface CoordSystem2D and its subclasses.
Defines namespace DataUtils.
Defines and implements templated class Datafield.
std::function< std::unique_ptr< ISimulation >(const mumufit::Parameters &)> simulation_builder_t
Definition: FitTypes.h:34
Defines and implements templated class Frame.
Defines interface IAxis.
Defines common detector interface.
Defines interface ISimulation2D.
Declares interface ISpecularScan.
Defines constants and "almost equal" in namespace Numeric.
Defines class SimDataPair.
Stores radiation power per bin.
Definition: Datafield.h:30
const IAxis & axis(size_t k) const
Definition: Datafield.cpp:91
std::vector< double > flatVector() const
Returns copy of raw data vector.
Definition: Datafield.cpp:119
size_t rank() const
Definition: Datafield.cpp:75
const Frame & frame() const
Definition: Datafield.cpp:86
std::vector< IAxis * > cloned_axes() const
Returns cloned axes.
Definition: Frame.cpp:32
virtual size_t size() const =0
Returns the number of bins.
Interface to provide axis translations to different units for simulation output.
Definition: ICoordSystem.h:40
std::vector< IAxis * > defaultAxes() const
Abstract detector interface.
Definition: IDetector.h:57
void iterateOverNonMaskedPoints(std::function< void(const_iterator)> func) const
Iterate over all non-masked points within "region of interest". If no region of interest is explicitl...
Definition: IDetector.cpp:252
const IAxis & axis(size_t index) const
One axis of the complete detector. Any region of interest is not taken into account.
Definition: IDetector.cpp:74
size_t rank() const
Returns number of defined axes.
Definition: IDetector.cpp:64
Abstract base class of simulations that generate 2D patterns.
Definition: ISimulation2D.h:34
IDetector & detector()
Definition: ISimulation2D.h:58
virtual ICoordSystem * createCoordSystem() const =0
Holds pair of simulation/experimental data to fit.
Definition: SimDataPair.h:30
SimulationResult userWeights() const
Returns the user uncertainties cut to the ROI area.
SimulationResult m_sim_data
Current simulation results. Masked areas are nullified.
Definition: SimDataPair.h:88
std::vector< double > experimental_array() const
Returns the flattened experimental data cut to the ROI area.
SimulationResult absoluteDifference() const
Returns the absolute difference between simulated and experimental data cut to the ROI area.
std::vector< double > user_weights_array() const
Returns a flat array of user weights cut to the ROI area.
std::vector< double > uncertainties_array() const
Returns the flattened experimental uncertainties cut to the ROI area. If no uncertainties are availab...
std::vector< double > simulation_array() const
Returns the flattened simulated intensities cut to the ROI area.
SimulationResult uncertainties() const
Returns the data uncertainties cut to the ROI area If no uncertainties present, returns zero-filled S...
SimulationResult experimentalData() const
Returns the experimental data cut to the ROI area.
bool containsUncertainties() const
void validate() const
std::unique_ptr< Datafield > m_raw_data
Raw experimental data as obtained from the user.
Definition: SimDataPair.h:97
std::unique_ptr< Datafield > m_raw_uncertainties
Data uncertainties as provided by the user.
Definition: SimDataPair.h:99
SimDataPair(simulation_builder_t builder, const Datafield &raw_data, std::unique_ptr< Datafield > &&raw_stdv, double user_weight=1.0)
Definition: SimDataPair.cpp:90
SimulationResult relativeDifference() const
Returns the relative difference between simulated and experimental data cut to the ROI area.
SimulationResult simulationResult() const
Returns the result of last computed simulation.
std::unique_ptr< Datafield > m_raw_user_weights
User-defined weights.
Definition: SimDataPair.h:101
SimulationResult m_uncertainties
Weights from experimental data uncertainties. Masked areas are nullified.
Definition: SimDataPair.h:92
SimulationResult m_exp_data
Experimental data cut to the ROI. Masked areas are nullified.
Definition: SimDataPair.h:90
SimulationResult m_user_weights
Manually defined (user) weights. Masked areas are nullified.
Definition: SimDataPair.h:94
void execSimulation(const mumufit::Parameters &params)
simulation_builder_t m_simulation_builder
ISimulation builder from the user to construct simulation for given set of parameters.
Definition: SimDataPair.h:85
An iterator for SimulationArea.
Wrapper around Datafield that also provides unit conversions.
bool empty() const
const ICoordSystem & converter() const
Returns underlying unit converter.
size_t size() const
Datafield * datafield(Coords units=Coords::UNDEFINED) const
A collection of fit parameters.
Definition: Parameters.h:26
double relativeDifference(double a, double b)
Returns the safe relative difference, which is 2(|a-b|)/(|a|+|b|) except in special cases.
Definition: Numeric.cpp:29
double GetAbsoluteDifference(double a, double b)
Returns the absolute value of the difference between a and b.
Definition: Numeric.cpp:23