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
17#include <algorithm>
18#include <stdexcept>
19
21
22// Register the algorithm into the AlgorithmFactory
23DECLARE_ALGORITHM(SplineInterpolation)
24
25using namespace API;
26using namespace Kernel;
27using Functions::CubicSpline;
28
29//----------------------------------------------------------------------------------------------
32SplineInterpolation::SplineInterpolation() : m_cspline(std::make_shared<CubicSpline>()) {}
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 if (binsNo > 2) {
170 // perform cubic spline interpolation
171 // for each histogram in workspace, calculate interpolation and derivatives
172 for (size_t i = 0; i < histNo; ++i) {
173 // Create and instance of the cubic spline function
174 m_cspline = std::make_shared<CubicSpline>();
175 // set the interpolation points
176 setInterpolationPoints(iwspt, i);
177 // compare the data set against our spline
178 calculateSpline(mwspt, outputWorkspace, i);
179 // calculate derivatives for each order
180 for (size_t j = 0; j < order; ++j) {
181 calculateDerivatives(mwspt, derivs[i], j + 1);
182 }
183 pgress.report();
184 }
185 } else {
186 // perform linear interpolation
187
188 // first check that the x-axis (first spectrum) is sorted ascending
189 if (!std::is_sorted(mwspt->x(0).rawData().begin(), mwspt->x(0).rawData().end())) {
190 throw std::runtime_error("X-axis of the workspace to match is not sorted. "
191 "Consider calling SortXAxis before.");
192 }
193
194 for (size_t i = 0; i < histNo; ++i) {
195 // set up the function that needs to be interpolated
196 std::unique_ptr<gsl_interp_accel, void (*)(gsl_interp_accel *)> acc(gsl_interp_accel_alloc(),
197 gsl_interp_accel_free);
198 std::unique_ptr<gsl_interp, void (*)(gsl_interp *)> linear(gsl_interp_alloc(gsl_interp_linear, binsNo),
199 gsl_interp_free);
200 gsl_interp_linear->init(linear.get(), &(iwspt->x(i)[0]), &(iwspt->y(i)[0]), binsNo);
201
202 // figure out the interpolation range
203 const std::pair<size_t, size_t> range = findInterpolationRange(iwspt, mwspt, i);
204
205 // perform interpolation in the range
206 for (size_t k = range.first; k < range.second; ++k) {
207 gsl_interp_linear->eval(linear.get(), &(iwspt->x(i)[0]), &(iwspt->y(i)[0]), binsNo, mwspt->x(0)[k], acc.get(),
208 &(outputWorkspace->mutableY(i)[k]));
209 // calculate only 1st order derivative if needed
210 if (order > 0) {
211 gsl_interp_linear->eval_deriv(linear.get(), &(iwspt->x(i)[0]), &(iwspt->y(i)[0]), binsNo, mwspt->x(0)[k],
212 acc.get(), &(derivs[i]->mutableY(0)[k]));
213 }
214 }
215 // flat extrapolation outside the range
216 extrapolateFlat(outputWorkspace, iwspt, i, range, order > 0, derivs);
217 pgress.report();
218 }
219 }
220 // Store the output workspaces
221 if (order > 0 && !isDefault("OutputWorkspaceDeriv")) {
222 // Store derivatives in a grouped workspace
224 for (size_t i = 0; i < histNo; ++i) {
225 wsg->addWorkspace(derivs[i]);
226 }
227 setProperty("OutputWorkspaceDeriv", wsg);
228 }
229 setProperty("OutputWorkspace", outputWorkspace);
230}
231
240API::MatrixWorkspace_sptr SplineInterpolation::setupOutputWorkspace(const API::MatrixWorkspace_sptr &mws,
241 const API::MatrixWorkspace_sptr &iws) const {
242 const size_t numSpec = iws->getNumberHistograms();
243 MatrixWorkspace_sptr outputWorkspace = WorkspaceFactory::Instance().create(mws, numSpec);
244 // share x-axes with the first spectrum of mws
245 for (size_t i = 0; i < numSpec; ++i) {
246 outputWorkspace->setSharedX(i, mws->sharedX(0));
247 }
248 // use the vertical (spectrum) axis form the iws
249 auto vAxis = std::unique_ptr<Axis>(iws->getAxis(1)->clone(mws.get()));
250 outputWorkspace->replaceAxis(1, std::move(vAxis));
251 return outputWorkspace;
252}
253
259MatrixWorkspace_sptr SplineInterpolation::convertBinnedData(MatrixWorkspace_sptr workspace) {
260 if (workspace->isHistogramData()) {
261 g_log.warning("Histogram data provided, converting to point data");
262 Algorithm_sptr converter = createChildAlgorithm("ConvertToPointData");
263 converter->initialize();
264 converter->setProperty("InputWorkspace", workspace);
265 converter->execute();
266 return converter->getProperty("OutputWorkspace");
267 } else {
268 return workspace;
269 }
270}
271
278void SplineInterpolation::setInterpolationPoints(const MatrixWorkspace_const_sptr &inputWorkspace,
279 const size_t row) const {
280 const auto &xIn = inputWorkspace->x(row);
281 const auto &yIn = inputWorkspace->y(row);
282 const size_t size = xIn.size();
283
284 // pass x attributes and y parameters to CubicSpline
285 m_cspline->setAttributeValue("n", static_cast<int>(size));
286
287 for (size_t i = 0; i < size; ++i) {
288 m_cspline->setXAttribute(i, xIn[i]);
289 m_cspline->setParameter(i, yIn[i]);
290 }
291}
292
299void SplineInterpolation::calculateDerivatives(const API::MatrixWorkspace_const_sptr &inputWorkspace,
300 const API::MatrixWorkspace_sptr &outputWorkspace,
301 const size_t order) const {
302 // get x and y parameters from workspaces
303 const size_t nData = inputWorkspace->y(0).size();
304 const double *xValues = &(inputWorkspace->x(0)[0]);
305 double *yValues = &(outputWorkspace->mutableY(order - 1)[0]);
306
307 // calculate the derivatives
308 m_cspline->derivative1D(yValues, xValues, nData, order);
309}
310
317void SplineInterpolation::calculateSpline(const MatrixWorkspace_const_sptr &inputWorkspace,
318 const MatrixWorkspace_sptr &outputWorkspace, const size_t row) const {
319 // setup input parameters
320 const size_t nData = inputWorkspace->y(0).size();
321 const double *xValues = &(inputWorkspace->x(0)[0]);
322 double *yValues = &(outputWorkspace->mutableY(row)[0]);
323
324 // calculate the interpolation
325 m_cspline->function1D(yValues, xValues, nData);
326}
327
337void SplineInterpolation::extrapolateFlat(const MatrixWorkspace_sptr &ows, const MatrixWorkspace_const_sptr &iwspt,
338 const size_t row, const std::pair<size_t, size_t> &indices,
339 const bool doDerivs, std::vector<MatrixWorkspace_sptr> &derivs) const {
340
341 const double yFirst = iwspt->y(row).front();
342 const double yLast = iwspt->y(row).back();
343 for (size_t bin = 0; bin < indices.first; ++bin) {
344 ows->mutableY(row)[bin] = yFirst;
345 if (doDerivs) {
346 // if derivatives are requested
347 derivs[row]->mutableY(0)[bin] = 0.;
348 }
349 }
350 const size_t numBins = ows->blocksize();
351 for (size_t bin = indices.second; bin < numBins; ++bin) {
352 ows->mutableY(row)[bin] = yLast;
353 if (doDerivs) {
354 // if derivatives are requested
355 derivs[row]->mutableY(0)[bin] = 0.;
356 }
357 }
358}
359
370std::pair<size_t, size_t> SplineInterpolation::findInterpolationRange(const MatrixWorkspace_const_sptr &iwspt,
371 const MatrixWorkspace_sptr &mwspt,
372 const size_t row) {
373
374 auto xAxisIn = iwspt->x(row).rawData();
375 std::sort(xAxisIn.begin(), xAxisIn.end());
376 const auto &xAxisOut = mwspt->x(0).rawData();
377
378 size_t firstIndex = 0;
379 size_t lastIndex = xAxisOut.size();
380
381 if (xAxisOut.front() >= xAxisIn.back()) {
382 lastIndex = firstIndex;
383 } else if (xAxisOut.back() <= xAxisIn.front()) {
384 firstIndex = lastIndex;
385 } else {
386 for (size_t i = 0; i < xAxisOut.size(); ++i) {
387 if (xAxisOut[i] > xAxisIn.front()) {
388 firstIndex = i;
389 break;
390 }
391 }
392 for (size_t i = 0; i < xAxisOut.size(); ++i) {
393 if (xAxisOut[i] > xAxisIn.back()) {
394 lastIndex = i;
395 break;
396 }
397 }
398 }
399
400 std::string log = "Workspace index " + std::to_string(row) +
401 ": Will perform flat extrapolation outside bin range: " + std::to_string(firstIndex) + " to " +
402 std::to_string(lastIndex) + "\n";
403
404 g_log.debug(log);
405
406 return std::make_pair(firstIndex, lastIndex);
407}
408} // namespace Mantid::CurveFitting::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
IPeaksWorkspace_sptr workspace
Definition: IndexPeaks.cpp:114
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
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.
Definition: Algorithm.cpp:842
Kernel::Logger & g_log
Definition: Algorithm.h:451
bool isDefault(const std::string &name) const
Definition: Algorithm.cpp:2084
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
void calculateSpline(const API::MatrixWorkspace_const_sptr &inputWorkspace, const API::MatrixWorkspace_sptr &outputWorkspace, const size_t row) const
Calculate the interpolation of the input workspace against the spline and store it in outputWorkspace...
std::pair< size_t, size_t > findInterpolationRange(const API::MatrixWorkspace_const_sptr &iwspt, const API::MatrixWorkspace_sptr &mwspt, const size_t row)
Find the the interpolation range.
void setInterpolationPoints(const API::MatrixWorkspace_const_sptr &inputWorkspace, const size_t row) const
set the points that define the spline used for interpolation of a workspace
void extrapolateFlat(const API::MatrixWorkspace_sptr &ows, const API::MatrixWorkspace_const_sptr &iwspt, const size_t row, const std::pair< size_t, size_t > &indices, const bool doDerivs, std::vector< API::MatrixWorkspace_sptr > &derivs) const
Extrapolates flat for the points outside the x-range.
void calculateDerivatives(const API::MatrixWorkspace_const_sptr &inputWorkspace, const API::MatrixWorkspace_sptr &outputWorkspace, const size_t order) const
Calculate the derivatives of the input workspace from the spline.
std::shared_ptr< Functions::CubicSpline > m_cspline
CubicSpline member used to perform interpolation.
A wrapper around GSL functions implementing cubic spline interpolation.
Definition: CubicSpline.h:31
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:114
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Definition: ProgressBase.h:51
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:61
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
STL namespace.
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