BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
DataUtils.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Device/Data/DataUtils.cpp
6 //! @brief Implements namespace DataUtils.
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 
15 #include "Device/Data/DataUtils.h"
17 #include "Base/Math/Numeric.h"
18 #include "Device/Data/ArrayUtils.h"
19 #include <iostream>
20 
21 namespace {
22 
23 std::vector<std::vector<double>> FT2DArray(const std::vector<std::vector<double>>& signal)
24 {
26  std::vector<std::vector<double>> ret;
27  ft.fft(signal, ret);
28  ft.fftshift(ret); // low frequency to center of array
29  return ret;
30 }
31 
32 } // namespace
33 
34 //! Returns relative difference between two data sets sum(dat[i] - ref[i])/ref[i]).
36  const OutputData<double>& ref)
37 {
38  if (!dat.hasSameDimensions(ref))
39  throw std::runtime_error("OutputData dimension differs from reference");
40 
41  double diff = 0.0;
42  for (size_t i = 0; i < dat.getAllocatedSize(); ++i)
43  diff += Numeric::GetRelativeDifference(dat[i], ref[i]);
44  diff /= dat.getAllocatedSize();
45  if (std::isnan(diff))
46  throw std::runtime_error("diff=NaN!");
47  return diff;
48 }
49 
50 //! Returns true is relative difference is below threshold; prints informative output
52  const OutputData<double>& ref, const double threshold)
53 {
54  const double diff = relativeDataDifference(dat, ref);
55  if (diff > threshold) {
56  std::cerr << "FAILED: relative deviation of dat from ref is " << diff
57  << ", above given threshold " << threshold << std::endl;
58  return false;
59  }
60  if (diff)
61  std::cerr << "- OK: relative deviation of dat from ref is " << diff
62  << ", within given threshold " << threshold << std::endl;
63  else
64  std::cout << "- OK: dat = ref\n";
65  return true;
66 }
67 
68 std::unique_ptr<OutputData<double>>
70  const OutputData<double>& reference)
71 {
72  if (!data.hasSameDimensions(reference))
73  throw std::runtime_error("DataUtils::createRelativeDifferenceData() -> "
74  "Error. Different dimensions of data and reference.");
75  std::unique_ptr<OutputData<double>> result(reference.clone());
76  for (size_t i = 0; i < result->getAllocatedSize(); ++i)
77  (*result)[i] = Numeric::GetRelativeDifference(data[i], reference[i]);
78  return result;
79 }
80 
81 std::unique_ptr<OutputData<double>>
83 {
84  if (data.rank() != 2)
85  throw std::runtime_error("DataUtils::rotateDataByN90Deg()"
86  " -> Error! Works only on two-dimensional data");
87  n = (4 + n % 4) % 4;
88  if (n == 0)
89  return std::unique_ptr<OutputData<double>>(data.clone());
90  std::unique_ptr<OutputData<double>> output(new OutputData<double>());
91 
92  // swapping axes if necessary
93  const IAxis& x_axis = data.axis(0);
94  const IAxis& y_axis = data.axis(1);
95  output->addAxis(n == 2 ? x_axis : y_axis);
96  output->addAxis(n == 2 ? y_axis : x_axis);
97 
98  // creating index mapping
99  std::function<void(std::vector<int>&)> index_mapping;
100  if (n == 2) {
101  const int end_bin_x = static_cast<int>(x_axis.size()) - 1;
102  const int end_bin_y = static_cast<int>(y_axis.size()) - 1;
103  index_mapping = [end_bin_x, end_bin_y](std::vector<int>& inds) {
104  inds[0] = end_bin_x - inds[0];
105  inds[1] = end_bin_y - inds[1];
106  };
107  } else {
108  const size_t rev_axis_i = n % 3;
109  const size_t end_bin = data.axis(rev_axis_i).size() - 1;
110  index_mapping = [rev_axis_i, end_bin](std::vector<int>& inds) {
111  const int tm_index = inds[rev_axis_i];
112  inds[rev_axis_i] = inds[rev_axis_i ^ 1];
113  inds[rev_axis_i ^ 1] = static_cast<int>(end_bin) - tm_index;
114  };
115  }
116 
117  for (size_t index = 0, size = data.getAllocatedSize(); index < size; ++index) {
118  std::vector<int> axis_inds = data.getAxesBinIndices(index);
119  index_mapping(axis_inds);
120  size_t output_index = output->toGlobalIndex(
121  {static_cast<unsigned>(axis_inds[0]), static_cast<unsigned>(axis_inds[1])});
122  (*output)[output_index] = data[index];
123  }
124  return output;
125 }
126 
127 std::unique_ptr<OutputData<double>>
128 DataUtils::createClippedDataSet(const OutputData<double>& origin, double x1, double y1, double x2,
129  double y2)
130 {
131  if (origin.rank() != 2)
132  throw std::runtime_error("DataUtils::createClippedData()"
133  " -> Error! Works only on two-dimensional data");
134 
135  std::unique_ptr<OutputData<double>> result(new OutputData<double>);
136  for (size_t i_axis = 0; i_axis < origin.rank(); i_axis++) {
137  const IAxis& axis = origin.axis(i_axis);
138  IAxis* new_axis;
139  if (i_axis == 0)
140  new_axis = axis.createClippedAxis(x1, x2);
141  else
142  new_axis = axis.createClippedAxis(y1, y2);
143  result->addAxis(*new_axis);
144  delete new_axis;
145  }
146  result->setAllTo(0.0);
147 
148  OutputData<double>::const_iterator it_origin = origin.begin();
149  OutputData<double>::iterator it_result = result->begin();
150  while (it_origin != origin.end()) {
151  double x = origin.getAxisValue(it_origin.getIndex(), 0);
152  double y = origin.getAxisValue(it_origin.getIndex(), 1);
153  if (result->axis(0).contains(x) && result->axis(1).contains(y)) {
154  *it_result = *it_origin;
155  ++it_result;
156  }
157  ++it_origin;
158  }
159 
160  return result;
161 }
162 
163 // For axis FixedBinAxis("axis", 8, -5.0, 3.0) the coordinate x=-4.5 (center of bin #0) will
164 // be converted into 0.5 (which is a bin center expressed in bin fraction coordinates).
165 // The coordinate -5.0 (outside of axis definition) will be converted to -0.5
166 // (center of non-existing bin #-1).
167 // Used for Mask conversion.
168 
169 double DataUtils::coordinateToBinf(double coordinate, const IAxis& axis)
170 {
171  size_t index = axis.findClosestIndex(coordinate);
172  Bin1D bin = axis.bin(index);
173  double f = (coordinate - bin.m_lower) / bin.binSize();
174  return static_cast<double>(index) + f;
175 }
176 
177 double DataUtils::coordinateFromBinf(double value, const IAxis& axis)
178 {
179  int index = static_cast<int>(value);
180 
181  double result(0);
182  if (index < 0) {
183  Bin1D bin = axis.bin(0);
184  result = bin.m_lower + value * bin.binSize();
185  } else if (index >= static_cast<int>(axis.size())) {
186  Bin1D bin = axis.bin(axis.size() - 1);
187  result = bin.m_upper + (value - axis.size()) * bin.binSize();
188  } else {
189  Bin1D bin = axis.bin(static_cast<size_t>(index));
190  result = bin.m_lower + (value - static_cast<double>(index)) * bin.binSize();
191  }
192 
193  return result;
194 }
195 
196 void DataUtils::coordinateToBinf(double& x, double& y, const OutputData<double>& data)
197 {
198  x = coordinateToBinf(x, data.axis(0));
199  y = coordinateToBinf(y, data.axis(1));
200 }
201 
202 void DataUtils::coordinateFromBinf(double& x, double& y, const OutputData<double>& data)
203 {
204  x = coordinateFromBinf(x, data.axis(0));
205  y = coordinateFromBinf(y, data.axis(1));
206 }
207 
208 std::vector<std::vector<double>>
210 {
211  if (data.rank() != 2)
212  throw std::runtime_error("DataUtils::create2DArrayfromOutputData() -> "
213  "Error! Works only on two-dimensional data");
214 
215  std::vector<std::vector<double>> array_2d;
216  std::vector<double> row_vec; // row vector for constructing each row of 2D array
217 
218  size_t nrows = data.axis(0).size();
219  size_t ncols = data.axis(1).size();
220 
221  size_t it = 0; // iterator of 'data'
222  for (size_t row = 0; row < nrows; row++) {
223  row_vec.clear();
224  for (size_t col = 0; col < ncols; col++) {
225  row_vec.push_back(data[it]);
226  it++;
227  }
228  array_2d.push_back(row_vec);
229  }
230 
231  return array_2d;
232 }
233 
234 std::unique_ptr<OutputData<double>>
235 DataUtils::createOutputDatafrom2DArray(const std::vector<std::vector<double>>& array_2d)
236 {
237  std::unique_ptr<OutputData<double>> result(new OutputData<double>);
238  size_t nrows = array_2d.size();
239  size_t ncols = array_2d[0].size();
240 
241  result->addAxis("x", nrows, 0.0, double(nrows));
242  result->addAxis("y", ncols, 0.0, double(ncols));
243  std::vector<unsigned> axes_indices(2);
244  for (unsigned row = 0; row < nrows; row++) {
245  for (unsigned col = 0; col < ncols; col++) {
246  axes_indices[0] = row;
247  axes_indices[1] = col;
248  size_t global_index = result->toGlobalIndex(axes_indices);
249  (*result)[global_index] = array_2d[row][col];
250  }
251  }
252 
253  return result;
254 }
255 
256 std::unique_ptr<OutputData<double>> DataUtils::createFFT(const OutputData<double>& data)
257 {
258  auto array_2d = DataUtils::create2DArrayfromOutputData(data);
259  auto fft_array_2d = FT2DArray(array_2d);
260  return DataUtils::createOutputDatafrom2DArray(fft_array_2d);
261 }
262 
264 {
265  return ArrayUtils::createData(vec).release();
266 }
267 
268 OutputData<double>* DataUtils::importArrayToOutputData(const std::vector<std::vector<double>>& vec)
269 {
270  return ArrayUtils::createData(vec).release();
271 }
Defines various functions to interact from numpy on Python side.
Defines namespace DataUtils.
Defines class Math::FourierTransform.
Defines constants and "almost equal" in namespace Numeric.
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:32
virtual size_t findClosestIndex(double value) const =0
find bin index which is best match for given value
virtual Bin1D bin(size_t index) const =0
retrieve a 1d bin for the given index
virtual size_t size() const =0
retrieve the number of bins
iterator end()
Returns read/write iterator that points to the one past last element.
Definition: OutputData.h:93
size_t rank() const
Returns number of dimensions.
Definition: OutputData.h:56
const IAxis & axis(size_t serial_number) const
returns axis with given serial number
Definition: OutputData.h:318
std::vector< int > getAxesBinIndices(size_t global_index) const
Returns vector of axes indices for given global index.
Definition: OutputData.h:355
bool hasSameDimensions(const OutputData< U > &right) const
Returns true if object have same dimensions and number of axes bins.
Definition: OutputData.h:575
iterator begin()
Returns read/write iterator that points to the first element.
Definition: OutputData.h:343
size_t getAllocatedSize() const
Returns total size of data buffer (product of bin number in every dimension).
Definition: OutputData.h:59
OutputData * clone() const
Definition: OutputData.h:259
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:430
CreateDataImpl::ReturnType< T > createData(const T &vec)
Creates OutputData array from input vector.
Definition: ArrayUtils.h:65
double coordinateToBinf(double coordinate, const IAxis &axis)
Transforms coordinate on axis into the bin-fraction-coordinate.
Definition: DataUtils.cpp:169
bool checkRelativeDifference(const OutputData< double > &dat, const OutputData< double > &ref, const double threshold)
Returns true is relative difference is below threshold; prints informative output.
Definition: DataUtils.cpp:51
double relativeDataDifference(const OutputData< double > &dat, const OutputData< double > &ref)
Returns relative difference between two data sets sum(dat[i] - ref[i])/ref[i]).
Definition: DataUtils.cpp:35
std::unique_ptr< OutputData< double > > createRelativeDifferenceData(const OutputData< double > &data, const OutputData< double > &reference)
Definition: DataUtils.cpp:69
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.
Definition: DataUtils.cpp:128
std::unique_ptr< OutputData< double > > createFFT(const OutputData< double > &data)
Creates Fourier Transform (OutputData format) of intensity map (OutputData format).
Definition: DataUtils.cpp:256
std::vector< std::vector< double > > create2DArrayfromOutputData(const OutputData< double > &data)
Creates a vector of vectors of double (2D Array) from OutputData.
Definition: DataUtils.cpp:209
std::unique_ptr< OutputData< double > > createOutputDatafrom2DArray(const std::vector< std::vector< double >> &array_2d)
Creates OutputData from a 2D Array.
Definition: DataUtils.cpp:235
double coordinateFromBinf(double value, const IAxis &axis)
Transforms bin-fraction-coordinate into axis coordinate.
Definition: DataUtils.cpp:177
OutputData< double > * importArrayToOutputData(const std::vector< double > &vec)
Reads 1D array of doubles to Python, for use in persistence test.
Definition: DataUtils.cpp:263
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) ...
Definition: DataUtils.cpp:82
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:29
Definition: Bin.h:20
double binSize() const
Definition: Bin.h:26
double m_upper
upper bound of the bin
Definition: Bin.h:24
double m_lower
lower bound of the bin
Definition: Bin.h:23