Mantid
Loading...
Searching...
No Matches
DirectILLTubeBackground.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
15#include "MantidHistogramData/HistogramIterator.h"
16#include "MantidHistogramData/LinearGenerator.h"
19
20namespace {
22namespace Prop {
23const std::string COMPONENTS{"Components"};
24const std::string DIAGNOSTICS_WS{"DiagnosticsWorkspace"};
25const std::string EPP_WS{"EPPWorkspace"};
26const std::string INPUT_WS{"InputWorkspace"};
27const std::string OUTPUT_WS{"OutputWorkspace"};
28const std::string POLYNOMIAL_DEGREE{"Degree"};
29const std::string SIGMA_MULTIPLIER{"NonBkgRegionInSigmas"};
30const std::string IGNORE_INVALID_DATA{"IgnoreInvalidData"};
31
32} // namespace Prop
33
40std::vector<double> bkgFittingRanges(Mantid::API::MatrixWorkspace const &ws, Mantid::API::Column const &statuses,
41 size_t const firstColumnIndex) {
42 std::vector<double> ranges;
43 auto const &spectrumInfo = ws.spectrumInfo();
44 bool needsRangeStart{true};
45 bool needsRangeEnd{false};
46 for (size_t i = 0; i < ws.getNumberHistograms(); ++i) {
47 if (spectrumInfo.isMasked(i) || statuses.cell<std::string>(i + firstColumnIndex) != "success") {
48 if (needsRangeEnd) {
49 needsRangeStart = true;
50 needsRangeEnd = false;
51 // Current spectrum number is the first to exclude.
52 ranges.emplace_back(static_cast<double>(i) - 0.5);
53 }
54 continue;
55 }
56 if (needsRangeStart) {
57 needsRangeStart = false;
58 needsRangeEnd = true;
59 ranges.emplace_back(static_cast<double>(i) - 0.5);
60 }
61 }
62 if (needsRangeEnd) {
63 ranges.emplace_back(static_cast<double>(ws.getNumberHistograms()) - 0.5);
64 }
65 return ranges;
66}
67
69struct PeakBounds {
71 std::vector<double> peakStarts;
73 std::vector<double> peakEnds;
74};
75
86PeakBounds peakBounds(size_t const firstIndex, size_t const lastIndex, double const sigmaMultiplier,
87 Mantid::API::Column const &peakCentreColumn, Mantid::API::Column const &sigmaColumn,
88 Mantid::API::Column const &fitStatusColumn) {
89 PeakBounds bounds;
90 size_t const boundsSize = lastIndex - firstIndex + 1;
91 bounds.peakStarts.resize(boundsSize);
92 bounds.peakEnds.resize(boundsSize);
93 for (size_t i = firstIndex; i <= lastIndex; ++i) {
94 auto const boundsIndex = i - firstIndex;
95 if (fitStatusColumn.cell<std::string>(i) == "success") {
96 auto const peakCentre = peakCentreColumn.cell<double>(i);
97 auto const peakWidth = sigmaMultiplier * sigmaColumn.cell<double>(i);
98 bounds.peakStarts[boundsIndex] = peakCentre - peakWidth;
99 bounds.peakEnds[boundsIndex] = peakCentre + peakWidth;
100 } else {
101 bounds.peakStarts[boundsIndex] = std::numeric_limits<double>::lowest();
102 bounds.peakEnds[boundsIndex] = std::numeric_limits<double>::max();
103 }
104 }
105 return bounds;
106}
107
115void checkEPPColumnsExist(Mantid::API::Column_sptr const &centreColumn, Mantid::API::Column_sptr const &sigmaColumn,
116 Mantid::API::Column_sptr const &statusColumn) {
117 using namespace Mantid::Kernel::Exception;
118 if (!centreColumn) {
119 throw NotFoundError("EPPWorkspace does not contain 'PeakCentre' column.", "PeakCentre");
120 }
121 if (!sigmaColumn) {
122 throw NotFoundError("EPPWorkspace does not contain 'Sigma' column", "Sigma");
123 }
124 if (!statusColumn) {
125 throw NotFoundError("EPPWorkspace does not contain 'FitStatus' column.", "FitStatus");
126 }
127}
128
135void checkComponentExists(std::string const &componentName, Mantid::Geometry::Instrument const &instrument) {
136 using namespace Mantid::Kernel::Exception;
137 auto const component = instrument.getComponentByName(componentName);
138 if (!component) {
139 throw NotFoundError("Component not found in InputWorkspace's instrument.", componentName);
140 }
141}
142
144struct Range {
146 size_t first{0};
148 size_t last{0};
149};
150
157Range componentWSIndexRange(Mantid::API::MatrixWorkspace const &componentWS,
158 Mantid::API::MatrixWorkspace const &originalWS) {
159 Range range;
160 auto const firstComponentSpectrumNo = componentWS.getSpectrum(0).getSpectrumNo();
161 range.first = originalWS.getIndexFromSpectrumNumber(firstComponentSpectrumNo);
162 auto const nComponentHistograms = componentWS.getNumberHistograms();
163 auto const lastComponentSpectrumNo = componentWS.getSpectrum(nComponentHistograms - 1).getSpectrumNo();
164 range.last = originalWS.getIndexFromSpectrumNumber(lastComponentSpectrumNo);
165 return range;
166}
167
174void writeComponentBackgroundToOutput(Mantid::API::MatrixWorkspace const &componentBkgWS,
175 Mantid::API::MatrixWorkspace &targetWS, size_t const firstTargetWSIndex) {
176 auto const &ys = componentBkgWS.y(0);
177 auto const &es = componentBkgWS.e(0);
178 for (size_t i = 0; i < ys.size(); ++i) {
179 targetWS.mutableY(firstTargetWSIndex + i) = ys[i];
180 targetWS.mutableE(firstTargetWSIndex + i) = es[i];
181 }
182}
183
184} // namespace
185
186namespace Mantid::Algorithms {
187
188// Register the algorithm into the AlgorithmFactory
189DECLARE_ALGORITHM(DirectILLTubeBackground)
190
191
192const std::string DirectILLTubeBackground::name() const { return "DirectILLTubeBackground"; }
193
195int DirectILLTubeBackground::version() const { return 1; }
196
198const std::string DirectILLTubeBackground::category() const {
199 return "CorrectionFunctions\\BackgroundCorrections;ILL\\Direct";
200}
201
203const std::vector<std::string> DirectILLTubeBackground::seeAlso() const {
204 return {"CalculateFlatBackground", "CalculatePolynomialBackground", "CreateUserDefinedBackground", "RemoveBackground",
205 "SplineBackground"};
206}
207
209const std::string DirectILLTubeBackground::summary() const {
210 return "Fits polynomial backgrounds over the pixels of position sensitive "
211 "tubes.";
212}
213
218 std::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>(Prop::INPUT_WS, "", Kernel::Direction::Input),
219 "A workspace to fit the backgrounds to.");
221 std::make_unique<API::WorkspaceProperty<API::MatrixWorkspace>>(Prop::OUTPUT_WS, "", Kernel::Direction::Output),
222 "The fitted backgrounds.");
223 declareProperty(std::make_unique<Kernel::ArrayProperty<std::string>>(Prop::COMPONENTS, std::vector<std::string>()),
224 "A list of component names for which to calculate the backgrounds.");
227 "A table workspace containing results from the FindEPP algorithm.");
228 auto positiveDouble = std::make_shared<Kernel::BoundedValidator<double>>();
229 positiveDouble->setLowerExclusive(0.);
230 declareProperty(Prop::SIGMA_MULTIPLIER, 6., positiveDouble,
231 "Half width of the range excluded from background around "
232 "the elastic peaks in multiplies of 'Sigma' in the EPP table.'.");
233 auto nonnegativeInt = std::make_shared<Kernel::BoundedValidator<int>>();
234 nonnegativeInt->setLower(0);
235 declareProperty(Prop::POLYNOMIAL_DEGREE, 0, nonnegativeInt, "The degree of the background polynomial.");
237 Prop::DIAGNOSTICS_WS, "", Kernel::Direction::Input, API::PropertyMode::Optional),
238 "Detector diagnostics workspace for masking.");
239 declareProperty(Prop::IGNORE_INVALID_DATA, false, "Flag to ignore infinities, NaNs and data with zero errors.");
240}
241
243std::map<std::string, std::string> DirectILLTubeBackground::validateInputs() {
244 std::map<std::string, std::string> issues;
245 API::MatrixWorkspace_sptr inWS = getProperty(Prop::INPUT_WS);
246 API::ITableWorkspace_sptr eppWS = getProperty(Prop::EPP_WS);
247 if (inWS->getNumberHistograms() != eppWS->rowCount()) {
248 issues[Prop::EPP_WS] = "Wrong EPP workspace? The number of the table rows "
249 "does not match the number of histograms in InputWorkspace.";
250 }
251 if (!isDefault(Prop::DIAGNOSTICS_WS)) {
252 API::MatrixWorkspace_sptr maskWS = getProperty(Prop::DIAGNOSTICS_WS);
253 if (inWS->getNumberHistograms() != maskWS->getNumberHistograms()) {
254 issues[Prop::EPP_WS] = "Wrong diagnostics workspace? The number of histograms "
255 "does not match with InputWorkspace.";
256 }
257 }
258 return issues;
259}
260
264 API::MatrixWorkspace_sptr inWS = getProperty(Prop::INPUT_WS);
265 auto ws = applyDiagnostics(inWS->clone());
266 API::MatrixWorkspace_sptr bkgWS = DataObjects::create<DataObjects::Workspace2D>(*ws);
267 for (size_t i = 0; i < bkgWS->getNumberHistograms(); ++i) {
268 bkgWS->convertToFrequencies(i);
269 }
270 API::ITableWorkspace_sptr eppWS = getProperty(Prop::EPP_WS);
271 double const sigmaMultiplier = getProperty(Prop::SIGMA_MULTIPLIER);
272 auto peakCentreColumn = eppWS->getColumn("PeakCentre");
273 auto sigmaColumn = eppWS->getColumn("Sigma");
274 auto fitStatusColumn = eppWS->getColumn("FitStatus");
275 checkEPPColumnsExist(peakCentreColumn, sigmaColumn, fitStatusColumn);
276 auto instrument = ws->getInstrument();
277 std::vector<std::string> const componentNames = components(*instrument);
278 API::Progress progress(this, 0.0, 1.0, componentNames.size());
280 for (int64_t i = 0; i < static_cast<int64_t>(componentNames.size()); ++i) {
282 auto const &componentName = componentNames[static_cast<size_t>(i)];
283 progress.report("Processing " + componentName);
284 checkComponentExists(componentName, *instrument);
285 auto componentWS = cropToComponent(ws, componentName);
286 auto const wsIndexRange = componentWSIndexRange(*componentWS, *ws);
287 auto const bkgRanges = bkgFittingRanges(*componentWS, *fitStatusColumn, wsIndexRange.first);
288 if (bkgRanges.empty()) {
289 continue;
290 }
291 auto const bounds = peakBounds(wsIndexRange.first, wsIndexRange.last, sigmaMultiplier, *peakCentreColumn,
292 *sigmaColumn, *fitStatusColumn);
293 auto averageWS = peakExcludingAverage(*componentWS, bounds.peakStarts, bounds.peakEnds);
294 auto fittedComponentBkg = fitComponentBackground(averageWS, bkgRanges);
295 writeComponentBackgroundToOutput(*fittedComponentBkg, *bkgWS, wsIndexRange.first);
297 }
299 if (!ws->isDistribution()) {
300 for (size_t i = 0; i < bkgWS->getNumberHistograms(); ++i) {
301 bkgWS->convertToCounts(i);
302 }
303 }
304 setProperty(Prop::OUTPUT_WS, bkgWS);
305}
306
313 if (isDefault(Prop::DIAGNOSTICS_WS)) {
314 return ws;
315 }
316 API::MatrixWorkspace_sptr diagnosticsWS = getProperty(Prop::DIAGNOSTICS_WS);
317 auto mask = createChildAlgorithm("MaskDetectors");
318 mask->setProperty("Workspace", ws);
319 mask->setProperty("MaskedWorkspace", diagnosticsWS);
320 mask->execute();
321 return ws;
322}
323
330std::vector<std::string> DirectILLTubeBackground::components(Geometry::Instrument const &instrument) {
331 if (isDefault(Prop::COMPONENTS)) {
332 std::string const COMPONENTS_PARAMETER{"components-for-backgrounds"};
333 if (!instrument.hasParameter(COMPONENTS_PARAMETER)) {
334 throw std::runtime_error("Could not find '" + COMPONENTS_PARAMETER +
335 "' in instrument parameters file. Component "
336 "names must be given using the '" +
337 Prop::COMPONENTS + "' property.");
338 }
339 auto const componentList = instrument.getStringParameter(COMPONENTS_PARAMETER).front();
340 setPropertyValue(Prop::COMPONENTS, componentList);
341 }
342 return getProperty(Prop::COMPONENTS);
343}
344
352 std::string const &componentName) {
353 auto crop = createChildAlgorithm("CropToComponent");
354 crop->setProperty("InputWorkspace", ws);
355 crop->setProperty("OutputWorkspace", "_unused");
356 crop->setProperty("ComponentNames", std::vector<std::string>{componentName});
357 crop->execute();
358 return crop->getProperty("OutputWorkspace");
359}
360
368 std::vector<double> const &xRanges) {
369 int const degree = getProperty(Prop::POLYNOMIAL_DEGREE);
370 auto calculateBkg = createChildAlgorithm("CalculatePolynomialBackground");
371 calculateBkg->setProperty("InputWorkspace", ws);
372 calculateBkg->setProperty("OutputWorkspace", "_unused");
373 calculateBkg->setProperty("Degree", degree);
374 calculateBkg->setProperty("XRanges", xRanges);
375 calculateBkg->setProperty("CostFunction", "Unweighted least squares");
376 calculateBkg->setProperty("IgnoreInvalidData", getPropertyValue(Prop::IGNORE_INVALID_DATA));
377
378 calculateBkg->execute();
379 return calculateBkg->getProperty("OutputWorkspace");
380}
381
390 std::vector<double> const &peakStarts,
391 std::vector<double> const &peakEnds) {
392 HistogramData::Points wsIndices{ws.getNumberHistograms(), HistogramData::LinearGenerator(0., 1.)};
393 // zeroCounts actually holds the mean frequencies but since its
394 // point data the type has to be Counts.
395 HistogramData::Counts zeroCounts(ws.getNumberHistograms(), 0.);
396 HistogramData::Histogram modelHistogram{wsIndices, zeroCounts};
397 API::MatrixWorkspace_sptr averageWS = DataObjects::create<DataObjects::Workspace2D>(1, modelHistogram);
398 for (size_t i = 0; i < ws.getNumberHistograms(); ++i) {
399 auto const peakStart = peakStarts[i];
400 auto const peakEnd = peakEnds[i];
401 auto histogram = ws.histogram(i);
402 size_t itemCount{0};
403 double &sum = averageWS->mutableY(0)[i];
404 double &error = averageWS->mutableE(0)[i];
405 for (auto &histogramItem : ws.histogram(i)) {
406 auto const center = histogramItem.center();
407 if (peakStart <= center && center <= peakEnd) {
408 continue;
409 }
410 sum += histogramItem.frequency();
411 auto const stdDev = histogramItem.frequencyStandardDeviation();
412 error = std::sqrt(error * error + stdDev * stdDev);
413 ++itemCount;
414 }
415 if (itemCount != 0) {
416 sum /= static_cast<double>(itemCount);
417 error /= static_cast<double>(itemCount);
418 }
419 }
420 return averageWS;
421}
422
423} // namespace Mantid::Algorithms
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double error
#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.
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
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.
bool isDefault(const std::string &name) const
void setPropertyValue(const std::string &name, const std::string &value) override
Set the value of a property by string N.B.
Column is the base class for columns of TableWorkspace.
Definition Column.h:35
T & cell(size_t index)
Templated method for returning a value. No type checks are done.
Definition Column.h:127
const SpectrumInfo & spectrumInfo() const
Return a reference to the SpectrumInfo object.
Geometry::Instrument_const_sptr getInstrument() const
Returns the parameterized instrument.
specnum_t getSpectrumNo() const
Base MatrixWorkspace Abstract Class.
virtual ISpectrum & getSpectrum(const size_t index)=0
Return the underlying ISpectrum ptr at the given workspace index.
const HistogramData::HistogramE & e(const size_t index) const
virtual std::size_t getNumberHistograms() const =0
Returns the number of histograms in the workspace.
bool isDistribution() const
Are the Y-values dimensioned?
HistogramData::Histogram histogram(const size_t index) const
Returns the Histogram at the given workspace index.
HistogramData::HistogramE & mutableE(const size_t index) &
size_t getIndexFromSpectrumNumber(const specnum_t specNo) const
Given a spectrum number, find the corresponding workspace index.
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.
DirectILLTubeBackground : Fits polynomial backgrounds over the pixels of position sensitive tubes.
API::MatrixWorkspace_sptr fitComponentBackground(API::MatrixWorkspace_sptr const &ws, std::vector< double > const &xRanges)
Fits a polynomial background.
API::MatrixWorkspace_sptr applyDiagnostics(API::MatrixWorkspace_sptr ws)
Apply a mask workspace (if given) to ws.
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.
std::map< std::string, std::string > validateInputs() override
Validate input properties.
void init() override
Initialize the algorithm's properties.
API::MatrixWorkspace_sptr cropToComponent(API::MatrixWorkspace_sptr const &ws, std::string const &componentName)
Crop a component workspace out of ws.
std::vector< std::string > components(Geometry::Instrument const &instrument)
Return a list of component names for the algorithm to process.
API::MatrixWorkspace_sptr peakExcludingAverage(const API::MatrixWorkspace &ws, std::vector< double > const &peakStarts, std::vector< double > const &peakEnds)
Average the histograms of a workspace.
const std::string category() const override
Algorithm's category for identification.
const std::vector< std::string > seeAlso() const override
Return a vector of related algorithms.
std::shared_ptr< const IComponent > getComponentByName(const std::string &cname, int nlevels=0) const override
Returns a pointer to the first component of assembly encountered with the given name.
std::vector< std::string > getStringParameter(const std::string &pname, bool recursive=true) const override
Get a parameter defined as a string.
Definition Component.h:244
bool hasParameter(const std::string &name, bool recursive=true) const override
Returns a boolean indicating if the component has the named parameter.
Base Instrument Class.
Definition Instrument.h:47
Support for a property that holds an array of values.
Exception for when an item is not found in a collection.
Definition Exception.h:145
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
std::shared_ptr< Column > Column_sptr
Definition Column.h:232
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
The exception classes used by Mantid.
Definition Exception.h:95
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 algorithm's properties.
STL namespace.
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54