BornAgain  1.19.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 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 
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.m_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*>() << m_res_function_2d;
52 }
53 
55  OutputData<double>* p_intensity_map) const
56 {
57  if (p_intensity_map->rank() != m_dimension) {
58  throw std::runtime_error(
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:
70  throw std::runtime_error(
71  "ConvolutionDetectorResolution::applyDetectorResolution() -> Error! "
72  "Class ConvolutionDetectorResolution must be initialized with dimension 1 or 2.");
73  }
74 }
75 
77 {
78  m_res_function_2d.reset(resFunc.clone());
80 }
81 
83 {
84  if (m_res_function_1d == 0)
85  throw std::runtime_error("ConvolutionDetectorResolution::apply1dConvolution() -> Error! "
86  "No 1d resolution function present for convolution of 1d data.");
87  if (p_intensity_map->rank() != 1)
88  throw std::runtime_error(
89  "ConvolutionDetectorResolution::apply1dConvolution() -> Error! "
90  "Number of axes for intensity map does not correspond to the dimension of the map.");
91  const IAxis& axis = p_intensity_map->axis(0);
92  // Construct source vector from original intensity map
93  std::vector<double> source_vector = p_intensity_map->getRawDataVector();
94  size_t data_size = source_vector.size();
95  if (data_size < 2)
96  return; // No convolution for sets of zero or one element
97  // Construct kernel vector from resolution function
98  if (axis.size() != data_size)
99  throw std::runtime_error(
100  "ConvolutionDetectorResolution::apply1dConvolution() -> Error! "
101  "Size of axis for intensity map does not correspond to size of data in the map.");
102  double step_size = std::abs(axis[0] - axis[axis.size() - 1]) / (data_size - 1);
103  double mid_value = axis[axis.size() / 2]; // because Convolve expects zero at midpoint
104  std::vector<double> kernel;
105  for (size_t index = 0; index < data_size; ++index) {
106  kernel.push_back(getIntegratedPDF1d(axis[index] - mid_value, step_size));
107  }
108  // Calculate convolution
109  std::vector<double> result;
110  Convolve().fftconvolve(source_vector, kernel, result);
111  // Truncate negative values that can arise because of finite precision of Fourier Transform
112  std::for_each(result.begin(), result.end(), [](double& val) { val = std::max(0.0, val); });
113  // Populate intensity map with results
114  p_intensity_map->setRawDataVector(result);
115 }
116 
118 {
119  if (m_res_function_2d == 0)
120  throw std::runtime_error("ConvolutionDetectorResolution::apply2dConvolution() -> Error! "
121  "No 2d resolution function present for convolution of 2d data.");
122  if (p_intensity_map->rank() != 2)
123  throw std::runtime_error(
124  "ConvolutionDetectorResolution::apply2dConvolution() -> Error! "
125  "Number of axes for intensity map does not correspond to the dimension of the map.");
126  const IAxis& axis_1 = p_intensity_map->axis(0);
127  const IAxis& axis_2 = p_intensity_map->axis(1);
128  size_t axis_size_1 = axis_1.size();
129  size_t axis_size_2 = axis_2.size();
130  if (axis_size_1 < 2 || axis_size_2 < 2)
131  return; // No 2d convolution for 1d data
132  // Construct source vector array from original intensity map
133  std::vector<double> raw_source_vector = p_intensity_map->getRawDataVector();
134  std::vector<std::vector<double>> source;
135  size_t raw_data_size = raw_source_vector.size();
136  if (raw_data_size != axis_size_1 * axis_size_2)
137  throw std::runtime_error(
138  "ConvolutionDetectorResolution::apply2dConvolution() -> Error! "
139  "Intensity map data size does not match the product of its axes' sizes");
140  for (auto it = raw_source_vector.begin(); it != raw_source_vector.end(); it += axis_size_2) {
141  std::vector<double> row_vector(it, it + axis_size_2);
142  source.push_back(row_vector);
143  }
144  // Construct kernel vector from resolution function
145  std::vector<std::vector<double>> kernel;
146  kernel.resize(axis_size_1);
147  double mid_value_1 = axis_1[axis_size_1 / 2]; // because Convolve expects zero at midpoint
148  double mid_value_2 = axis_2[axis_size_2 / 2]; // because Convolve expects zero at midpoint
149  double step_size_1 = std::abs(axis_1[0] - axis_1[axis_size_1 - 1]) / (axis_size_1 - 1);
150  double step_size_2 = std::abs(axis_2[0] - axis_2[axis_size_2 - 1]) / (axis_size_2 - 1);
151  for (size_t index_1 = 0; index_1 < axis_size_1; ++index_1) {
152  double value_1 = axis_1[index_1] - mid_value_1;
153  std::vector<double> row_vector;
154  row_vector.resize(axis_size_2, 0.0);
155  for (size_t index_2 = 0; index_2 < axis_size_2; ++index_2) {
156  double value_2 = axis_2[index_2] - mid_value_2;
157  double z_value = getIntegratedPDF2d(value_1, step_size_1, value_2, step_size_2);
158  row_vector[index_2] = z_value;
159  }
160  kernel[index_1] = row_vector;
161  }
162  // Calculate convolution
163  std::vector<std::vector<double>> result;
164  Convolve().fftconvolve(source, kernel, result);
165  // Populate intensity map with results
166  std::vector<double> result_vector;
167  for (size_t index_1 = 0; index_1 < axis_size_1; ++index_1) {
168  for (size_t index_2 = 0; index_2 < axis_size_2; ++index_2) {
169  double value = result[index_1][index_2];
170  result_vector.push_back(value);
171  }
172  }
173  // Truncate negative values that can arise because of finite precision of Fourier Transform
174  std::for_each(result_vector.begin(), result_vector.end(),
175  [](double& val) { val = std::max(0.0, val); });
176  for (auto it = p_intensity_map->begin(); it != p_intensity_map->end(); ++it)
177  (*it) = result_vector[it.getIndex()];
178 }
179 
180 double ConvolutionDetectorResolution::getIntegratedPDF1d(double x, double step) const
181 {
182  double halfstep = step / 2.0;
183  double xmin = x - halfstep;
184  double xmax = x + halfstep;
185  ASSERT(m_res_function_1d != nullptr);
186  return m_res_function_1d(xmax) - m_res_function_1d(xmin);
187 }
188 
189 double ConvolutionDetectorResolution::getIntegratedPDF2d(double x, double step_x, double y,
190  double step_y) const
191 {
192  double halfstepx = step_x / 2.0;
193  double halfstepy = step_y / 2.0;
194  double xmin = x - halfstepx;
195  double xmax = x + halfstepx;
196  double ymin = y - halfstepy;
197  double ymax = y + halfstepy;
198  double result =
199  m_res_function_2d->evaluateCDF(xmax, ymax) - m_res_function_2d->evaluateCDF(xmax, ymin)
200  - m_res_function_2d->evaluateCDF(xmin, ymax) + m_res_function_2d->evaluateCDF(xmin, ymin);
201  return result;
202 }
#define ASSERT(condition)
Definition: Assert.h:31
Defines class ConvolutionDetectorResolution.
Defines class Math::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 > m_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.
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:38
void fftconvolve(const double1d_t &source, const double1d_t &kernel, double1d_t &result)
convolution in 1D
Definition: Convolve.cpp:138
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:57
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:93
size_t rank() const
Returns number of dimensions.
Definition: OutputData.h:56
std::vector< T > getRawDataVector() const
Returns copy of raw data vector.
Definition: OutputData.h:334
const IAxis & axis(size_t serial_number) const
returns axis with given serial number
Definition: OutputData.h:318
void setRawDataVector(const std::vector< T > &data_vector)
Sets new values to raw data vector.
Definition: OutputData.h:556
iterator begin()
Returns read/write iterator that points to the first element.
Definition: OutputData.h:343