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 declareProperty("EndWorkspaceIndex", 0, mustBePositive, "The end index of the spectrum range for fitting.");
38}
39
43void SplineBackground::exec() {
44 API::MatrixWorkspace_sptr inWS = getProperty("InputWorkspace");
45 const int ncoeffs = getProperty("NCoeff");
46 const int spec = getProperty("WorkspaceIndex");
47 int specEnd = getProperty("EndWorkspaceIndex");
48
49 // Check if the user specified an End Workspace Index and if it is valid
50 if (specEnd) {
51 if (specEnd >= static_cast<int>(inWS->getNumberHistograms()))
52 throw std::out_of_range("EndWorkspaceIndex is out of range.");
53 } else {
54 specEnd = spec;
55 }
56 // Check the user specified Workspace Index is valid
57 if (spec >= static_cast<int>(inWS->getNumberHistograms()))
58 throw std::out_of_range("WorkspaceIndex is out of range.");
59
60 // Create Output Workspace
62 WorkspaceFactory::Instance().create(inWS, (specEnd - spec) + 1, inWS->x(spec).size(), inWS->y(spec).size());
63 for (int wsIndex = spec; wsIndex < (specEnd + 1); wsIndex++) {
64
65 /* this is the data to be fitted */
66 const auto numBins = static_cast<int>(calculateNumBinsToProcess(inWS.get(), wsIndex));
67 if (numBins < ncoeffs) {
68 throw std::out_of_range("Too many basis functions (NCoeff)");
69 }
70
71 allocateBSplinePointers(numBins, ncoeffs);
72 addWsDataToSpline(inWS.get(), wsIndex, numBins);
73
74 const auto &xVals = inWS->x(wsIndex);
75 setupSpline(xVals.front(), xVals.back(), numBins, ncoeffs);
76
77 // Wrap this in its own block so we can alias the member variable
78 // to a shorter name for the duration of this call
79 {
80 // Have to pass a pointer so it can write chi ^ 2 even though we don't
81 // use this value
82 double chisq;
83 // Create temporary alias to reduce the size of the call
85 // do the fit
86 gsl_multifit_wlinear(sp.fittedWs, sp.binWeights, sp.yData, sp.coefficients, sp.covariance, &chisq,
88 }
89 updateSplineOutput(inWS, outWS, spec, wsIndex);
91 }
92
93 setProperty("OutputWorkspace", outWS);
94}
95
102size_t SplineBackground::calculateNumBinsToProcess(const API::MatrixWorkspace *ws, const size_t currentWSIndex) {
103 const auto &yInputVals = ws->y(currentWSIndex);
104 size_t ySize = yInputVals.size();
105
106 // Check and subtract masked bins from the count
107 bool isMasked = ws->hasMaskedBins(currentWSIndex);
108 if (isMasked) {
109 ySize -= static_cast<int>(ws->maskedBins(currentWSIndex).size());
110 }
111 return ySize;
112}
113
122void SplineBackground::addWsDataToSpline(const API::MatrixWorkspace *ws, const size_t specNum, int expectedNumBins) {
123 const auto xPointData = ws->points(specNum);
124 const auto &yInputVals = ws->y(specNum);
125 const auto &eInputVals = ws->e(specNum);
126 const bool hasMaskedBins = ws->hasMaskedBins(specNum);
127
128 // Mark masked bins to skip them later
129 std::vector<bool> masked(yInputVals.size(), false);
130 if (hasMaskedBins) {
131 const auto &maskedBinsMap = ws->maskedBins(specNum);
132 for (const auto &maskedBin : maskedBinsMap) {
133 masked[maskedBin.first] = true;
134 }
135 }
136
137 int numUnmaskedBins = 0;
138 for (size_t i = 0; i < yInputVals.size(); ++i) {
139 if (hasMaskedBins && masked[i])
140 continue;
141 gsl_vector_set(m_splinePointers.xData, numUnmaskedBins, xPointData[i]);
142 gsl_vector_set(m_splinePointers.yData, numUnmaskedBins, yInputVals[i]);
143 gsl_vector_set(m_splinePointers.binWeights, numUnmaskedBins, calculateBinWeight(eInputVals[i]));
144
145 ++numUnmaskedBins;
146 }
147
148 if (expectedNumBins != numUnmaskedBins) {
150 throw std::runtime_error("Assertion failed: number of unmasked bins found was"
151 " not equal to the number that was calculated");
152 }
153}
154
162void SplineBackground::allocateBSplinePointers(int numBins, int ncoeffs) {
163 const int k = 4; // order of the spline + 1 (cubic)
164 const int nbreak = ncoeffs - (k - 2);
165
166 if (nbreak <= 0)
167 throw std::out_of_range("Too low NCoeff");
168
169 // Create an alias to tidy it up
171 // allocate a cubic bspline workspace (k = 4)
172 sp.splineToProcess = gsl_bspline_alloc(k, nbreak);
173 sp.inputSplineWs = gsl_vector_alloc(ncoeffs);
174
175 sp.xData = gsl_vector_alloc(numBins);
176 sp.yData = gsl_vector_alloc(numBins);
177 sp.fittedWs = gsl_matrix_alloc(numBins, ncoeffs);
178 sp.coefficients = gsl_vector_alloc(ncoeffs);
179 sp.binWeights = gsl_vector_alloc(numBins);
180 sp.covariance = gsl_matrix_alloc(ncoeffs, ncoeffs);
181 sp.weightedLinearFitWs = gsl_multifit_linear_alloc(numBins, ncoeffs);
182}
183
193double SplineBackground::calculateBinWeight(double errValue) {
194 double outBinWeight = -1;
195 if (errValue <= 0 || !std::isfinite(errValue)) {
196 // Regardless of which warning we print we should
197 // set the bin weight
198 outBinWeight = 0;
199 if (g_log.is(Kernel::Logger::Priority::PRIO_WARNING)) {
200 if (errValue <= 0) {
201 g_log.warning("Spline background found an error value of 0 or less on an unmasked"
202 " bin. This bin will have no weight during the fitting process");
203 } else {
204 // nan / inf
205 g_log.warning("Spline background found an error value of nan or inf on an"
206 " unmasked bin. This bin will have no weight during the fitting"
207 " process");
208 }
209 }
210 } else {
211 // Value is perfectly normal in every way
212 outBinWeight = 1. / (errValue * errValue);
213 }
214 return outBinWeight;
215}
216
220void SplineBackground::freeBSplinePointers() {
221 // Create an alias to tidy it up
223
224 gsl_bspline_free(sp.splineToProcess);
225 sp.splineToProcess = nullptr;
226 gsl_vector_free(sp.inputSplineWs);
227 sp.inputSplineWs = nullptr;
228 gsl_vector_free(sp.xData);
229 sp.xData = nullptr;
230 gsl_vector_free(sp.yData);
231 sp.yData = nullptr;
232 gsl_matrix_free(sp.fittedWs);
233 sp.fittedWs = nullptr;
234 gsl_vector_free(sp.coefficients);
235 sp.coefficients = nullptr;
236 gsl_vector_free(sp.binWeights);
237 sp.binWeights = nullptr;
238 gsl_matrix_free(sp.covariance);
239 sp.covariance = nullptr;
240 gsl_multifit_linear_free(sp.weightedLinearFitWs);
241 sp.weightedLinearFitWs = nullptr;
242}
243
253void SplineBackground::updateSplineOutput(const API::MatrixWorkspace_sptr &inputWS, API::MatrixWorkspace_sptr &outputWS,
254 const size_t startWSIndex, const size_t currentIndexOfInputWS) {
255 // Update Output Workspace with Spline fitted values
256 const auto &xInputVals = inputWS->x(currentIndexOfInputWS);
257 size_t currentIndexOfOutputWS = currentIndexOfInputWS - startWSIndex;
258
259 outputWS->getSpectrum(currentIndexOfOutputWS)
260 .setSpectrumNo(inputWS->getSpectrum(currentIndexOfInputWS).getSpectrumNo());
261
262 double yi, yerr;
263 auto &yVals = outputWS->mutableY(currentIndexOfOutputWS);
264 auto &eVals = outputWS->mutableE(currentIndexOfOutputWS);
265
266 for (size_t i = 0; i < inputWS->y(currentIndexOfInputWS).size(); i++) {
267 double xi = xInputVals[i];
270 &yi, &yerr);
271 yVals[i] = yi;
272 eVals[i] = yerr;
273 }
274 outputWS->setSharedX(currentIndexOfOutputWS, inputWS->sharedX(currentIndexOfInputWS));
275}
276
285void SplineBackground::setupSpline(double xMin, double xMax, int numBins, int ncoeff) {
286 /* use uniform breakpoints */
287 gsl_bspline_knots_uniform(xMin, xMax, m_splinePointers.splineToProcess);
288
289 /* construct the fit matrix X */
290 for (int i = 0; i < numBins; ++i) {
291 double xi = gsl_vector_get(m_splinePointers.xData, i);
292
293 /* compute B_j(xi) for all j */
295
296 /* fill in row i of X */
297 for (int j = 0; j < ncoeff; ++j) {
298 double Bj = gsl_vector_get(m_splinePointers.inputSplineWs, j);
299 gsl_matrix_set(m_splinePointers.fittedWs, i, j, Bj);
300 }
301 }
302}
303
304} // namespace Mantid::CurveFitting::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
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.
Kernel::Logger & g_log
Definition Algorithm.h:422
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.
void allocateBSplinePointers(int numBins, int ncoeffs)
Allocates various pointers used within GSL.
void freeBSplinePointers()
Deallocates various pointers within GSL.
void updateSplineOutput(const API::MatrixWorkspace_sptr &inputWS, API::MatrixWorkspace_sptr &outputWS, const size_t spec, const size_t wsIndex)
Gets the values from the fitted GSL, and creates a clone of input workspace with new values.
void setupSpline(double xMin, double xMax, int numBins, int ncoeff)
Sets up the splines for later fitting.
size_t calculateNumBinsToProcess(const API::MatrixWorkspace *ws, const size_t currentWSIndex)
Calculates the number on unmasked bins to process.
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:117
bool is(int level) const
Returns true if at least the given log level is set.
Definition Logger.cpp:177
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