BornAgain  1.19.79
Open-source research software to simulate and fit neutron and x-ray reflectometry and grazing-incidence small-angle scattering
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"
16 #include "Base/Axis/Bin.h"
17 #include "Base/Axis/FixedBinAxis.h"
18 #include "Base/Axis/Frame.h"
20 #include "Base/Util/Assert.h"
21 #include "Device/Data/ArrayUtils.h"
22 #include "Device/Data/Datafield.h"
23 #include <algorithm>
24 #include <cmath>
25 #include <functional>
26 #include <tspectrum.h> // third-party code, extracted from CERN ROOT (class TSpectrum2)
27 
28 namespace {
29 
30 std::vector<std::vector<double>> FT2DArray(const std::vector<std::vector<double>>& signal)
31 {
33  std::vector<std::vector<double>> result;
34  ft.fft(signal, result);
35  ft.fftshift(result); // low frequency to center of array
36  return result;
37 }
38 
39 } // namespace
40 
41 std::unique_ptr<Datafield> DataUtils::Data::createRearrangedDataSet(const Datafield& data, int n)
42 {
43  if (data.rank() != 2)
44  throw std::runtime_error("DataUtils::Data::rotateDataByN90Deg()"
45  " -> Error! Works only on two-dimensional data");
46  n = (4 + n % 4) % 4;
47  if (n == 0)
48  return std::unique_ptr<Datafield>(data.clone());
49 
50  std::unique_ptr<Datafield> output;
51  std::function<void(std::vector<int>&)> index_mapping;
52 
53  if (n == 2) {
54  output.reset(new Datafield({data.axis(0).clone(), data.axis(1).clone()}));
55  const int end_bin_x = static_cast<int>(data.axis(0).size()) - 1;
56  const int end_bin_y = static_cast<int>(data.axis(1).size()) - 1;
57  index_mapping = [end_bin_x, end_bin_y](std::vector<int>& inds) {
58  inds[0] = end_bin_x - inds[0];
59  inds[1] = end_bin_y - inds[1];
60  };
61 
62  } else {
63  output.reset(new Datafield({data.axis(1).clone(), data.axis(0).clone()}));
64  const size_t rev_axis_i = n % 3;
65  const size_t end_bin = data.axis(rev_axis_i).size() - 1;
66  index_mapping = [rev_axis_i, end_bin](std::vector<int>& inds) {
67  const int tm_index = inds[rev_axis_i];
68  inds[rev_axis_i] = inds[rev_axis_i ^ 1];
69  inds[rev_axis_i ^ 1] = static_cast<int>(end_bin) - tm_index;
70  };
71  }
72 
73  for (size_t i = 0, size = data.size(); i < size; ++i) {
74  std::vector<int> axis_inds = data.frame().allIndices(i);
75  index_mapping(axis_inds);
76  size_t iout = output->frame().toGlobalIndex(
77  {static_cast<unsigned>(axis_inds[0]), static_cast<unsigned>(axis_inds[1])});
78  (*output)[iout] = data[i];
79  }
80  return output;
81 }
82 
83 std::vector<std::vector<double>> DataUtils::Data::create2DArrayfromDatafield(const Datafield& data)
84 {
85  if (data.rank() != 2)
86  throw std::runtime_error("DataUtils::Data::create2DArrayfromDatafield() -> "
87  "Error! Works only on two-dimensional data");
88 
89  std::vector<std::vector<double>> array_2d;
90  std::vector<double> row_vec; // row vector for constructing each row of 2D array
91 
92  size_t nrows = data.axis(0).size();
93  size_t ncols = data.axis(1).size();
94 
95  size_t it = 0; // iterator of 'data'
96  for (size_t row = 0; row < nrows; row++) {
97  row_vec.clear();
98  for (size_t col = 0; col < ncols; col++) {
99  row_vec.push_back(data[it]);
100  it++;
101  }
102  array_2d.push_back(row_vec);
103  }
104 
105  return array_2d;
106 }
107 
108 std::unique_ptr<Datafield>
109 DataUtils::Data::vecvecToDatafield(const std::vector<std::vector<double>>& array_2d)
110 {
111  size_t nrows = array_2d.size();
112  size_t ncols = array_2d[0].size();
113 
114  auto frame = new Frame(
115  {new FixedBinAxis("x", nrows, 0.0, double(nrows)),
116  new FixedBinAxis("y", ncols, 0.0, double(ncols))});
117  std::vector<double> out(frame->size());
118  std::vector<unsigned> axes_indices(2);
119  for (unsigned row = 0; row < nrows; row++) {
120  for (unsigned col = 0; col < ncols; col++) {
121  axes_indices[0] = row;
122  axes_indices[1] = col;
123  size_t iout = frame->toGlobalIndex(axes_indices);
124  out[iout] = array_2d[row][col];
125  }
126  }
127  return std::make_unique<Datafield>(frame, out);
128 }
129 
130 std::unique_ptr<Datafield> DataUtils::Data::createFFT(const Datafield& data)
131 {
132  auto array_2d = DataUtils::Data::create2DArrayfromDatafield(data);
133  auto fft_array_2d = FT2DArray(array_2d);
134  return DataUtils::Data::vecvecToDatafield(fft_array_2d);
135 }
136 
137 Datafield* DataUtils::Data::importArrayToDatafield(const std::vector<double>& vec)
138 {
139  return DataUtils::Array::createPField1D(vec).release();
140 }
141 
142 Datafield* DataUtils::Data::importArrayToDatafield(const std::vector<std::vector<double>>& vec)
143 {
144  return DataUtils::Array::createPField2D(vec).release();
145 }
146 
147 std::vector<std::pair<double, double>> DataUtils::Data::FindPeaks(const Datafield& field,
148  double sigma,
149  const std::string& option,
150  double threshold)
151 {
152  std::vector<std::vector<double>> arr = DataUtils::Array::createVector2D(field);
153  tspectrum::Spectrum2D spec;
154  auto peaks = spec.find_peaks(arr, sigma, option, threshold);
155 
156  // coordinates of peaks in histogram axes units
157  std::vector<std::pair<double, double>> result;
158 
159  for (const auto& p : peaks) {
160  double row_value = p.first;
161  double col_value = p.second;
162 
163  auto xaxis_index = static_cast<size_t>(col_value);
164  size_t yaxis_index = field.yAxis().size() - 1 - static_cast<size_t>(row_value);
165 
166  Bin1D xbin = field.xAxis().bin(xaxis_index);
167  Bin1D ybin = field.yAxis().bin(yaxis_index);
168 
169  double dx = col_value - static_cast<size_t>(col_value);
170  double dy = -1.0 * (row_value - static_cast<size_t>(row_value));
171 
172  double x = xbin.center() + xbin.binSize() * dx;
173  double y = ybin.center() + ybin.binSize() * dy;
174 
175  result.emplace_back(x, y);
176  }
177  return result;
178 }
Defines various functions to interact from numpy on Python side.
Defines the macro ASSERT.
Defines structs Bin1D, Bin1DCVector.
Defines namespace DataUtils.
Defines and implements templated class Datafield.
Defines class FixedBinAxis.
Defines class Math::FourierTransform.
Defines and implements templated class Frame.
Definition: Bin.h:20
double center() const
Definition: Bin.h:30
double binSize() const
Definition: Bin.h:31
Stores radiation power per bin.
Definition: Datafield.h:30
const IAxis & axis(size_t k) const
Definition: Datafield.cpp:91
Datafield * clone() const
Definition: Datafield.cpp:48
size_t rank() const
Definition: Datafield.cpp:75
const IAxis & yAxis() const
Definition: Datafield.cpp:101
size_t size() const
Returns total size of data buffer (product of bin number in every dimension).
Definition: Datafield.cpp:80
const IAxis & xAxis() const
Definition: Datafield.cpp:96
const Frame & frame() const
Definition: Datafield.cpp:86
Axis with fixed bin size.
Definition: FixedBinAxis.h:23
Fourier transform of vectors (in 1D or 2D) using Fast Fourier Transform (fftw package).
void fftshift(double1d_t &result) const
Shift low frequency to the center of 1D FT array.
void fft(const double1d_t &source, double1d_t &result)
FT in 1D.
Holds one or two axes.
Definition: Frame.h:27
std::vector< int > allIndices(size_t i_flat) const
Returns vector of axes indices for given global index.
Definition: Frame.cpp:48
virtual IAxis * clone() const =0
virtual Bin1D bin(size_t index) const =0
retrieve a 1d bin for the given index
virtual size_t size() const =0
Returns the number of bins.
std::vector< std::vector< double > > createVector2D(const Datafield &data)
Creates 2D vector from Datafield.
Definition: ArrayUtils.cpp:73
std::unique_ptr< Datafield > createPField1D(const std::vector< double > &vec)
Definition: ArrayUtils.cpp:32
std::unique_ptr< Datafield > createPField2D(const std::vector< std::vector< double >> &vec)
Definition: ArrayUtils.cpp:40
Datafield * importArrayToDatafield(const std::vector< double > &vec)
Reads 1D array of doubles to Python, for use in persistence test.
Definition: DataUtils.cpp:137
std::unique_ptr< Datafield > vecvecToDatafield(const std::vector< std::vector< double >> &array_2d)
Creates Datafield from a 2D Array.
Definition: DataUtils.cpp:109
std::unique_ptr< Datafield > createRearrangedDataSet(const Datafield &data, int n)
Returns new object with input data rotated by n*90 deg counterclockwise (n > 0) or clockwise (n < 0) ...
Definition: DataUtils.cpp:41
std::vector< std::pair< double, double > > FindPeaks(const Datafield &hist, double sigma=2, const std::string &option={}, double threshold=0.05)
Returns vector of peak center coordinates, for peaks in given histogram.
Definition: DataUtils.cpp:147
std::unique_ptr< Datafield > createFFT(const Datafield &data)
Creates Fourier Transform (Datafield format) of intensity map (Datafield format).
Definition: DataUtils.cpp:130
std::vector< std::vector< double > > create2DArrayfromDatafield(const Datafield &data)
Creates a vector of vectors of double (2D Array) from Datafield.
Definition: DataUtils.cpp:83