1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
// ************************************************************************************************
//
// BornAgain: simulate and fit reflection and scattering
//
//! @file Device/Resolution/ConvolutionDetectorResolution.cpp
//! @brief Implements class ConvolutionDetectorResolution.
//!
//! @homepage http://www.bornagainproject.org
//! @license GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
// ************************************************************************************************
#include "Device/Resolution/ConvolutionDetectorResolution.h"
#include "Base/Axis/Frame.h"
#include "Base/Axis/Scale.h"
#include "Base/Util/Assert.h"
#include "Device/Data/Datafield.h"
#include "Device/Resolution/Convolve.h"
ConvolutionDetectorResolution::ConvolutionDetectorResolution(cumulative_DF_1d res_function_1d)
: m_rank(1)
, m_res_function_1d(res_function_1d)
{
}
ConvolutionDetectorResolution::ConvolutionDetectorResolution(
const IResolutionFunction2D& res_function_2d)
: m_rank(2)
, m_res_function_1d(nullptr)
{
setResolutionFunction(res_function_2d);
}
ConvolutionDetectorResolution::~ConvolutionDetectorResolution() = default;
ConvolutionDetectorResolution::ConvolutionDetectorResolution(
const ConvolutionDetectorResolution& other)
: m_rank(other.m_rank)
, m_res_function_1d(other.m_res_function_1d)
{
if (other.m_res_function_2d)
setResolutionFunction(*other.m_res_function_2d);
}
ConvolutionDetectorResolution* ConvolutionDetectorResolution::clone() const
{
return new ConvolutionDetectorResolution(*this);
}
std::vector<const INode*> ConvolutionDetectorResolution::nodeChildren() const
{
return std::vector<const INode*>() << m_res_function_2d;
}
void ConvolutionDetectorResolution::execDetectorResolution(Datafield* df) const
{
ASSERT(df->rank() == m_rank);
if (m_rank == 1)
apply1dConvolution(df);
else if (m_rank == 2)
apply2dConvolution(df);
else
ASSERT_NEVER;
}
void ConvolutionDetectorResolution::setResolutionFunction(const IResolutionFunction2D& resFunc)
{
m_res_function_2d.reset(resFunc.clone());
}
void ConvolutionDetectorResolution::apply1dConvolution(Datafield* df) const
{
ASSERT(m_res_function_1d);
ASSERT(df->rank() == 1);
const Scale& axis = df->axis(0);
const size_t n = df->size();
if (n < 2)
return; // No convolution for sets of zero or one element
// Construct kernel vector from resolution function
ASSERT(axis.size() == n);
double step_size = std::abs(axis.binCenter(0) - axis.binCenter(n - 1)) / (n - 1);
double mid_value = axis.binCenter(n / 2); // because Convolve expects zero at midpoint
std::vector<double> kernel;
for (size_t index = 0; index < n; ++index)
kernel.push_back(getIntegratedPDF1d(axis.binCenter(index) - mid_value, step_size));
// Calculate convolution
std::vector<double> result;
Convolve().fftconvolve1D(df->flatVector(), kernel, result);
// Truncate negative values that can arise because of finite precision of Fourier Transform
for (double& e : result)
e = std::max(0.0, e);
df->setVector(result);
}
void ConvolutionDetectorResolution::apply2dConvolution(Datafield* df) const
{
ASSERT(m_res_function_2d);
ASSERT(df->rank() == 2);
const Scale& X = df->axis(0);
const Scale& Y = df->axis(1);
const size_t nx = X.size();
const size_t ny = Y.size();
if (nx < 2 && ny < 2)
return;
// Construct kernel vector from resolution function
double2d_t kernel;
kernel.resize(ny);
double mid_value1 = X.binCenter(nx / 2); // because Convolve expects zero at midpoint
double mid_value2 = Y.binCenter(ny / 2); // because Convolve expects zero at midpoint
double dx = std::abs(X.binCenter(0) - X.binCenter(nx - 1)) / (nx - 1);
double dy = std::abs(Y.binCenter(0) - Y.binCenter(ny - 1)) / (ny - 1);
for (size_t iy = 0; iy < ny; ++iy) {
double y = Y.binCenter(iy) - mid_value2;
std::vector<double> row_vector;
row_vector.resize(nx, 0.0);
for (size_t ix = 0; ix < nx; ++ix) {
double x = X.binCenter(ix) - mid_value1;
row_vector[ix] = getIntegratedPDF2d(x, dx, y, dy);
}
kernel[iy] = row_vector;
}
// Calculate convolution
double2d_t result;
Convolve().fftconvolve2D(df->values2D(), kernel, result);
df->setVector2D(result);
}
double ConvolutionDetectorResolution::getIntegratedPDF1d(double x, double step) const
{
double halfstep = step / 2.0;
double xmin = x - halfstep;
double xmax = x + halfstep;
ASSERT(m_res_function_1d != nullptr);
return m_res_function_1d(xmax) - m_res_function_1d(xmin);
}
double ConvolutionDetectorResolution::getIntegratedPDF2d(double x, double step_x, double y,
double step_y) const
{
double halfstepx = step_x / 2.0;
double halfstepy = step_y / 2.0;
double xmin = x - halfstepx;
double xmax = x + halfstepx;
double ymin = y - halfstepy;
double ymax = y + halfstepy;
double result =
m_res_function_2d->evaluateCDF(xmax, ymax) - m_res_function_2d->evaluateCDF(xmax, ymin)
- m_res_function_2d->evaluateCDF(xmin, ymax) + m_res_function_2d->evaluateCDF(xmin, ymin);
return result;
}
|