Mantid
Loading...
Searching...
No Matches
CubicSpline.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 +
7//----------------------------------------------------------------------
8// Includes
9//----------------------------------------------------------------------
12#include "MantidKernel/Logger.h"
13
14#include <algorithm>
15#include <ostream>
16#include <stdexcept>
17#include <vector>
18
19#include <gsl/gsl_errno.h>
20
22
23using namespace CurveFitting;
24namespace {
26Kernel::Logger g_log("CubicSpline");
27} // namespace
28
29using namespace Kernel;
30
31using namespace API;
32
33DECLARE_FUNCTION(CubicSpline)
34
35
39 : m_min_points(3), m_acc(Kernel::spline::make_interp_accel()),
40 m_spline(gsl_spline_alloc(gsl_interp_cspline, m_min_points)), m_recalculateSpline(true) {
41 // setup class with a default set of attributes
42 declareAttribute("n", Attribute(m_min_points));
43
44 // declare corresponding attributes and parameters
45 declareAttribute("x0", Attribute(0.0));
46 declareAttribute("x1", Attribute(1.0));
47 declareAttribute("x2", Attribute(2.0));
48
49 declareParameter("y0", 0);
50 declareParameter("y1", 0);
51 declareParameter("y2", 0);
52}
53
60void CubicSpline::function1D(double *out, const double *xValues, const size_t nData) const {
61 // check if spline needs recalculating
62 int n = getAttribute("n").asInt();
63
64 // setup the reference points and calculate
67
68 calculateSpline(out, xValues, nData);
69}
70
75void CubicSpline::setupInput(int const n) const {
76 // Populate data points from the input attributes and parameters
77 bool isSorted = true;
78
79 std::vector<double> x(n);
80 std::vector<double> y(n);
81
82 for (int i = 0; i < n; ++i) {
83
84 x[i] = getAttribute("x" + std::to_string(i)).asDouble();
85 y[i] = getParameter(i);
86
87 if (isSorted) {
88 // if x[i] is out of order with its neighbours
89 if (i > 1 && i < n && (x[i - 1] < x[i - 2] || x[i - 1] > x[i])) {
90 isSorted = false;
91 }
92 }
93 }
94
95 // sort the data points if necessary
96 if (!isSorted) {
97 g_log.warning() << "Spline x parameters are not in ascending order. Values "
98 "will be sorted.\n";
99
100 using point = std::pair<double, double>;
101 std::vector<point> pairs;
102 pairs.reserve(n);
103 for (int i = 0; i < n; ++i) {
104 pairs.emplace_back(x[i], y[i]);
105 }
106
107 std::sort(pairs.begin(), pairs.end(), [](const point &xy1, const point &xy2) { return xy1.first < xy2.first; });
108
109 for (int i = 0; i < n; ++i) {
110 x[i] = pairs[i].first;
111 y[i] = pairs[i].second;
112 }
113 }
114
115 // initialize the spline
116 int status = gsl_spline_init(m_spline.get(), x.data(), y.data(), n);
117 checkGSLError(status, GSL_EINVAL);
118 m_recalculateSpline = false;
119}
120
128void CubicSpline::derivative1D(double *out, const double *xValues, size_t nData, const size_t order) const {
129 int n = getAttribute("n").asInt();
130
131 // setup the reference points and calculate
133 setupInput(n);
134
135 calculateDerivative(out, xValues, nData, order);
136}
137
143bool CubicSpline::checkXInRange(double x) const { return (x >= m_spline->interp->xmin && x <= m_spline->interp->xmax); }
144
151void CubicSpline::calculateSpline(double *out, const double *xValues, const size_t nData) const {
152 // calculate spline for given input set
153 bool outOfRange(false);
154 for (size_t i = 0; i < nData; ++i) {
155 if (checkXInRange(xValues[i])) {
156 // calculate the y value
157 out[i] = splineEval(xValues[i]);
158 } else {
159 // if out of range, set it to constant of fringe values
160 outOfRange = true;
161 if (xValues[i] < m_spline->interp->xmin) {
162 out[i] = splineEval(m_spline->interp->xmin);
163 } else {
164 out[i] = splineEval(m_spline->interp->xmax);
165 }
166 }
167 }
168
169 // inform user that some values weren't calculated
170 if (outOfRange) {
171 g_log.information() << "Some x values where out of range and will not be calculated.\n";
172 }
173}
174
180double CubicSpline::splineEval(const double x) const {
181 // calculate the y value
182 double y;
183 int errorCode = gsl_spline_eval_e(m_spline.get(), x, m_acc.get(), &y);
184
185 // check if GSL function returned an error
186 checkGSLError(errorCode, GSL_EDOM);
187
188 return y;
189}
190
198void CubicSpline::calculateDerivative(double *out, const double *xValues, const size_t nData,
199 const size_t order) const {
200 double xDeriv = 0;
201 int errorCode = 0;
202 bool outOfRange(false);
203
204 // throw error if the order is not the 1st or 2nd derivative
205 if (order < 1)
206 throw std::invalid_argument("CubicSpline: order of derivative must be 1 or greater");
207
208 for (size_t i = 0; i < nData; ++i) {
209 if (checkXInRange(xValues[i])) {
210 // choose the order of the derivative
211 if (order == 1) {
212 errorCode = gsl_spline_eval_deriv_e(m_spline.get(), xValues[i], m_acc.get(), &xDeriv);
213 } else if (order == 2) {
214 errorCode = gsl_spline_eval_deriv2_e(m_spline.get(), xValues[i], m_acc.get(), &xDeriv);
215 }
216 } else {
217 // if out of range, just set it to zero
218 outOfRange = true;
219 xDeriv = 0;
220 }
221
222 // check GSL functions didn't return an error
223 checkGSLError(errorCode, GSL_EDOM);
224
225 // record the value
226 out[i] = xDeriv;
227 }
228
229 // inform user that some values weren't calculated
230 if (outOfRange) {
231 g_log.information() << "Some x values were out of range and will not be calculated.";
232 }
233}
234
241void CubicSpline::setParameter(size_t i, const double &value, bool explicitlySet) {
242 // Call parent setParameter implementation
243 ParamFunction::setParameter(i, value, explicitlySet);
244
245 // recalculate if necessary
246 m_recalculateSpline = true;
247}
248
254void CubicSpline::setAttribute(const std::string &attName, const API::IFunction::Attribute &att) {
255
256 if (attName == "n") {
257 // get the new and old number of data points
258 int n = att.asInt();
259 int oldN = getAttribute("n").asInt();
260
261 // check that the number of data points is in a valid range
262 if (n > oldN) {
263 // get the name of the last x data point
264 std::string oldXName = "x" + std::to_string(oldN - 1);
265 double oldX = getAttribute(oldXName).asDouble();
266
267 // reallocate gsl object to new size
269
270 // create blank a number of new blank parameters and attributes
271 for (int i = oldN; i < n; ++i) {
272 std::string num = std::to_string(i);
273
274 std::string newXName = "x" + num;
275 std::string newYName = "y" + num;
276
277 declareAttribute(newXName, Attribute(oldX + static_cast<double>(i - oldN + 1)));
278 declareParameter(newYName, 0);
279 }
280
281 // flag that the spline + derivatives will now need to be recalculated
282 m_recalculateSpline = true;
283 } else if (n < oldN) {
284 throw std::invalid_argument("Cubic Spline: Can't decrease the number of attributes");
285 }
286 }
287
288 storeAttributeValue(attName, att);
289}
290
296void CubicSpline::setXAttribute(const size_t index, double x) {
297 size_t n = static_cast<size_t>(getAttribute("n").asInt());
298
299 // check that setting the x attribute is within our range
300 if (index < n) {
301 std::string xName = "x" + std::to_string(index);
302 setAttributeValue(xName, x);
303
304 // attributes updated, flag for recalculation
305 m_recalculateSpline = true;
306 } else {
307 throw std::range_error("Cubic Spline: x index out of range.");
308 }
309}
310
317void CubicSpline::checkGSLError(const int status, const int errorType) const {
318 // check GSL functions didn't return an error
319 if (status == errorType) {
320 m_recalculateSpline = true;
321
322 std::string message("CubicSpline: ");
323 message.append(gsl_strerror(errorType));
324
325 throw std::runtime_error(message);
326 }
327}
328
334 m_spline.reset(gsl_spline_alloc(gsl_interp_cspline, n));
335 gsl_interp_accel_reset(m_acc.get());
336}
337
338} // namespace Mantid::CurveFitting::Functions
double value
The value of the point.
Definition FitMW.cpp:51
#define DECLARE_FUNCTION(classname)
Macro for declaring a new type of function to be used with the FunctionFactory.
Mantid::API::IFunction::Attribute Attribute
std::map< DeltaEMode::Type, std::string > index
Attribute is a non-fitting parameter.
Definition IFunction.h:285
int asInt() const
Returns int value if attribute is a int, throws exception otherwise.
double asDouble() const
Returns double value if attribute is a double, throws exception otherwise.
virtual Attribute getAttribute(const std::string &name) const
Return a value of attribute attName.
void declareAttribute(const std::string &name, const API::IFunction::Attribute &defaultValue)
Declare a single attribute.
void setAttributeValue(const std::string &attName, const T &value)
Set an attribute value.
Definition IFunction.h:601
void storeAttributeValue(const std::string &name, const API::IFunction::Attribute &value)
Store an attribute's value.
void setParameter(size_t, const double &value, bool explicitlySet=true) override
Set i-th parameter.
void declareParameter(const std::string &name, double initValue=0, const std::string &description="") override
Declare a new parameter.
double getParameter(size_t i) const override
Get i-th parameter.
A wrapper around GSL functions implementing cubic spline interpolation.
Definition CubicSpline.h:29
bool m_recalculateSpline
Flag for checking if the spline needs recalculating.
Definition CubicSpline.h:59
void setParameter(size_t i, const double &value, bool explicitlySet=true) override
Set a parameter for the function and flags the spline for re-calculation.
bool checkXInRange(double x) const
Check if an x value falls within the range of the spline.
void setAttribute(const std::string &attName, const Attribute &) override
Set a value to attribute attName.
void reallocGSLObjects(const int n)
Reallocate the spline object to use n data points.
double splineEval(const double x) const
Evaluate a point on the spline, with basic error handling.
void setupInput(int const n) const
Method to setup the gsl function.
Mantid::Kernel::spline::accel_uptr m_acc
GSL interpolation accelerator object.
Definition CubicSpline.h:53
void setXAttribute(const size_t index, double x)
Set the value of a data point location to x.
void function1D(double *out, const double *xValues, const size_t nData) const override
Execute the function.
void checkGSLError(const int status, const int errorType) const
Check if an error occurred and throw appropriate message.
void calculateSpline(double *out, const double *xValues, const size_t nData) const
Calculate the spline.
Mantid::Kernel::spline::spline_uptr m_spline
GSL data structure used to calculate spline.
Definition CubicSpline.h:56
void calculateDerivative(double *out, const double *xValues, const size_t nData, const size_t order) const
Calculate the derivative.
void derivative1D(double *out, const double *xValues, size_t nData, const size_t order) const override
Calculate the derivatives for a set of points on the spline.
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::string to_string(const wide_integer< Bits, Signed > &n)