File: ConvolutionDetectorResolution.cpp

package info (click to toggle)
bornagain 23.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 103,936 kB
  • sloc: cpp: 423,131; python: 40,997; javascript: 11,167; awk: 630; sh: 318; ruby: 173; xml: 130; makefile: 51; ansic: 24
file content (158 lines) | stat: -rw-r--r-- 5,491 bytes parent folder | download | duplicates (2)
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;
}