Mantid
Loading...
Searching...
No Matches
CalculatePolynomialBackground.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
19
20#include <utility>
21
22namespace {
24namespace Prop {
25const static std::string COST_FUNCTION = "CostFunction";
26const static std::string INPUT_WS = "InputWorkspace";
27const static std::string OUTPUT_WS = "OutputWorkspace";
28const static std::string POLY_DEGREE = "Degree";
29const static std::string XRANGES = "XRanges";
30const static std::string MINIMIZER = "Minimizer";
31const static std::string IGNORE_INVALID_DATA = "IgnoreInvalidData";
32} // namespace Prop
33
35namespace CostFunc {
36const static std::string UNWEIGHTED_LEAST_SQUARES = "Unweighted least squares";
37const static std::string WEIGHTED_LEAST_SQUARES = "Least squares";
38} // namespace CostFunc
39
41namespace Minimizer {
42const static std::string LEVENBERG_MARQUARDT_MD = "Levenberg-MarquardtMD";
43const static std::string LEVENBERG_MARQUARDT = "Levenberg-Marquardt";
44} // namespace Minimizer
45
52std::vector<double> filterRangesOutsideX(const std::vector<double> &ranges, const Mantid::API::MatrixWorkspace &ws,
53 const size_t wsIndex) {
54 const auto minX = ws.x(wsIndex).front();
55 const auto maxX = ws.x(wsIndex).back();
56 std::vector<double> filtered;
57 filtered.reserve(ranges.size());
58 for (size_t i = 0; i < ranges.size() / 2; ++i) {
59 const auto first = ranges[2 * i];
60 const auto second = ranges[2 * i + 1];
61 if (!(first > maxX || second < minX)) {
62 filtered.emplace_back(first);
63 filtered.emplace_back(second);
64 }
65 }
66 return filtered;
67}
68
75std::pair<double, double> totalRange(const std::vector<double> &ranges, const Mantid::API::MatrixWorkspace &ws,
76 const size_t wsIndex) {
77 const auto minX = ws.x(wsIndex).front();
78 const auto maxX = ws.x(wsIndex).back();
79 if (ranges.empty()) {
80 return std::pair<double, double>(minX, maxX);
81 }
82 const auto minmaxIt = std::minmax_element(ranges.cbegin(), ranges.cend());
83 const auto minEdge = *minmaxIt.first;
84 const auto maxEdge = *minmaxIt.second;
85 return std::pair<double, double>(std::min(minEdge, minX), std::max(maxEdge, maxX));
86}
87
93std::vector<double> includedRanges(const std::vector<double> &ranges, const std::pair<double, double> &totalRange) {
94 if (ranges.empty()) {
95 return {totalRange.first, totalRange.second};
96 }
97 // Sort the range edges keeping the information whether the edge
98 // 'starts' or 'ends' a range.
99 enum class Edge { start = -1, end = 1 };
100 std::vector<std::pair<double, Edge>> edges(ranges.size());
101 for (size_t i = 0; i < ranges.size(); ++i) {
102 edges[i].first = ranges[i];
103 edges[i].second = i % 2 == 0 ? Edge::start : Edge::end;
104 }
105 std::sort(edges.begin(), edges.end(), [](const std::pair<double, Edge> &p1, const std::pair<double, Edge> &p2) {
106 if (p1.first == p2.first)
107 return p1.second < p2.second;
108 return p1.first < p2.first;
109 });
110 // If an 'end' edge is followed by a 'start', we have a new range. Everything
111 // else can be merged.
112 std::vector<double> mergedRanges;
113 mergedRanges.reserve(ranges.size());
114 auto edgeIt = edges.begin();
115 mergedRanges.emplace_back(std::max(edges.front().first, totalRange.first));
116 while (edgeIt != edges.end()) {
117 auto endEdgeIt = edgeIt + 1;
118 while (endEdgeIt != edges.end()) {
119 const auto val = *endEdgeIt;
120 const auto prevVal = *(endEdgeIt - 1);
121 if (val.second == Edge::start && prevVal.second == Edge::end) {
122 mergedRanges.emplace_back(prevVal.first);
123 mergedRanges.emplace_back(val.first);
124 edgeIt = endEdgeIt;
125 break;
126 }
127 ++endEdgeIt;
128 }
129 ++edgeIt;
130 }
131 mergedRanges.emplace_back(std::min(edges.back().first, totalRange.second));
132 return mergedRanges;
133}
134
141std::vector<double> histogramRanges(const std::vector<double> &ranges, const Mantid::API::MatrixWorkspace &ws,
142 const size_t wsIndex) {
143 const auto filteredRanges = filterRangesOutsideX(ranges, ws, wsIndex);
144 if (!ranges.empty() && filteredRanges.empty()) {
145 throw std::runtime_error("The given XRanges mismatch with the histogram at workspace index " +
146 std::to_string(wsIndex));
147 }
148 const auto fullRange = totalRange(filteredRanges, ws, wsIndex);
149 return includedRanges(filteredRanges, fullRange);
150}
151
156std::vector<double> invertRanges(const std::vector<double> &ranges) {
157 std::vector<double> inversion(ranges.size() - 2);
158 for (size_t i = 1; i < ranges.size() - 1; ++i) {
159 inversion[i - 1] = ranges[i];
160 }
161 return inversion;
162}
163
174std::vector<double> executeFit(Mantid::API::Algorithm &fit, const std::string &function,
175 const Mantid::API::MatrixWorkspace_sptr &ws, const size_t wsIndex,
176 const std::vector<double> &ranges, const std::string &costFunction,
177 const std::string &minimizer, const bool &ignoreInvalidData) {
178 const auto fitRanges = histogramRanges(ranges, *ws, wsIndex);
179 const auto excludedRanges = invertRanges(fitRanges);
180 fit.setProperty("Function", function);
181 fit.setProperty("InputWorkspace", ws);
182 fit.setProperty("WorkspaceIndex", static_cast<int>(wsIndex));
183 fit.setProperty("StartX", fitRanges.front());
184 fit.setProperty("EndX", fitRanges.back());
185 fit.setProperty("Exclude", excludedRanges);
186 fit.setProperty("Minimizer", minimizer);
187 fit.setProperty(Prop::COST_FUNCTION, costFunction);
188 fit.setProperty(Prop::IGNORE_INVALID_DATA, ignoreInvalidData);
189 fit.setProperty("CreateOutput", true);
190 fit.executeAsChildAlg();
191 Mantid::API::ITableWorkspace_sptr fitResult = fit.getProperty("OutputParameters");
192 std::vector<double> parameters(fitResult->rowCount() - 1);
193 for (size_t row = 0; row < parameters.size(); ++row) {
194 parameters[row] = fitResult->cell<double>(row, 1);
195 }
196 return parameters;
197}
198
204std::string makeFunctionString(const std::string &name, const std::vector<double> &parameters) {
205 const auto degree = parameters.size() - 1;
206 std::ostringstream s;
207 s << "name=" << name;
208 if (degree > 2)
209 s << ",n=" << degree;
210 for (size_t d = 0; d <= degree; ++d) {
211 s << ',' << 'A' << d << '=' << parameters[d];
212 }
213 return s.str();
214}
215
220std::string makeNameString(const size_t degree) {
221 std::ostringstream name;
222 switch (degree) {
223 case 0:
224 name << "FlatBackground";
225 break;
226 case 1:
227 name << "LinearBackground";
228 break;
229 case 2:
230 name << "Quadratic";
231 break;
232 default:
233 name << "Polynomial";
234 }
235 return name.str();
236}
237
244void evaluateInPlace(const std::string &name, const std::vector<double> &parameters, Mantid::API::MatrixWorkspace &ws,
245 const size_t wsIndex) {
246 const auto degree = parameters.size() - 1;
247 auto bkg = std::dynamic_pointer_cast<Mantid::API::IFunction1D>(
248 Mantid::API::FunctionFactory::Instance().createFunction(name));
249 if (degree > 2) {
250 Mantid::API::IFunction1D::Attribute att = bkg->getAttribute("n");
251 att.fromString(std::to_string(degree));
252 bkg->setAttribute("n", att);
253 }
254 for (size_t d = 0; d <= degree; ++d) {
255 std::string param = 'A' + std::to_string(d);
256 bkg->setParameter(param, parameters[d]);
257 }
258 auto *y = const_cast<double *>(ws.mutableY(wsIndex).rawData().data());
259 bkg->function1D(y, ws.points(wsIndex).rawData().data(), ws.y(wsIndex).size());
260}
261} // namespace
262
263namespace Mantid::Algorithms {
264
265// Register the algorithm into the AlgorithmFactory
266DECLARE_ALGORITHM(CalculatePolynomialBackground)
267
268//----------------------------------------------------------------------------------------------
269
270
271const std::string CalculatePolynomialBackground::name() const { return "CalculatePolynomialBackground"; }
272
275
278 return "CorrectionFunctions\\BackgroundCorrections";
279}
280
282const std::string CalculatePolynomialBackground::summary() const {
283 return "Fits a polynomial background to a workspace.";
284}
285
286//----------------------------------------------------------------------------------------------
290 auto increasingAxis = std::make_shared<API::IncreasingAxisValidator>();
291 auto nonnegativeInt = std::make_shared<Kernel::BoundedValidator<int>>();
292 nonnegativeInt->setLower(0);
293 auto orderedPairs = std::make_shared<Kernel::ArrayOrderedPairsValidator<double>>();
295 Prop::INPUT_WS, "", Kernel::Direction::Input, increasingAxis),
296 "An input workspace.");
298 std::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>(Prop::OUTPUT_WS, "", Kernel::Direction::Output),
299 "A workspace containing the fitted background.");
300 declareProperty(Prop::POLY_DEGREE, 0, nonnegativeInt, "Degree of the fitted polynomial.");
301 declareProperty(std::make_unique<Kernel::ArrayProperty<double>>(Prop::XRANGES, std::vector<double>(), orderedPairs),
302 "A list of fitting ranges given as pairs of X values.");
303 std::array<std::string, 2> costFuncOpts{{CostFunc::WEIGHTED_LEAST_SQUARES, CostFunc::UNWEIGHTED_LEAST_SQUARES}};
304 declareProperty(Prop::COST_FUNCTION, CostFunc::WEIGHTED_LEAST_SQUARES,
305 std::make_shared<Kernel::ListValidator<std::string>>(costFuncOpts),
306 "The cost function to be passed to the Fit algorithm.");
307 std::array<std::string, 2> minimizerOpts{{Minimizer::LEVENBERG_MARQUARDT_MD, Minimizer::LEVENBERG_MARQUARDT}};
308 declareProperty(Prop::MINIMIZER, Minimizer::LEVENBERG_MARQUARDT_MD,
309 std::make_shared<Kernel::ListValidator<std::string>>(minimizerOpts),
310 "The minimizer to be passed to the Fit algorithm.");
311 declareProperty(Prop::IGNORE_INVALID_DATA, false, "Flag to ignore infinities, NaNs and data with zero errors.");
312}
313
314//----------------------------------------------------------------------------------------------
318 API::MatrixWorkspace_sptr inWS = getProperty(Prop::INPUT_WS);
319
320 API::MatrixWorkspace_sptr outWS{DataObjects::create<DataObjects::Workspace2D>(*inWS)};
321 const std::vector<double> inputRanges = getProperty(Prop::XRANGES);
322 const std::string costFunction = getProperty(Prop::COST_FUNCTION);
323 const bool ignoreInvalidData = getProperty(Prop::IGNORE_INVALID_DATA);
324 const std::string minimizer = getProperty(Prop::MINIMIZER);
325 const auto polyDegree = static_cast<size_t>(static_cast<int>(getProperty(Prop::POLY_DEGREE)));
326 const std::vector<double> initialParams(polyDegree + 1, 0.1);
327 const auto polyDegreeStr = makeNameString(polyDegree);
328 const auto fitFunction = makeFunctionString(polyDegreeStr, initialParams);
329 const auto nHistograms = static_cast<int64_t>(inWS->getNumberHistograms());
330 API::Progress progress(this, 0, 1.0, nHistograms);
331 PARALLEL_FOR_IF(Kernel::threadSafe(*inWS, *outWS))
332 for (int64_t i = 0; i < nHistograms; ++i) {
334 const bool logging{false};
335 auto fit = createChildAlgorithm("Fit", 0, 0, logging);
336 const auto parameters =
337 executeFit(*fit, fitFunction, inWS, i, inputRanges, costFunction, minimizer, ignoreInvalidData);
338 evaluateInPlace(polyDegreeStr, parameters, *outWS, i);
339 progress.report();
341 }
343
344 setProperty(Prop::OUTPUT_WS, outWS);
345}
346
347} // namespace Mantid::Algorithms
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
Base class from which all concrete algorithm classes should be derived.
Definition Algorithm.h:76
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.
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
void executeAsChildAlg() override
Execute as a Child Algorithm.
Attribute is a non-fitting parameter.
Definition IFunction.h:285
void fromString(const std::string &str)
Set value from a string.
Base MatrixWorkspace Abstract Class.
HistogramData::Points points(const size_t index) const
const HistogramData::HistogramX & x(const size_t index) const
HistogramData::HistogramY & mutableY(const size_t index) &
const HistogramData::HistogramY & y(const size_t index) const
Helper class for reporting progress from algorithms.
Definition Progress.h:25
A property class for workspaces.
CalculatePolynomialBackground : This algorithm fits a polynomial background to an input workspace and...
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
int version() const override
Algorithm's version for identification.
const std::string category() const override
Algorithm's category for identification.
void init() override
Initialize the algorithm's properties.
Support for a property that holds an array of values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
ListValidator is a validator that requires the value of a property to be one of a defined list of pos...
String constants for cost function options.
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
boost::graph_traits< PeakGraph >::edge_descriptor Edge
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
String constants for minimizer options.
String constants for algorithm's properties.
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