Mantid
Loading...
Searching...
No Matches
SplineInterpolation.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 +
8
11#include "MantidAPI/Progress.h"
12#include "MantidAPI/TextAxis.h"
16#include "MantidKernel/Spline.h"
17
18#include <algorithm>
19#include <stdexcept>
20
22
23// Register the algorithm into the AlgorithmFactory
24DECLARE_ALGORITHM(SplineInterpolation)
25
26using namespace API;
27using namespace Kernel;
28
29//----------------------------------------------------------------------------------------------
32SplineInterpolation::SplineInterpolation() {}
33
34//----------------------------------------------------------------------------------------------
36const std::string SplineInterpolation::name() const { return "SplineInterpolation"; }
37
39int SplineInterpolation::version() const { return 1; }
40
42const std::string SplineInterpolation::category() const {
43 return "Optimization;CorrectionFunctions\\BackgroundCorrections";
44}
45
47const std::string SplineInterpolation::summary() const {
48 return "Interpolates a set of spectra onto a spline defined by a second "
49 "input workspace. Optionally, this algorithm can also calculate "
50 "derivatives up to order 2 as a side product";
51}
52
53//----------------------------------------------------------------------------------------------
56void SplineInterpolation::init() {
57 declareProperty(std::make_unique<WorkspaceProperty<>>("WorkspaceToMatch", "", Direction::Input),
58 "The workspace which defines the points of the spline.");
59
60 declareProperty(std::make_unique<WorkspaceProperty<>>("WorkspaceToInterpolate", "", Direction::Input),
61 "The workspace on which to perform the interpolation algorithm.");
62
63 declareProperty(std::make_unique<WorkspaceProperty<>>("OutputWorkspace", "", Direction::Output),
64 "The workspace containing the calculated points and derivatives");
65
66 declareProperty(std::make_unique<WorkspaceProperty<WorkspaceGroup>>("OutputWorkspaceDeriv", "", Direction::Output,
68 "The workspace containing the calculated derivatives");
69
70 auto validator = std::make_shared<BoundedValidator<int>>(0, 2);
71 declareProperty("DerivOrder", 2, validator, "Order to derivatives to calculate.");
72
73 declareProperty("Linear2Points", false,
74 "Set to true to perform linear "
75 "interpolation if only 2 points are "
76 "present.");
77}
78
79//----------------------------------------------------------------------------------------------
82std::map<std::string, std::string> SplineInterpolation::validateInputs() {
83 // initialise map (result)
84 std::map<std::string, std::string> result;
85
86 // get inputs that need validation
87 const bool linear = getProperty("Linear2Points");
88 const int derivOrder = getProperty("DerivOrder");
89
90 MatrixWorkspace_const_sptr iwsValid = getProperty("WorkspaceToInterpolate");
91 if (iwsValid) {
92 try {
93 const size_t binsNo = iwsValid->blocksize();
94
95 // The minimum number of points for cubic splines is 3
96 if (binsNo < 2) {
97 result["WorkspaceToInterpolate"] = "Workspace must have minimum 2 points.";
98 } else if (binsNo == 2) {
99 if (!linear) {
100 result["WorkspaceToInterpolate"] = "Workspace has only 2 points, "
101 "you can enable linear interpolation by "
102 "setting the property Linear2Points. Otherwise "
103 "provide a minimum of 3 points.";
104 } else if (derivOrder == 2) {
105 result["DerivOrder"] = "Linear interpolation is requested, hence "
106 "derivative order can be maximum 1.";
107 }
108 }
109 } catch (std::length_error &) {
110 result["WorkspaceToInterpolate"] = "The input workspace does not have "
111 "the same number of bins per spectrum";
112 }
113 } else {
114 result["WorkspaceToInterpolate"] = "The input is not a MatrixWorkspace";
115 }
116
117 return result;
118}
119
120//----------------------------------------------------------------------------------------------
123void SplineInterpolation::exec() {
124 // read in algorithm parameters
125 const int derivOrder = getProperty("DerivOrder");
126 const auto order = static_cast<size_t>(derivOrder);
127
128 // set input workspaces
129 MatrixWorkspace_sptr mws = getProperty("WorkspaceToMatch");
130 MatrixWorkspace_sptr iws = getProperty("WorkspaceToInterpolate");
131
132 const size_t histNo = iws->getNumberHistograms();
133 const size_t binsNo = iws->blocksize();
134 const size_t histNoToMatch = mws->getNumberHistograms();
135
136 // vector of multiple derivative workspaces
137 std::vector<MatrixWorkspace_sptr> derivs(histNo);
138
139 // warn user that we only use first spectra in matching workspace
140 if (histNoToMatch > 1 && !iws->isCommonBins()) {
141 g_log.warning() << "The workspace to interpolate doesn't have common bins, SplineInterpolation algorithm will use "
142 "the x-axis of the first spectrum.\n";
143 }
144
145 MatrixWorkspace_sptr outputWorkspace = setupOutputWorkspace(mws, iws);
146
147 Progress pgress(this, 0.0, 1.0, histNo);
148
149 // first prepare the derivatives if needed
150 if (order > 0) {
151 for (size_t i = 0; i < histNo; ++i) {
152 derivs[i] = WorkspaceFactory::Instance().create(mws, order);
153 auto vAxis = std::make_unique<NumericAxis>(order);
154 for (size_t j = 0; j < order; ++j) {
155 vAxis->setValue(j, static_cast<int>(j) + 1.);
156 derivs[i]->setSharedX(j, mws->sharedX(0));
157 }
158 derivs[i]->replaceAxis(1, std::move(vAxis));
159 }
160 }
161
162 // convert data to binned data if initially histogrammed, do not alter the
163 // original inputs; these should be used in subsequent processing, however,
164 // note that the parent of the output workspace is still mws, and not mwspt!
165 // meaning that it can be either histogram or point data dependent on the mws
168
169 // a lambda to create a unique pointer to a spline object
170 std::function<std::unique_ptr<Mantid::Kernel::Spline<double, double>>(size_t)> spline_creator;
171 if (binsNo > 2) {
172 // perform cubic spline interpolation
173 // for each histogram in workspace, calculate interpolation and derivatives
174 spline_creator = [iwspt](size_t i) {
175 return std::make_unique<Mantid::Kernel::CubicSpline<double, double>>(iwspt->x(i).rawData(),
176 iwspt->y(i).rawData());
177 };
178 } else {
179 // perform linear interpolation
180
181 // first check that the x-axis (first spectrum) is sorted ascending
182 if (!std::is_sorted(mwspt->x(0).rawData().begin(), mwspt->x(0).rawData().end())) {
183 throw std::runtime_error("X-axis of the workspace to match is not sorted. "
184 "Consider calling SortXAxis before.");
185 }
186 spline_creator = [iwspt](size_t i) {
187 return std::make_unique<Mantid::Kernel::LinearSpline<double, double>>(iwspt->x(i).rawData(),
188 iwspt->y(i).rawData());
189 };
190 }
191
192 for (size_t i = 0; i < histNo; ++i) {
193 // figure out the interpolation range
194 const std::pair<size_t, size_t> range = findInterpolationRange(iwspt, mwspt, i);
195
196 // set up the function that needs to be interpolated
197 auto spline = spline_creator(i);
198
199 // perform interpolation in the range
200 // NOTE for legacy compatibility, this uses only the FIRST spectrum's X-axis for all other interpolations
201 std::span<double const> xInRange(mwspt->x(0).cbegin() + range.first, range.second - range.first);
202 auto &yNew = outputWorkspace->mutableY(i);
203 std::transform(xInRange.begin(), xInRange.end(), yNew.begin() + range.first,
204 [&spline](double x) { return (*spline)(x); });
205
206 // flat extrapolation outside the range
207 const double yFirst = iwspt->y(i).front();
208 const double yLast = iwspt->y(i).back();
209 std::fill(yNew.begin(), yNew.begin() + range.first, yFirst);
210 std::fill(yNew.begin() + range.second, yNew.end(), yLast);
211
212 // if derivatives are requested, only give first-order
213 for (size_t j = 0; j < order; ++j) {
214 auto &deriv = derivs[i]->mutableY(j);
215 // 0 outside the range
216 std::fill(deriv.begin(), deriv.begin() + range.first, 0.0);
217 std::fill(deriv.begin() + range.second, deriv.end(), 0.0);
218 // eval inside range
219 std::transform(xInRange.begin(), xInRange.end(), deriv.begin() + range.first,
220 [&spline, j](double x) { return spline->deriv(x, static_cast<unsigned int>(j + 1)); });
221 }
222 pgress.report();
223 }
224
225 // Store the output workspaces
226 if (order > 0 && !isDefault("OutputWorkspaceDeriv")) {
227 // Store derivatives in a grouped workspace
229 for (size_t i = 0; i < histNo; ++i) {
230 wsg->addWorkspace(derivs[i]);
231 }
232 setProperty("OutputWorkspaceDeriv", wsg);
233 }
234 setProperty("OutputWorkspace", outputWorkspace);
235}
236
245API::MatrixWorkspace_sptr SplineInterpolation::setupOutputWorkspace(const API::MatrixWorkspace_sptr &mws,
246 const API::MatrixWorkspace_sptr &iws) const {
247 const size_t numSpec = iws->getNumberHistograms();
248 MatrixWorkspace_sptr outputWorkspace = WorkspaceFactory::Instance().create(mws, numSpec);
249 // share x-axes with the first spectrum of mws
250 for (size_t i = 0; i < numSpec; ++i) {
251 outputWorkspace->setSharedX(i, mws->sharedX(0));
252 }
253 // use the vertical (spectrum) axis form the iws
254 auto vAxis = std::unique_ptr<Axis>(iws->getAxis(1)->clone(mws.get()));
255 outputWorkspace->replaceAxis(1, std::move(vAxis));
256 return outputWorkspace;
257}
258
264MatrixWorkspace_sptr SplineInterpolation::convertBinnedData(MatrixWorkspace_sptr workspace) {
265 if (workspace->isHistogramData()) {
266 g_log.warning("Histogram data provided, converting to point data");
267 Algorithm_sptr converter = createChildAlgorithm("ConvertToPointData");
268 converter->initialize();
269 converter->setProperty("InputWorkspace", workspace);
270 converter->execute();
271 return converter->getProperty("OutputWorkspace");
272 } else {
273 return workspace;
274 }
275}
276
287std::pair<size_t, size_t> SplineInterpolation::findInterpolationRange(const MatrixWorkspace_const_sptr &iwspt,
288 const MatrixWorkspace_sptr &mwspt,
289 const size_t row) {
290
291 auto xAxisIn = iwspt->x(row).rawData();
292 std::sort(xAxisIn.begin(), xAxisIn.end());
293 const auto &xAxisOut = mwspt->x(0).rawData();
294
295 size_t firstIndex = 0;
296 size_t lastIndex = xAxisOut.size();
297
298 if (xAxisOut.empty() || xAxisIn.empty()) {
299 // if either vector is empty, don't do anything else
300 } else {
301 if (xAxisOut.front() >= xAxisIn.back()) {
302 lastIndex = firstIndex;
303 } else if (xAxisOut.back() <= xAxisIn.front()) {
304 firstIndex = lastIndex;
305 } else {
306 auto start =
307 std::find_if(xAxisOut.cbegin(), xAxisOut.cend(), [&xAxisIn](double x) { return x >= xAxisIn.front(); });
308 firstIndex = std::distance(xAxisOut.begin(), start);
309 auto stop = std::find_if(start, xAxisOut.cend(), [&xAxisIn](double x) { return x > xAxisIn.back(); });
310 lastIndex = std::distance(xAxisOut.begin(), stop);
311 }
312 }
313
314 std::string log = "Workspace index " + std::to_string(row) +
315 ": Will perform flat extrapolation outside bin range: " + std::to_string(firstIndex) + " to " +
316 std::to_string(lastIndex) + "\n";
317
318 g_log.debug(log);
319
320 return std::make_pair(firstIndex, lastIndex);
321}
322} // namespace Mantid::CurveFitting::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Kernel::Logger & g_log
Definition Algorithm.h:422
bool isDefault(const std::string &name) const
Helper class for reporting progress from algorithms.
Definition Progress.h:25
Class to hold a set of workspaces.
A property class for workspaces.
API::MatrixWorkspace_sptr setupOutputWorkspace(const API::MatrixWorkspace_sptr &mws, const API::MatrixWorkspace_sptr &iws) const
setup an output workspace using meta data from inws and taking a number of spectra
API::MatrixWorkspace_sptr convertBinnedData(API::MatrixWorkspace_sptr workspace)
convert a binned workspace to point data using ConvertToPointData
std::pair< size_t, size_t > findInterpolationRange(const API::MatrixWorkspace_const_sptr &iwspt, const API::MatrixWorkspace_sptr &mwspt, const size_t row)
Find the interpolation range.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< WorkspaceGroup > WorkspaceGroup_sptr
shared pointer to Mantid::API::WorkspaceGroup
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition Algorithm.h:52
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
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