BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
IntensityDataFunctions.cpp
Go to the documentation of this file.
1 // ************************************************************************** //
2 //
3 // BornAgain: simulate and fit scattering at grazing incidence
4 //
5 //! @file Device/Instrument/IntensityDataFunctions.cpp
6 //! @brief Implement class IntensityDataFunctions.
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 
22 #include "Fit/TestEngine/Numeric.h"
23 #include <cmath>
24 #include <iostream>
25 
26 //! Returns sum of relative differences between each pair of elements:
27 //! (a, b) -> 2*abs(a - b)/(|a| + |b|) ( and zero if a=b=0 within epsilon )
29  const SimulationResult& ref)
30 {
31  if (dat.size() != ref.size())
32  throw std::runtime_error("Error in IntensityDataFunctions::RelativeDifference: "
33  "different number of elements");
34  if (dat.empty())
35  return 0.0;
36  double sum_of_diff = 0.0;
37  for (size_t i = 0; i < dat.size(); ++i) {
38  sum_of_diff += Numeric::GetRelativeDifference(dat[i], ref[i]);
39  }
40  return sum_of_diff / dat.size();
41 }
42 
43 //! Returns relative difference between two data sets sum(dat[i] - ref[i])/ref[i]).
45  const OutputData<double>& ref)
46 {
47  if (!dat.hasSameDimensions(ref))
49  "IntensityDataFunctions::getRelativeDifference() -> "
50  "Error. Different dimensions of data and reference.");
51 
52  double diff = 0.0;
53  for (size_t i = 0; i < dat.getAllocatedSize(); ++i)
54  diff += Numeric::GetRelativeDifference(dat[i], ref[i]);
55  diff /= dat.getAllocatedSize();
56 
57  if (std::isnan(diff))
58  throw Exceptions::RuntimeErrorException("diff=NaN!");
59  return diff;
60 }
61 
63 {
64  return getRelativeDifference(*std::unique_ptr<OutputData<double>>(dat.getData().meanValues()),
65  *std::unique_ptr<OutputData<double>>(ref.getData().meanValues()));
66 }
67 
68 //! Returns true is relative difference is below threshold; prints informative output
70  const OutputData<double>& ref,
71  const double threshold)
72 {
73  const double diff = getRelativeDifference(dat, ref);
74  if (diff > threshold) {
75  std::cerr << " => FAILED: relative deviation of dat from ref is " << diff
76  << ", above given threshold " << threshold << "\n";
77  return false;
78  }
79  if (diff)
80  std::cerr << " => OK: relative deviation of dat from ref is " << diff
81  << ", within given threshold " << threshold << "\n";
82  else
83  std::cout << " => OK: dat = ref\n";
84  return true;
85 }
86 
87 std::unique_ptr<OutputData<double>>
89  const OutputData<double>& reference)
90 {
91  if (!data.hasSameDimensions(reference))
93  "IntensityDataFunctions::createRelativeDifferenceData() -> "
94  "Error. Different dimensions of data and reference.");
95  std::unique_ptr<OutputData<double>> result(reference.clone());
96  for (size_t i = 0; i < result->getAllocatedSize(); ++i)
97  (*result)[i] = Numeric::GetRelativeDifference(data[i], reference[i]);
98  return result;
99 }
100 
101 std::unique_ptr<OutputData<double>>
103 {
104  if (data.getRank() != 2)
105  throw Exceptions::LogicErrorException("IntensityDataFunctions::rotateDataByN90Deg()"
106  " -> Error! Works only on two-dimensional data");
107  n = (4 + n % 4) % 4;
108  if (n == 0)
109  return std::unique_ptr<OutputData<double>>(data.clone());
110  std::unique_ptr<OutputData<double>> output(new OutputData<double>());
111 
112  // swapping axes if necessary
113  const IAxis& x_axis = data.getAxis(0);
114  const IAxis& y_axis = data.getAxis(1);
115  output->addAxis(n == 2 ? x_axis : y_axis);
116  output->addAxis(n == 2 ? y_axis : x_axis);
117 
118  // creating index mapping
119  std::function<void(std::vector<int>&)> index_mapping;
120  if (n == 2) {
121  const int end_bin_x = static_cast<int>(x_axis.size()) - 1;
122  const int end_bin_y = static_cast<int>(y_axis.size()) - 1;
123  index_mapping = [end_bin_x, end_bin_y](std::vector<int>& inds) {
124  inds[0] = end_bin_x - inds[0];
125  inds[1] = end_bin_y - inds[1];
126  };
127  } else {
128  const size_t rev_axis_i = n % 3;
129  const size_t end_bin = data.getAxis(rev_axis_i).size() - 1;
130  index_mapping = [rev_axis_i, end_bin](std::vector<int>& inds) {
131  const int tmp_index = inds[rev_axis_i];
132  inds[rev_axis_i] = inds[rev_axis_i ^ 1];
133  inds[rev_axis_i ^ 1] = static_cast<int>(end_bin) - tmp_index;
134  };
135  }
136 
137  for (size_t index = 0, size = data.getAllocatedSize(); index < size; ++index) {
138  std::vector<int> axis_inds = data.getAxesBinIndices(index);
139  index_mapping(axis_inds);
140  size_t output_index = output->toGlobalIndex(
141  {static_cast<unsigned>(axis_inds[0]), static_cast<unsigned>(axis_inds[1])});
142  (*output)[output_index] = data[index];
143  }
144  return output;
145 }
146 
147 std::unique_ptr<OutputData<double>>
149  double x2, double y2)
150 {
151  if (origin.getRank() != 2)
152  throw Exceptions::LogicErrorException("IntensityDataFunctions::createClippedData()"
153  " -> Error! Works only on two-dimensional data");
154 
155  std::unique_ptr<OutputData<double>> result(new OutputData<double>);
156  for (size_t i_axis = 0; i_axis < origin.getRank(); i_axis++) {
157  const IAxis& axis = origin.getAxis(i_axis);
158  IAxis* new_axis;
159  if (i_axis == 0)
160  new_axis = axis.createClippedAxis(x1, x2);
161  else
162  new_axis = axis.createClippedAxis(y1, y2);
163  result->addAxis(*new_axis);
164  delete new_axis;
165  }
166  result->setAllTo(0.0);
167 
168  OutputData<double>::const_iterator it_origin = origin.begin();
169  OutputData<double>::iterator it_result = result->begin();
170  while (it_origin != origin.end()) {
171  double x = origin.getAxisValue(it_origin.getIndex(), 0);
172  double y = origin.getAxisValue(it_origin.getIndex(), 1);
173  if (result->getAxis(0).contains(x) && result->getAxis(1).contains(y)) {
174  *it_result = *it_origin;
175  ++it_result;
176  }
177  ++it_origin;
178  }
179 
180  return result;
181 }
182 
183 // For axis FixedBinAxis("axis", 8, -5.0, 3.0) the coordinate x=-4.5 (center of bin #0) will
184 // be converted into 0.5 (which is a bin center expressed in bin fraction coordinates).
185 // The coordinate -5.0 (outside of axis definition) will be converted to -0.5
186 // (center of non-existing bin #-1).
187 // Used for Mask conversion.
188 
189 double IntensityDataFunctions::coordinateToBinf(double coordinate, const IAxis& axis)
190 {
191  size_t index = axis.findClosestIndex(coordinate);
192  Bin1D bin = axis.getBin(index);
193  double f = (coordinate - bin.m_lower) / bin.getBinSize();
194  return static_cast<double>(index) + f;
195 }
196 
197 double IntensityDataFunctions::coordinateFromBinf(double value, const IAxis& axis)
198 {
199  int index = static_cast<int>(value);
200 
201  double result(0);
202  if (index < 0) {
203  Bin1D bin = axis.getBin(0);
204  result = bin.m_lower + value * bin.getBinSize();
205  } else if (index >= static_cast<int>(axis.size())) {
206  Bin1D bin = axis.getBin(axis.size() - 1);
207  result = bin.m_upper + (value - axis.size()) * bin.getBinSize();
208  } else {
209  Bin1D bin = axis.getBin(static_cast<size_t>(index));
210  result = bin.m_lower + (value - static_cast<double>(index)) * bin.getBinSize();
211  }
212 
213  return result;
214 }
215 
216 void IntensityDataFunctions::coordinateToBinf(double& x, double& y, const OutputData<double>& data)
217 {
218  x = coordinateToBinf(x, data.getAxis(0));
219  y = coordinateToBinf(y, data.getAxis(1));
220 }
221 
223  const OutputData<double>& data)
224 {
225  x = coordinateFromBinf(x, data.getAxis(0));
226  y = coordinateFromBinf(y, data.getAxis(1));
227 }
228 
229 std::vector<std::vector<double>>
231 {
232  if (data.getRank() != 2)
234  "IntensityDataFunctions::create2DArrayfromOutputData() -> "
235  "Error! Works only on two-dimensional data");
236 
237  std::vector<std::vector<double>> array_2d;
238  std::vector<double> row_vec; // row vector for constructing each row of 2D array
239 
240  size_t nrows = data.getAxis(0).size();
241  size_t ncols = data.getAxis(1).size();
242 
243  size_t it = 0; // iterator of 'data'
244  for (size_t row = 0; row < nrows; row++) {
245  row_vec.clear();
246  for (size_t col = 0; col < ncols; col++) {
247  row_vec.push_back(data[it]);
248  it++;
249  }
250  array_2d.push_back(row_vec);
251  }
252 
253  return array_2d;
254 }
255 
256 std::vector<std::vector<double>>
257 IntensityDataFunctions::FT2DArray(const std::vector<std::vector<double>>& signal)
258 {
259  FourierTransform ft;
260  std::vector<std::vector<double>> fft_array;
261  ft.fft(signal, fft_array);
262  // shifting low frequency to center of array
263  ft.fftshift(fft_array);
264 
265  return fft_array;
266 }
267 
268 std::unique_ptr<OutputData<double>> IntensityDataFunctions::createOutputDatafrom2DArray(
269  const std::vector<std::vector<double>>& array_2d)
270 {
271  std::unique_ptr<OutputData<double>> result(new OutputData<double>);
272  size_t nrows = array_2d.size();
273  size_t ncols = array_2d[0].size();
274 
275  result->addAxis("x", nrows, 0.0, double(nrows));
276  result->addAxis("y", ncols, 0.0, double(ncols));
277  std::vector<unsigned> axes_indices(2);
278  for (unsigned row = 0; row < nrows; row++) {
279  for (unsigned col = 0; col < ncols; col++) {
280  axes_indices[0] = row;
281  axes_indices[1] = col;
282  size_t global_index = result->toGlobalIndex(axes_indices);
283  (*result)[global_index] = array_2d[row][col];
284  }
285  }
286 
287  return result;
288 }
289 
290 std::unique_ptr<OutputData<double>>
292 {
294  auto fft_array_2d = IntensityDataFunctions::FT2DArray(array_2d);
296 }
Defines various functions to interact from numpy on Python side.
Defines class ConvolutionDetectorResolution.
Defines class MathFunctions::FourierTransform.
Defines class IHistogram.
Defines class Instrument.
Defines class IntensityDataFunctions.
Defines constants and "almost equal" in namespace Numeric.
Defines class SimulationResult.
Fourier transform of vectors (in 1D or 2D) using Fast Fourier Transform (fftw package).
void fft(const double1d_t &source, double1d_t &result)
FT in 1D.
void fftshift(double1d_t &result)
Shift low frequency to the center of 1D FT array.
Interface for one-dimensional axes.
Definition: IAxis.h:25
virtual IAxis * createClippedAxis(double left, double right) const
Creates a new clipped axis.
Definition: IAxis.cpp:34
virtual size_t findClosestIndex(double value) const =0
find bin index which is best match for given value
virtual Bin1D getBin(size_t index) const =0
retrieve a 1d bin for the given index
virtual size_t size() const =0
retrieve the number of bins
Base class for 1D and 2D histograms holding values of double type.
Definition: IHistogram.h:27
const OutputData< CumulativeValue > & getData() const
Definition: IHistogram.h:92
OutputData< double > * meanValues() const
Definition: OutputData.h:279
iterator end()
Returns read/write iterator that points to the one past last element.
Definition: OutputData.h:96
std::vector< int > getAxesBinIndices(size_t global_index) const
Returns vector of axes indices for given global index.
Definition: OutputData.h:356
size_t getRank() const
Returns number of dimensions.
Definition: OutputData.h:59
const IAxis & getAxis(size_t serial_number) const
returns axis with given serial number
Definition: OutputData.h:314
bool hasSameDimensions(const OutputData< U > &right) const
Returns true if object have same dimensions and number of axes bins.
Definition: OutputData.h:578
iterator begin()
Returns read/write iterator that points to the first element.
Definition: OutputData.h:344
size_t getAllocatedSize() const
Returns total size of data buffer (product of bin number in every dimension).
Definition: OutputData.h:62
OutputData * clone() const
Definition: OutputData.h:253
double getAxisValue(size_t global_index, size_t i_selected_axis) const
Returns the value of selected axis for given global_index.
Definition: OutputData.h:433
Wrapper around OutputData<double> that also provides unit conversions.
bool empty() const
size_t size() const
double getRelativeDifference(const OutputData< double > &dat, const OutputData< double > &ref)
Returns relative difference between two data sets sum(dat[i] - ref[i])/ref[i]).
std::unique_ptr< OutputData< double > > createFFT(const OutputData< double > &data)
Creates Fourier Transform (OutputData format) of intensity map (OutputData format).
double RelativeDifference(const SimulationResult &dat, const SimulationResult &ref)
Returns sum of relative differences between each pair of elements: (a, b) -> 2*abs(a - b)/(a + b) ( a...
std::unique_ptr< OutputData< double > > createClippedDataSet(const OutputData< double > &origin, double x1, double y1, double x2, double y2)
Returns new IntensityData objects which axes clipped to represent the specified rectangle.
std::unique_ptr< OutputData< double > > createRelativeDifferenceData(const OutputData< double > &data, const OutputData< double > &reference)
double coordinateToBinf(double coordinate, const IAxis &axis)
Transforms coordinate on axis into the bin-fraction-coordinate.
std::unique_ptr< OutputData< double > > createRearrangedDataSet(const OutputData< double > &data, int n)
Returns new object with input data rotated by n*90 deg counterclockwise (n > 0) or clockwise (n < 0) ...
bool checkRelativeDifference(const OutputData< double > &dat, const OutputData< double > &ref, const double threshold)
Returns true is relative difference is below threshold; prints informative output.
std::vector< std::vector< double > > create2DArrayfromOutputData(const OutputData< double > &data)
Creates a vector of vectors of double (2D Array) from OutputData.
double coordinateFromBinf(double value, const IAxis &axis)
Transforms bin-fraction-coordinate into axis coordinate.
std::unique_ptr< OutputData< double > > createOutputDatafrom2DArray(const std::vector< std::vector< double >> &array_2d)
Creates OutputData from a 2D Array.
std::vector< std::vector< double > > FT2DArray(const std::vector< std::vector< double >> &signal)
Creates a Fourier Transform of a 2D Array (vector of vectors).
double GetRelativeDifference(double a, double b)
Returns the safe relative difference, which is 2(|a-b|)/(|a|+|b|) except in special cases.
Definition: Numeric.cpp:32
Definition: Bin.h:20
double m_upper
upper bound of the bin
Definition: Bin.h:24
double getBinSize() const
Definition: Bin.h:26
double m_lower
lower bound of the bin
Definition: Bin.h:23