BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
ConvolutionDetectorResolution.cpp
Go to the documentation of this file.
1 // ************************************************************************** //
2 //
3 // BornAgain: simulate and fit scattering at grazing incidence
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 
17 
19  : m_dimension(1), m_res_function_1d(res_function_1d)
20 {
21  setName("ConvolutionDetectorResolution");
22 }
23 
25  const IResolutionFunction2D& p_res_function_2d)
26  : m_dimension(2), m_res_function_1d(0)
27 {
28  setName("ConvolutionDetectorResolution");
29  setResolutionFunction(p_res_function_2d);
30 }
31 
33 
35  const ConvolutionDetectorResolution& other)
36 {
37  m_dimension = other.m_dimension;
39  if (other.mp_res_function_2d)
41  setName(other.getName());
42 }
43 
45 {
46  return new ConvolutionDetectorResolution(*this);
47 }
48 
49 std::vector<const INode*> ConvolutionDetectorResolution::getChildren() const
50 {
51  return std::vector<const INode*>() << mp_res_function_2d;
52 }
53 
55  OutputData<double>* p_intensity_map) const
56 {
57  if (p_intensity_map->getRank() != m_dimension) {
59  "ConvolutionDetectorResolution::applyDetectorResolution() -> Error! "
60  "Intensity map must have same dimension as detector resolution function.");
61  }
62  switch (m_dimension) {
63  case 1:
64  apply1dConvolution(p_intensity_map);
65  break;
66  case 2:
67  apply2dConvolution(p_intensity_map);
68  break;
69  default:
71  "ConvolutionDetectorResolution::applyDetectorResolution() -> Error! "
72  "Class ConvolutionDetectorResolution must be initialized with dimension 1 or 2.");
73  }
74 }
75 
77 {
78  mp_res_function_2d.reset(resFunc.clone());
80 }
81 
83 {
84  if (m_res_function_1d == 0)
86  "ConvolutionDetectorResolution::apply1dConvolution() -> Error! "
87  "No 1d resolution function present for convolution of 1d data.");
88  if (p_intensity_map->getRank() != 1)
90  "ConvolutionDetectorResolution::apply1dConvolution() -> Error! "
91  "Number of axes for intensity map does not correspond to the dimension of the map.");
92  const IAxis& axis = p_intensity_map->getAxis(0);
93  // Construct source vector from original intensity map
94  std::vector<double> source_vector = p_intensity_map->getRawDataVector();
95  size_t data_size = source_vector.size();
96  if (data_size < 2)
97  return; // No convolution for sets of zero or one element
98  // Construct kernel vector from resolution function
99  if (axis.size() != data_size)
101  "ConvolutionDetectorResolution::apply1dConvolution() -> Error! "
102  "Size of axis for intensity map does not correspond to size of data in the map.");
103  double step_size = std::abs(axis[0] - axis[axis.size() - 1]) / (data_size - 1);
104  double mid_value = axis[axis.size() / 2]; // because Convolve expects zero at midpoint
105  std::vector<double> kernel;
106  for (size_t index = 0; index < data_size; ++index) {
107  kernel.push_back(getIntegratedPDF1d(axis[index] - mid_value, step_size));
108  }
109  // Calculate convolution
110  std::vector<double> result;
111  Convolve().fftconvolve(source_vector, kernel, result);
112  // Truncate negative values that can arise because of finite precision of Fourier Transform
113  std::for_each(result.begin(), result.end(), [](double& val) { val = std::max(0.0, val); });
114  // Populate intensity map with results
115  p_intensity_map->setRawDataVector(result);
116 }
117 
119 {
120  if (mp_res_function_2d == 0)
122  "ConvolutionDetectorResolution::apply2dConvolution() -> Error! "
123  "No 2d resolution function present for convolution of 2d data.");
124  if (p_intensity_map->getRank() != 2)
126  "ConvolutionDetectorResolution::apply2dConvolution() -> Error! "
127  "Number of axes for intensity map does not correspond to the dimension of the map.");
128  const IAxis& axis_1 = p_intensity_map->getAxis(0);
129  const IAxis& axis_2 = p_intensity_map->getAxis(1);
130  size_t axis_size_1 = axis_1.size();
131  size_t axis_size_2 = axis_2.size();
132  if (axis_size_1 < 2 || axis_size_2 < 2)
133  return; // No 2d convolution for 1d data
134  // Construct source vector array from original intensity map
135  std::vector<double> raw_source_vector = p_intensity_map->getRawDataVector();
136  std::vector<std::vector<double>> source;
137  size_t raw_data_size = raw_source_vector.size();
138  if (raw_data_size != axis_size_1 * axis_size_2)
140  "ConvolutionDetectorResolution::apply2dConvolution() -> Error! "
141  "Intensity map data size does not match the product of its axes' sizes");
142  for (auto it = raw_source_vector.begin(); it != raw_source_vector.end(); it += axis_size_2) {
143  std::vector<double> row_vector(it, it + axis_size_2);
144  source.push_back(row_vector);
145  }
146  // Construct kernel vector from resolution function
147  std::vector<std::vector<double>> kernel;
148  kernel.resize(axis_size_1);
149  double mid_value_1 = axis_1[axis_size_1 / 2]; // because Convolve expects zero at midpoint
150  double mid_value_2 = axis_2[axis_size_2 / 2]; // because Convolve expects zero at midpoint
151  double step_size_1 = std::abs(axis_1[0] - axis_1[axis_size_1 - 1]) / (axis_size_1 - 1);
152  double step_size_2 = std::abs(axis_2[0] - axis_2[axis_size_2 - 1]) / (axis_size_2 - 1);
153  for (size_t index_1 = 0; index_1 < axis_size_1; ++index_1) {
154  double value_1 = axis_1[index_1] - mid_value_1;
155  std::vector<double> row_vector;
156  row_vector.resize(axis_size_2, 0.0);
157  for (size_t index_2 = 0; index_2 < axis_size_2; ++index_2) {
158  double value_2 = axis_2[index_2] - mid_value_2;
159  double z_value = getIntegratedPDF2d(value_1, step_size_1, value_2, step_size_2);
160  row_vector[index_2] = z_value;
161  }
162  kernel[index_1] = row_vector;
163  }
164  // Calculate convolution
165  std::vector<std::vector<double>> result;
166  Convolve().fftconvolve(source, kernel, result);
167  // Populate intensity map with results
168  std::vector<double> result_vector;
169  for (size_t index_1 = 0; index_1 < axis_size_1; ++index_1) {
170  for (size_t index_2 = 0; index_2 < axis_size_2; ++index_2) {
171  double value = result[index_1][index_2];
172  result_vector.push_back(value);
173  }
174  }
175  // Truncate negative values that can arise because of finite precision of Fourier Transform
176  std::for_each(result_vector.begin(), result_vector.end(),
177  [](double& val) { val = std::max(0.0, val); });
178  for (auto it = p_intensity_map->begin(); it != p_intensity_map->end(); ++it)
179  (*it) = result_vector[it.getIndex()];
180 }
181 
182 double ConvolutionDetectorResolution::getIntegratedPDF1d(double x, double step) const
183 {
184  double halfstep = step / 2.0;
185  double xmin = x - halfstep;
186  double xmax = x + halfstep;
187  ASSERT(m_res_function_1d != nullptr);
188  return m_res_function_1d(xmax) - m_res_function_1d(xmin);
189 }
190 
191 double ConvolutionDetectorResolution::getIntegratedPDF2d(double x, double step_x, double y,
192  double step_y) const
193 {
194  double halfstepx = step_x / 2.0;
195  double halfstepy = step_y / 2.0;
196  double xmin = x - halfstepx;
197  double xmax = x + halfstepx;
198  double ymin = y - halfstepy;
199  double ymax = y + halfstepy;
200  double result =
201  mp_res_function_2d->evaluateCDF(xmax, ymax) - mp_res_function_2d->evaluateCDF(xmax, ymin)
202  - mp_res_function_2d->evaluateCDF(xmin, ymax) + mp_res_function_2d->evaluateCDF(xmin, ymin);
203  return result;
204 }
#define ASSERT(condition)
Definition: Assert.h:26
Defines class ConvolutionDetectorResolution.
Defines class MathFunctions::Convolve.
Convolutes the intensity in 1 or 2 dimensions with a resolution function.
double getIntegratedPDF1d(double x, double step) const
void setResolutionFunction(const IResolutionFunction2D &resFunc)
void apply1dConvolution(OutputData< double > *p_intensity_map) const
std::unique_ptr< IResolutionFunction2D > mp_res_function_2d
void apply2dConvolution(OutputData< double > *p_intensity_map) const
ConvolutionDetectorResolution(cumulative_DF_1d res_function_1d)
Constructor taking a 1 dimensional resolution function as argument.
virtual ConvolutionDetectorResolution * clone() const
virtual void applyDetectorResolution(OutputData< double > *p_intensity_map) const
Convolve given intensities with the encapsulated resolution.
std::vector< const INode * > getChildren() const
Returns a vector of children (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:34
void fftconvolve(const double1d_t &source, const double1d_t &kernel, double1d_t &result)
convolution in 1D
Definition: Convolve.cpp:123
Interface for one-dimensional axes.
Definition: IAxis.h:25
virtual size_t size() const =0
retrieve the number of bins
void registerChild(INode *node)
Definition: INode.cpp:58
const std::string & getName() const
void setName(const std::string &name)
Interface providing two-dimensional resolution function.
virtual IResolutionFunction2D * clone() const =0
iterator end()
Returns read/write iterator that points to the one past last element.
Definition: OutputData.h:96
std::vector< T > getRawDataVector() const
Returns copy of raw data vector.
Definition: OutputData.h:335
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
void setRawDataVector(const std::vector< T > &data_vector)
Sets new values to raw data vector.
Definition: OutputData.h:559
iterator begin()
Returns read/write iterator that points to the first element.
Definition: OutputData.h:344