BornAgain  1.19.79
Open-source research software to simulate and fit neutron and x-ray reflectometry and grazing-incidence small-angle scattering
ConvolutionDetectorResolution.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Device/Resolution/ConvolutionDetectorResolution.cpp
6 //! @brief Implements class ConvolutionDetectorResolution.
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/FixedBinAxis.h"
17 #include "Base/Axis/Frame.h"
18 #include "Base/Util/Assert.h"
20 #include <stdexcept>
21 
23  : m_rank(1)
24  , m_res_function_1d(res_function_1d)
25 {
26 }
27 
29  const IResolutionFunction2D& p_res_function_2d)
30  : m_rank(2)
31  , m_res_function_1d(nullptr)
32 {
33  setResolutionFunction(p_res_function_2d);
34 }
35 
37 
39  const ConvolutionDetectorResolution& other)
40 {
41  m_rank = other.m_rank;
43  if (other.m_res_function_2d)
45 }
46 
48 {
49  return new ConvolutionDetectorResolution(*this);
50 }
51 
52 std::vector<const INode*> ConvolutionDetectorResolution::nodeChildren() const
53 {
54  return std::vector<const INode*>() << m_res_function_2d;
55 }
56 
58 {
59  if (p_intensity_map->rank() != m_rank) {
60  throw std::runtime_error(
61  "ConvolutionDetectorResolution::applyDetectorResolution() -> Error! "
62  "Intensity map must have same dimension as detector resolution function.");
63  }
64  switch (m_rank) {
65  case 1:
66  apply1dConvolution(p_intensity_map);
67  break;
68  case 2:
69  apply2dConvolution(p_intensity_map);
70  break;
71  default:
72  throw std::runtime_error(
73  "ConvolutionDetectorResolution::applyDetectorResolution() -> Error! "
74  "Class ConvolutionDetectorResolution must be initialized with dimension 1 or 2.");
75  }
76 }
77 
79 {
80  m_res_function_2d.reset(resFunc.clone());
81 }
82 
84 {
85  ASSERT(m_res_function_1d == nullptr);
86  if (p_intensity_map->rank() != 1)
87  throw std::runtime_error(
88  "ConvolutionDetectorResolution::apply1dConvolution() -> Error! "
89  "Number of axes for intensity map does not correspond to the dimension of the map.");
90  const IAxis& axis = p_intensity_map->axis(0);
91  // Construct source vector from original intensity map
92  std::vector<double> source_vector = p_intensity_map->flatVector();
93  size_t data_size = source_vector.size();
94  if (data_size < 2)
95  return; // No convolution for sets of zero or one element
96  // Construct kernel vector from resolution function
97  if (axis.size() != data_size)
98  throw std::runtime_error(
99  "ConvolutionDetectorResolution::apply1dConvolution() -> Error! "
100  "Size of axis for intensity map does not correspond to size of data in the map.");
101  double step_size = std::abs(axis[0] - axis[axis.size() - 1]) / (data_size - 1);
102  double mid_value = axis[axis.size() / 2]; // because Convolve expects zero at midpoint
103  std::vector<double> kernel;
104  for (size_t index = 0; index < data_size; ++index)
105  kernel.push_back(getIntegratedPDF1d(axis[index] - mid_value, step_size));
106  // Calculate convolution
107  std::vector<double> result;
108  Convolve().fftconvolve(source_vector, kernel, result);
109  // Truncate negative values that can arise because of finite precision of Fourier Transform
110  for (double& e : result)
111  e = std::max(0.0, e);
112  // Populate intensity map with results
113  p_intensity_map->setVector(result);
114 }
115 
117 {
119  if (p_intensity_map->rank() != 2)
120  throw std::runtime_error(
121  "ConvolutionDetectorResolution::apply2dConvolution() -> Error! "
122  "Number of axes for intensity map does not correspond to the dimension of the map.");
123  const IAxis& axis_1 = p_intensity_map->axis(0);
124  const IAxis& axis_2 = p_intensity_map->axis(1);
125  size_t axis_size_1 = axis_1.size();
126  size_t axis_size_2 = axis_2.size();
127  if (axis_size_1 < 2 || axis_size_2 < 2)
128  return; // No 2d convolution for 1d data
129  // Construct source vector array from original intensity map
130  std::vector<double> raw_source_vector = p_intensity_map->flatVector();
131  std::vector<std::vector<double>> source;
132  size_t raw_data_size = raw_source_vector.size();
133  if (raw_data_size != axis_size_1 * axis_size_2)
134  throw std::runtime_error(
135  "ConvolutionDetectorResolution::apply2dConvolution() -> Error! "
136  "Intensity map data size does not match the product of its axes' sizes");
137  for (auto it = raw_source_vector.begin(); it != raw_source_vector.end(); it += axis_size_2) {
138  std::vector<double> row_vector(it, it + axis_size_2);
139  source.push_back(row_vector);
140  }
141  // Construct kernel vector from resolution function
142  std::vector<std::vector<double>> kernel;
143  kernel.resize(axis_size_1);
144  double mid_value_1 = axis_1[axis_size_1 / 2]; // because Convolve expects zero at midpoint
145  double mid_value_2 = axis_2[axis_size_2 / 2]; // because Convolve expects zero at midpoint
146  double step_size_1 = std::abs(axis_1[0] - axis_1[axis_size_1 - 1]) / (axis_size_1 - 1);
147  double step_size_2 = std::abs(axis_2[0] - axis_2[axis_size_2 - 1]) / (axis_size_2 - 1);
148  for (size_t index_1 = 0; index_1 < axis_size_1; ++index_1) {
149  double value_1 = axis_1[index_1] - mid_value_1;
150  std::vector<double> row_vector;
151  row_vector.resize(axis_size_2, 0.0);
152  for (size_t index_2 = 0; index_2 < axis_size_2; ++index_2) {
153  double value_2 = axis_2[index_2] - mid_value_2;
154  double z_value = getIntegratedPDF2d(value_1, step_size_1, value_2, step_size_2);
155  row_vector[index_2] = z_value;
156  }
157  kernel[index_1] = row_vector;
158  }
159  // Calculate convolution
160  std::vector<std::vector<double>> result;
161  Convolve().fftconvolve(source, kernel, result);
162  // Populate intensity map with results
163  std::vector<double> result_vector;
164  for (size_t index_1 = 0; index_1 < axis_size_1; ++index_1) {
165  for (size_t index_2 = 0; index_2 < axis_size_2; ++index_2) {
166  double value = result[index_1][index_2];
167  result_vector.push_back(value);
168  }
169  }
170  ASSERT(axis_size_1 * axis_size_2 == p_intensity_map->size());
171  for (size_t i = 0; i < p_intensity_map->size(); ++i) {
172  size_t i0 = p_intensity_map->frame().projectedIndex(i, 0);
173  size_t i1 = p_intensity_map->frame().projectedIndex(i, 1);
174  (*p_intensity_map)[i] = std::max(0.0, result[i0][i1]);
175  }
176 }
177 
178 double ConvolutionDetectorResolution::getIntegratedPDF1d(double x, double step) const
179 {
180  double halfstep = step / 2.0;
181  double xmin = x - halfstep;
182  double xmax = x + halfstep;
183  ASSERT(m_res_function_1d != nullptr);
184  return m_res_function_1d(xmax) - m_res_function_1d(xmin);
185 }
186 
187 double ConvolutionDetectorResolution::getIntegratedPDF2d(double x, double step_x, double y,
188  double step_y) const
189 {
190  double halfstepx = step_x / 2.0;
191  double halfstepy = step_y / 2.0;
192  double xmin = x - halfstepx;
193  double xmax = x + halfstepx;
194  double ymin = y - halfstepy;
195  double ymax = y + halfstepy;
196  double result =
197  m_res_function_2d->evaluateCDF(xmax, ymax) - m_res_function_2d->evaluateCDF(xmax, ymin)
198  - m_res_function_2d->evaluateCDF(xmin, ymax) + m_res_function_2d->evaluateCDF(xmin, ymin);
199  return result;
200 }
Defines the macro ASSERT.
#define ASSERT(condition)
Definition: Assert.h:45
Defines class ConvolutionDetectorResolution.
Defines class Math::Convolve.
Defines class FixedBinAxis.
Defines and implements templated class Frame.
Convolutes the intensity in 1 or 2 dimensions with a resolution function.
double getIntegratedPDF1d(double x, double step) const
void setResolutionFunction(const IResolutionFunction2D &resFunc)
std::vector< const INode * > nodeChildren() const override
Returns all children.
~ConvolutionDetectorResolution() override
std::unique_ptr< IResolutionFunction2D > m_res_function_2d
ConvolutionDetectorResolution(cumulative_DF_1d res_function_1d)
Constructor taking a 1 dimensional resolution function as argument.
void apply1dConvolution(Datafield *p_intensity_map) const
void applyDetectorResolution(Datafield *p_intensity_map) const override
Convolve given intensities with the encapsulated resolution.
ConvolutionDetectorResolution * clone() const override
void apply2dConvolution(Datafield *p_intensity_map) const
double getIntegratedPDF2d(double x, double step_x, double y, double step_y) const
Convolution of two real vectors (in 1D or 2D) using Fast Fourier Transform.
Definition: Convolve.h:36
void fftconvolve(const double1d_t &source, const double1d_t &kernel, double1d_t &result)
convolution in 1D
Definition: Convolve.cpp:115
Stores radiation power per bin.
Definition: Datafield.h:30
const IAxis & axis(size_t k) const
Definition: Datafield.cpp:91
void setVector(const std::vector< double > &data_vector)
Sets new values to raw data vector.
Definition: Datafield.cpp:69
std::vector< double > flatVector() const
Returns copy of raw data vector.
Definition: Datafield.cpp:119
size_t rank() const
Definition: Datafield.cpp:75
size_t size() const
Returns total size of data buffer (product of bin number in every dimension).
Definition: Datafield.cpp:80
const Frame & frame() const
Definition: Datafield.cpp:86
size_t projectedIndex(size_t i_flat, size_t k_axis) const
Returns axis bin index for given global index.
Definition: Frame.cpp:56
Abstract base class for one-dimensional axes.
Definition: IAxis.h:27
virtual size_t size() const =0
Returns the number of bins.
Interface providing two-dimensional resolution function.
IResolutionFunction2D * clone() const override=0