Mantid
Loading...
Searching...
No Matches
RealFFT.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
13
15
16#include <algorithm>
17#include <cmath>
18#include <functional>
19#include <numeric>
20#include <sstream>
21
24
25#include "MantidHistogramData/LinearGenerator.h"
26
28
29// Register the class into the algorithm factory
30DECLARE_ALGORITHM(RealFFT)
31
32using namespace Kernel;
33using namespace API;
34
39 "The name of the input workspace.");
42 "The name of the output workspace. It contains three "
43 "spectra: the real, the imaginary parts of the transform and "
44 "their modulus.");
45
46 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
47 mustBePositive->setLower(0);
49 "The index of the spectrum in the input workspace to transform.");
50
51 std::vector<std::string> fft_dir{"Forward", "Backward"};
52 declareProperty(PropertyNames::TRANSFORM, "Forward", std::make_shared<StringListValidator>(fft_dir),
53 R"(The direction of the transform: "Forward" or "Backward".)");
55 "Ignores the requirement that X bins be linear and of the same size. "
56 "FFT result will not be valid for the X axis, and should be ignored.");
57}
58
59std::map<std::string, std::string> RealFFT::validateInputs() {
60 std::map<std::string, std::string> issues;
62 auto groupWS = dynamic_cast<API::WorkspaceGroup const *>(inWS.get());
63 if (groupWS != nullptr) {
64 for (std::size_t idx = 0; idx < groupWS->size(); idx++) {
65 auto subissues = this->actuallyValidateInputs(groupWS->getItem(idx));
66 for (auto const &[key, value] : subissues) {
67 std::string new_key = key + "_" + std::to_string(idx);
68 issues[new_key] = value;
69 }
70 }
71 } else {
72 issues = this->actuallyValidateInputs(inWS);
73 }
74 return issues;
75}
76
77std::map<std::string, std::string> RealFFT::actuallyValidateInputs(API::Workspace_sptr const &ws) {
78 std::map<std::string, std::string> issues;
79
80 // verify a matrix workspace has been passed
81 auto inWS = dynamic_cast<API::MatrixWorkspace const *>(ws.get());
82 if (!inWS) {
83 issues[PropertyNames::INPUT_WKSP] = "RealFFT requires an input matrix workspace";
84 return issues;
85 }
86 std::string transform = getProperty(PropertyNames::TRANSFORM);
87 int wi = (transform == "Forward") ? getProperty(PropertyNames::WKSP_INDEX) : 0;
88 if (wi >= static_cast<int>(inWS->getNumberHistograms())) {
89 issues[PropertyNames::INPUT_WKSP] = "Property WorkspaceIndex is out of range";
91 return issues;
92 }
93 if (transform == "Backward") {
94 if (inWS->getNumberHistograms() < 2)
95 issues[PropertyNames::INPUT_WKSP] = "The input workspace must have at least 2 spectra.";
96 }
97
98 // Check that the x values are evenly spaced
99 bool IgnoreXBins = getProperty(PropertyNames::IGNORE_X_BINS);
100 if (!IgnoreXBins) {
101 const auto &X = inWS->x(wi);
102 double dx = (X.back() - X.front()) / static_cast<double>(X.size() - 1);
103 for (size_t i = 0; i < X.size() - 2; i++) {
104 if (std::abs(dx - X[i + 1] + X[i]) / dx > 1e-7) {
105 issues[PropertyNames::INPUT_WKSP] = "X axis must be linear (all bins have same width). This can be ignored if "
106 "IgnoreXBins is set to true.";
108 break;
109 }
110 }
111 }
112 return issues;
113}
114
121
122 // get the x-spacing
123 std::string transform = getProperty(PropertyNames::TRANSFORM);
124 int spec = (transform == "Forward") ? getProperty(PropertyNames::WKSP_INDEX) : 0;
125 const auto &X = inWS->x(spec);
126 double dx = (X.back() - X.front()) / static_cast<double>(X.size() - 1);
127 auto ySize = inWS->y(spec).size();
128
130
131 if (transform == "Forward") {
132 // first, transform the data
133 auto const &yData = inWS->y(spec);
134 std::vector<double> data(yData.cbegin(), yData.cend());
137 gsl_fft_real_transform(data.data(), 1, ySize, wavetable.get(), workspace.get());
138 workspace.reset();
139 wavetable.reset();
140
141 // unpack the halfcomplex values -- will be full complex, interleaved real/imag
142 std::vector<double> unpacked(2 * ySize, 0.0);
143 gsl_fft_halfcomplex_unpack(data.data(), unpacked.data(), 1, ySize);
144
145 // second, setup the workspace
146 // NOTE: the FT of a real sequence is a half-complex sequence.
147 // The "half" part refers to a mirror symmetry z[k] = z*[N-k], NOT to the actual number of complex elements in the
148 // transform. There are as many complex elements in the output sequence as real elements in the input sequence.
149 // HOWEVER for real data, only the first N/2 points are true measurements; the rest are artifacts
150 auto yOutSize = ySize / 2 + 1;
151 auto xOutSize = inWS->isHistogramData() ? yOutSize + 1 : yOutSize;
152
153 outWS = WorkspaceFactory::Instance().create(inWS, 3, xOutSize, yOutSize);
154 auto tAxis = std::make_unique<API::TextAxis>(3);
155 tAxis->setLabel(0, "Real");
156 tAxis->setLabel(1, "Imag");
157 tAxis->setLabel(2, "Modulus");
158 outWS->replaceAxis(1, std::move(tAxis));
159
160 // set the workspace x values
161 auto &x = outWS->mutableX(0);
162 double df = 1.0 / (dx * static_cast<double>(ySize));
163 std::generate(x.begin(), x.end(), HistogramData::LinearGenerator(0, df));
164 outWS->setSharedX(1, outWS->sharedX(0));
165 outWS->setSharedX(2, outWS->sharedX(0));
166
167 // set the workspace y values
168 auto &y1 = outWS->mutableY(0);
169 auto &y2 = outWS->mutableY(1);
170 auto &y3 = outWS->mutableY(2);
171 for (std::size_t i = 0; i < yOutSize; i++) {
172 std::size_t const j = i * 2;
173 double re = unpacked[j];
174 double im = unpacked[j + 1];
175 y1[i] = re * dx; // real part
176 y2[i] = im * dx; // imaginary part
177 y3[i] = dx * sqrt(re * re + im * im); // modulus
178 }
179 } else // Backward
180 {
181 // NOTE for legacy compatibility, the size of the output is computed this way
182 std::size_t yOutSize = (ySize - 1) * 2;
183 if (inWS->y(1).back() != 0.0) {
184 yOutSize++;
185 }
186
187 // Setup the half-complex data vector, whose arrangement depends on even/odd of input sequence.
188
189 // If there are N complex elements, there are at most 2 * N real values to store.
190 // Half-complex has the symmetry z[k] = z*[N - k], which further reduced storage needs.
191 // if N is ODD: N = 2n + 1, z[1] = z*[N - 1], ..., z[n] = z*[n+1], z[n+1] = z*[n] --> n unique complex values
192 // if N is EVEN: N = 2n, z[1] = z*[N - 1], ..., z[n] = z*[n] is REAL --> n - 1 unique complex values
193 // In both cases, including the pure real elements, the halfcomplex array size is equal to N
194 bool even = (yOutSize % 2 == 0);
195 std::size_t num_unique_complex = (even ? yOutSize / 2 : (yOutSize - 1) / 2);
196 std::vector<double> yhc(yOutSize);
197
198 auto const &yR = inWS->y(0); // real
199 auto const &yI = inWS->y(1); // imag
200
201 // Pack the values in a halfcomplex array with GSL format, based on even / odd behavior
202 // index | 0 | 1 | | n - 1 | n = num unique complex elements
203 // hc | y[0] | y[1] y[2] | ...| y[2*n - 3] y[2*n - 2] | y[2*n - 1] y[2*n]
204 // odd | z[0].real | z[1].real, z[1].imag | ...| z[n-1].real, z[n-1].imag| z[n].real, z[n].imag
205 // even | z[0].real | z[1].real, z[1].imag | ...| z[n-1].real, z[n-1].imag| z[n].real
206 yhc[0] = yR[0];
207 // starting at 1, interleaved real/imag; odd is an inclusive loop, even exclusive loop with special treatment
208 for (std::size_t i = 1; i < num_unique_complex + (even ? 0UL : 1UL); i++) {
209 std::size_t const j = 2 * i;
210 yhc[j - 1] = yR[i]; // real
211 yhc[j] = yI[i]; // imag
212 }
213 // if even, an unmatched real value at the end
214 if (even) {
215 yhc[2 * num_unique_complex - 1] = yR[num_unique_complex];
216 }
217
218 // Then, inverse transform the data
221 gsl_fft_halfcomplex_inverse(yhc.data(), 1, yOutSize, wavetable.get(), workspace.get());
222 wavetable.reset();
223 workspace.reset();
224
225 // Finally, setup the output workspace
226 std::size_t xOutSize = inWS->isHistogramData() ? yOutSize + 1 : yOutSize;
227 double df = 1.0 / (dx * static_cast<double>(yOutSize));
228 outWS = WorkspaceFactory::Instance().create(inWS, 1, xOutSize, yOutSize);
229 auto tAxis = std::make_unique<API::TextAxis>(1);
230 tAxis->setLabel(0, "Real");
231 outWS->replaceAxis(1, std::move(tAxis));
232
233 // set the workspace x values
234 auto &xData = outWS->mutableX(0);
235 std::generate(xData.begin(), xData.end(), HistogramData::LinearGenerator(0, df));
236
237 // set the workspace y values
238 auto &yData = outWS->mutableY(0);
239 std::move(yhc.begin(), yhc.begin() + yOutSize, yData.begin());
240 yData /= df;
241 }
242
244}
245
246} // namespace Mantid::Algorithms::RealFFT
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
gsl_fft_real_wavetable * wavetable
double value
The value of the point.
Definition FitMW.cpp:51
IPeaksWorkspace_sptr workspace
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Base MatrixWorkspace Abstract Class.
Class to hold a set of workspaces.
A property class for workspaces.
void init() override
Initialisation method. Declares properties to be used in algorithm.
Definition RealFFT.cpp:36
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
Definition RealFFT.cpp:59
void exec() override
Executes the algorithm.
Definition RealFFT.cpp:119
std::map< std::string, std::string > actuallyValidateInputs(API::Workspace_sptr const &)
Definition RealFFT.cpp:77
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::string const TRANSFORM("Transform")
std::string const INPUT_WKSP("InputWorkspace")
std::string const IGNORE_X_BINS("IgnoreXBins")
std::string const OUTPUT_WKSP("OutputWorkspace")
std::string const WKSP_INDEX("WorkspaceIndex")
real_wt_uptr make_gsl_real_wavetable(std::size_t dn)
Definition GSL_Helpers.h:33
std::unique_ptr< gsl_fft_halfcomplex_wavetable, GSLFree > hc_wt_uptr
Definition GSL_Helpers.h:31
std::unique_ptr< gsl_fft_real_workspace, GSLFree > real_ws_uptr
Definition GSL_Helpers.h:30
hc_wt_uptr make_gsl_hc_wavetable(std::size_t dn)
Definition GSL_Helpers.h:35
real_ws_uptr make_gsl_real_workspace(std::size_t dn)
Definition GSL_Helpers.h:34
std::unique_ptr< gsl_fft_real_wavetable, GSLFree > real_wt_uptr
Definition GSL_Helpers.h:29
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54