Mantid
Loading...
Searching...
No Matches
SplineBackground.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//----------------------------------------------------------------------
15
16#include <gsl/gsl_bspline.h>
17#include <gsl/gsl_multifit.h>
18#include <gsl/gsl_statistics.h>
19
21
22DECLARE_ALGORITHM(SplineBackground)
23
24using namespace Kernel;
25using namespace API;
26
28void SplineBackground::init() {
29 declareProperty(std::make_unique<WorkspaceProperty<API::MatrixWorkspace>>("InputWorkspace", "", Direction::Input),
30 "The name of the input workspace.");
31 declareProperty(std::make_unique<WorkspaceProperty<API::MatrixWorkspace>>("OutputWorkspace", "", Direction::Output),
32 "The name to use for the output workspace.");
33 auto mustBePositive = std::make_shared<BoundedValidator<int>>();
34 mustBePositive->setLower(0);
35 declareProperty("WorkspaceIndex", 0, mustBePositive, "The index of the spectrum for fitting.");
36 declareProperty("NCoeff", 10, "The number of b-spline coefficients.");
37}
38
42void SplineBackground::exec() {
43 API::MatrixWorkspace_sptr inWS = getProperty("InputWorkspace");
44 const int ncoeffs = getProperty("NCoeff");
45 const int spec = getProperty("WorkspaceIndex");
46
47 // Check if the user specified a spectrum number its valid
48 if (spec > static_cast<int>(inWS->getNumberHistograms()))
49 throw std::out_of_range("WorkspaceIndex is out of range.");
50
51 /* this is the data to be fitted */
52 const auto numBins = static_cast<int>(calculateNumBinsToProcess(inWS.get()));
53
54 if (numBins < ncoeffs) {
55 throw std::out_of_range("Too many basis functions (NCoeff)");
56 }
57
58 allocateBSplinePointers(numBins, ncoeffs);
59 addWsDataToSpline(inWS.get(), spec, numBins);
60
61 const auto &xVals = inWS->x(spec);
62 setupSpline(xVals.front(), xVals.back(), numBins, ncoeffs);
63
64 // Wrap this in its own block so we can alias the member variable
65 // to a shorter name for the duration of this call
66 {
67 // Have to pass a pointer so it can write chi ^ 2 even though we don't
68 // use this value
69 double chisq;
70 // Create temporary alias to reduce the size of the call
72 // do the fit
73 gsl_multifit_wlinear(sp.fittedWs, sp.binWeights, sp.yData, sp.coefficients, sp.covariance, &chisq,
75 }
76
77 auto outWS = saveSplineOutput(inWS, spec);
79 setProperty("OutputWorkspace", outWS);
80}
81
87size_t SplineBackground::calculateNumBinsToProcess(const API::MatrixWorkspace *ws) {
88 const int spec = getProperty("WorkspaceIndex");
89 const auto &yInputVals = ws->y(spec);
90 size_t ySize = yInputVals.size();
91
92 // Check and subtract masked bins from the count
93 bool isMasked = ws->hasMaskedBins(spec);
94 if (isMasked) {
95 ySize -= static_cast<int>(ws->maskedBins(spec).size());
96 }
97 return ySize;
98}
99
108void SplineBackground::addWsDataToSpline(const API::MatrixWorkspace *ws, const size_t specNum, int expectedNumBins) {
109 const auto xPointData = ws->points(specNum);
110 const auto &yInputVals = ws->y(specNum);
111 const auto &eInputVals = ws->e(specNum);
112 const bool hasMaskedBins = ws->hasMaskedBins(specNum);
113
114 // Mark masked bins to skip them later
115 std::vector<bool> masked(yInputVals.size(), false);
116 if (hasMaskedBins) {
117 const auto &maskedBinsMap = ws->maskedBins(specNum);
118 for (const auto &maskedBin : maskedBinsMap) {
119 masked[maskedBin.first] = true;
120 }
121 }
122
123 int numUnmaskedBins = 0;
124 for (size_t i = 0; i < yInputVals.size(); ++i) {
125 if (hasMaskedBins && masked[i])
126 continue;
127 gsl_vector_set(m_splinePointers.xData, numUnmaskedBins, xPointData[i]);
128 gsl_vector_set(m_splinePointers.yData, numUnmaskedBins, yInputVals[i]);
129 gsl_vector_set(m_splinePointers.binWeights, numUnmaskedBins, calculateBinWeight(eInputVals[i]));
130
131 ++numUnmaskedBins;
132 }
133
134 if (expectedNumBins != numUnmaskedBins) {
136 throw std::runtime_error("Assertion failed: number of unmasked bins found was"
137 " not equal to the number that was calculated");
138 }
139}
140
148void SplineBackground::allocateBSplinePointers(int numBins, int ncoeffs) {
149 const int k = 4; // order of the spline + 1 (cubic)
150 const int nbreak = ncoeffs - (k - 2);
151
152 if (nbreak <= 0)
153 throw std::out_of_range("Too low NCoeff");
154
155 // Create an alias to tidy it up
157 // allocate a cubic bspline workspace (k = 4)
158 sp.splineToProcess = gsl_bspline_alloc(k, nbreak);
159 sp.inputSplineWs = gsl_vector_alloc(ncoeffs);
160
161 sp.xData = gsl_vector_alloc(numBins);
162 sp.yData = gsl_vector_alloc(numBins);
163 sp.fittedWs = gsl_matrix_alloc(numBins, ncoeffs);
164 sp.coefficients = gsl_vector_alloc(ncoeffs);
165 sp.binWeights = gsl_vector_alloc(numBins);
166 sp.covariance = gsl_matrix_alloc(ncoeffs, ncoeffs);
167 sp.weightedLinearFitWs = gsl_multifit_linear_alloc(numBins, ncoeffs);
168}
169
179double SplineBackground::calculateBinWeight(double errValue) {
180 double outBinWeight = -1;
181 if (errValue <= 0 || !std::isfinite(errValue)) {
182 // Regardless of which warning we print we should
183 // set the bin weight
184 outBinWeight = 0;
185 if (errValue <= 0) {
186 g_log.warning("Spline background found an error value of 0 or less on an unmasked"
187 " bin. This bin will have no weight during the fitting process");
188 } else {
189 // nan / inf
190 g_log.warning("Spline background found an error value of nan or inf on an"
191 " unmasked bin. This bin will have no weight during the fitting"
192 " process");
193 }
194 } else {
195 // Value is perfectly normal in every way
196 outBinWeight = 1. / (errValue * errValue);
197 }
198 return outBinWeight;
199}
200
204void SplineBackground::freeBSplinePointers() {
205 // Create an alias to tidy it up
207
208 gsl_bspline_free(sp.splineToProcess);
209 sp.splineToProcess = nullptr;
210 gsl_vector_free(sp.inputSplineWs);
211 sp.inputSplineWs = nullptr;
212 gsl_vector_free(sp.xData);
213 sp.xData = nullptr;
214 gsl_vector_free(sp.yData);
215 sp.yData = nullptr;
216 gsl_matrix_free(sp.fittedWs);
217 sp.fittedWs = nullptr;
218 gsl_vector_free(sp.coefficients);
219 sp.coefficients = nullptr;
220 gsl_vector_free(sp.binWeights);
221 sp.binWeights = nullptr;
222 gsl_matrix_free(sp.covariance);
223 sp.covariance = nullptr;
224 gsl_multifit_linear_free(sp.weightedLinearFitWs);
225 sp.weightedLinearFitWs = nullptr;
226}
227
237MatrixWorkspace_sptr SplineBackground::saveSplineOutput(const API::MatrixWorkspace_sptr &ws, const size_t spec) {
238 const auto &xInputVals = ws->x(spec);
239 const auto &yInputVals = ws->y(spec);
240 /* output the smoothed curve */
241 API::MatrixWorkspace_sptr outWS = WorkspaceFactory::Instance().create(ws, 1, xInputVals.size(), yInputVals.size());
242
243 outWS->getSpectrum(0).setSpectrumNo(ws->getSpectrum(spec).getSpectrumNo());
244
245 double yi, yerr;
246 auto &yVals = outWS->mutableY(0);
247 auto &eVals = outWS->mutableE(0);
248
249 for (size_t i = 0; i < yInputVals.size(); i++) {
250 double xi = xInputVals[i];
253 &yi, &yerr);
254 yVals[i] = yi;
255 eVals[i] = yerr;
256 }
257 outWS->setSharedX(0, ws->sharedX(0));
258 return outWS;
259}
260
269void SplineBackground::setupSpline(double xMin, double xMax, int numBins, int ncoeff) {
270 /* use uniform breakpoints */
271 gsl_bspline_knots_uniform(xMin, xMax, m_splinePointers.splineToProcess);
272
273 /* construct the fit matrix X */
274 for (int i = 0; i < numBins; ++i) {
275 double xi = gsl_vector_get(m_splinePointers.xData, i);
276
277 /* compute B_j(xi) for all j */
279
280 /* fill in row i of X */
281 for (int j = 0; j < ncoeff; ++j) {
282 double Bj = gsl_vector_get(m_splinePointers.inputSplineWs, j);
283 gsl_matrix_set(m_splinePointers.fittedWs, i, j, Bj);
284 }
285 }
286}
287
288} // namespace Mantid::CurveFitting::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
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
Kernel::Logger & g_log
Definition: Algorithm.h:451
Base MatrixWorkspace Abstract Class.
const MaskList & maskedBins(const size_t &workspaceIndex) const
Returns the list of masked bins for a spectrum.
const HistogramData::HistogramE & e(const size_t index) const
HistogramData::Points points(const size_t index) const
bool hasMaskedBins(const size_t &workspaceIndex) const
Does this spectrum contain any masked bins.
const HistogramData::HistogramY & y(const size_t index) const
A property class for workspaces.
void addWsDataToSpline(const API::MatrixWorkspace *ws, const size_t specNum, int expectedNumBins)
Adds data from the workspace to the GSL vectors for later processing.
API::MatrixWorkspace_sptr saveSplineOutput(const API::MatrixWorkspace_sptr &ws, const size_t spec)
Gets the values from the fitted GSL, and creates a clone of input workspace with new values.
void allocateBSplinePointers(int numBins, int ncoeffs)
Allocates various pointers used within GSL.
size_t calculateNumBinsToProcess(const API::MatrixWorkspace *ws)
Calculates the number on unmasked bins to process.
void freeBSplinePointers()
Deallocates various pointers within GSL.
void setupSpline(double xMin, double xMax, int numBins, int ncoeff)
Sets up the splines for later fitting.
double calculateBinWeight(double errValue)
Calculates the bin weight using the error values in the WS.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
Struct holding various pointers required by GSL.
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54