Mantid
Loading...
Searching...
No Matches
UnwrapMonitorsInTOF.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 +
13#include "MantidHistogramData/Counts.h"
14#include "MantidHistogramData/Histogram.h"
15#include "MantidHistogramData/Points.h"
17#include <numeric>
18
19namespace {
20const double specialWavelengthCutoff = -1.0;
21const double specialTimeOfFlightCutoff = -1.0;
22const int specialIndex = -1;
23
24struct MinAndMaxTof {
25 MinAndMaxTof(double minTof, double maxTof) : minTof(minTof), maxTof(maxTof) {}
26 double minTof;
27 double maxTof;
28};
29
30struct MinAndMaxIndex {
31 MinAndMaxIndex(int minIndex, int maxIndex) : minIndex(minIndex), maxIndex(maxIndex) {}
32 int minIndex;
33 int maxIndex;
34};
35
50MinAndMaxTof getMinAndMaxTofForDistanceFromSoure(double distanceFromSource, double lowerWavelengthLimit,
51 double upperWavelengthLimit) {
52 // Calculate and set the constant factor for the conversion to wavelength
53 const double angstromConversion = 1e10;
54 const double microsecondConversion = 1e6;
55 const double conversionConstant =
56 (distanceFromSource * Mantid::PhysicalConstants::NeutronMass * microsecondConversion) /
57 (Mantid::PhysicalConstants::h * angstromConversion);
58 const double minTof = lowerWavelengthLimit == specialWavelengthCutoff ? specialTimeOfFlightCutoff
59 : conversionConstant * lowerWavelengthLimit;
60 const double maxTof = upperWavelengthLimit == specialWavelengthCutoff ? specialTimeOfFlightCutoff
61 : conversionConstant * upperWavelengthLimit;
62 return MinAndMaxTof(minTof, maxTof);
63}
64
65double getDistanceFromSourceForWorkspaceIndex(Mantid::API::MatrixWorkspace *workspace,
66 const Mantid::API::SpectrumInfo &spectrumInfo, size_t workspaceIndex) {
67 const auto &detector = spectrumInfo.detector(workspaceIndex);
68 return detector.getDistance(*(workspace->getInstrument()->getSource()));
69}
70
71MinAndMaxTof getMinAndMaxTof(Mantid::API::MatrixWorkspace *workspace, const Mantid::API::SpectrumInfo &spectrumInfo,
72 size_t workspaceIndex, double lowerWavelengthLimit, double upperWavelengthLimit) {
73 const auto distanceFromSource = getDistanceFromSourceForWorkspaceIndex(workspace, spectrumInfo, workspaceIndex);
74 return getMinAndMaxTofForDistanceFromSoure(distanceFromSource, lowerWavelengthLimit, upperWavelengthLimit);
75}
76
85Mantid::HistogramData::Points getPoints(Mantid::API::MatrixWorkspace *workspace, size_t workspaceIndex) {
86 auto points = workspace->points(workspaceIndex);
87 std::vector<double> doubledData(2 * points.size(), 0);
88 std::copy(std::begin(points), std::end(points), std::begin(doubledData));
89
90 // Calculate the doubled up bit. To do this we need to:
91 // 1. Get the last element of the original BinEdges
92 // 2. For each entry in the BinEdges calcualte the increment and add it to the
93 // last element. In addition
94 // we have to make sure that there is a spacing between the last element
95 // and the newly append last+1 element
96 // This spacing is taken to be the difference between the first and the
97 // second element
98 auto firstTof = points.front();
99 auto lastTof = points.back();
100 auto doubledDataIterator = doubledData.begin();
101 std::advance(doubledDataIterator, points.size());
102
103 double lastElementToNewElementSpacing = 0.0;
104 if (doubledData.size() > 1) {
105 lastElementToNewElementSpacing = doubledData[1] - doubledData[0];
106 }
107
108 for (auto pointsIterator = points.begin(); doubledDataIterator != doubledData.end();
109 ++doubledDataIterator, ++pointsIterator) {
110 auto newValue = lastTof + lastElementToNewElementSpacing + (*pointsIterator - firstTof);
111 *doubledDataIterator = newValue;
112 }
113
114 return Mantid::HistogramData::Points{doubledData};
115}
116
117MinAndMaxIndex getMinAndMaxIndex(const MinAndMaxTof &minMaxTof, const Mantid::HistogramData::Points &points) {
118 int minIndex = specialIndex;
119 int maxIndex = specialIndex;
120 const auto minCutOff = minMaxTof.minTof;
121 const auto maxCutOff = minMaxTof.maxTof;
122 int index = 0;
123 for (const auto &element : points) {
124 if (element < minCutOff) {
125 minIndex = index;
126 }
127
128 if (element > maxCutOff) {
129 maxIndex = index;
130 // We can break here since the min would have been set before the max
131 break;
132 }
133 ++index;
134 }
135
136 // We have to take care since the maxIndex can be a special index for two
137 // reasons
138 // 1. The maxCutOff is smaller than the smallest time of flight value of the
139 // workspace, then the special index index is correct
140 // 2. The maxCutOff is larger then the largest time of flight value of the
141 // workspace, then the last index is correct
142 if (maxIndex == specialIndex) {
143 if (points[points.size() - 1] < maxCutOff) {
144 maxIndex = static_cast<int>(points.size()) - 1;
145 }
146 }
147
148 // The min index is the index which is the largest lower bound index which is
149 // not in the TOF region, hence we need index + 1 as the
150 // lower bound index
151 minIndex += 1;
152
153 return MinAndMaxIndex(minIndex, maxIndex);
154}
155
156void setTofBelowLowerBoundToZero(std::vector<double> &doubledData, int minIndex) {
157 if (minIndex == specialIndex) {
158 return;
159 }
160 auto begin = doubledData.begin();
161 auto end = begin;
162 std::advance(end, minIndex);
163 std::fill(begin, end, 0.0);
164}
165
166void setTofAboveUpperBoundToZero(std::vector<double> &doubledData, int maxIndex) {
167 if (maxIndex >= static_cast<int>(doubledData.size()) - 1) {
168 return;
169 }
170 auto begin = doubledData.begin();
171 std::advance(begin, maxIndex);
172 auto end = doubledData.end();
173 std::fill(begin, end, 0.0);
174}
175
186Mantid::HistogramData::Counts getCounts(Mantid::API::MatrixWorkspace *workspace, size_t workspaceIndex,
187 const MinAndMaxTof &minMaxTof, const Mantid::HistogramData::Points &points) {
188 // Create the data twice
189 auto counts = workspace->counts(workspaceIndex);
190 std::vector<double> doubledData(2 * counts.size(), 0);
191 auto doubledDataIterator = doubledData.begin();
192 std::copy(std::begin(counts), std::end(counts), doubledDataIterator);
193 std::advance(doubledDataIterator, counts.size());
194 std::copy(std::begin(counts), std::end(counts), doubledDataIterator);
195
196 // Now set everything to zero entires which correspond to TOF which is less
197 // than minTof and more than maxTof
198 auto minAndMaxIndex = getMinAndMaxIndex(minMaxTof, points);
199 if (minMaxTof.minTof != specialTimeOfFlightCutoff) {
200 setTofBelowLowerBoundToZero(doubledData, minAndMaxIndex.minIndex);
201 }
202
203 if (minMaxTof.maxTof != specialTimeOfFlightCutoff) {
204 setTofAboveUpperBoundToZero(doubledData, minAndMaxIndex.maxIndex);
205 }
206 return Mantid::HistogramData::Counts(doubledData);
207}
208
218std::vector<size_t> getWorkspaceIndicesForMonitors(Mantid::API::MatrixWorkspace *workspace) {
219 std::vector<size_t> workspaceIndices;
220 auto monitorWorkspace = workspace->monitorWorkspace();
221 if (monitorWorkspace) {
222 auto numberOfHistograms = monitorWorkspace->getNumberHistograms();
223 for (size_t index = 0; index < numberOfHistograms; ++index) {
224 const auto &spectrum = workspace->getSpectrum(index);
225 auto spectrumNumber = spectrum.getSpectrumNo();
226 auto workspaceIndex = workspace->getIndexFromSpectrumNumber(spectrumNumber);
227 workspaceIndices.emplace_back(workspaceIndex);
228 }
229 } else {
230 auto numberOfHistograms = workspace->getNumberHistograms();
231 const auto &spectrumInfo = workspace->spectrumInfo();
232 for (size_t workspaceIndex = 0; workspaceIndex < numberOfHistograms; ++workspaceIndex) {
233 if (spectrumInfo.isMonitor(workspaceIndex)) {
234 workspaceIndices.emplace_back(workspaceIndex);
235 }
236 }
237 }
238 return workspaceIndices;
239}
240} // namespace
241
242namespace Mantid::Algorithms {
243
246
247// Register the algorithm into the AlgorithmFactory
248DECLARE_ALGORITHM(UnwrapMonitorsInTOF)
249
250//----------------------------------------------------------------------------------------------
251
252
253const std::string UnwrapMonitorsInTOF::name() const { return "UnwrapMonitorsInTOF"; }
254
256int UnwrapMonitorsInTOF::version() const { return 1; }
257
259const std::string UnwrapMonitorsInTOF::category() const { return "CorrectionFunctions\\InstrumentCorrections"; }
260
262const std::string UnwrapMonitorsInTOF::summary() const {
263 return "Takes a TOF input workspace that contains 'raw' data and unwraps "
264 "monitor data "
265 "according to a specified wavelength range. The monitor spectra are "
266 "essentially "
267 "doubled and then trimmed to the specified wavelength range. If no "
268 "wavelength "
269 "range is specified (-1), then the doubled data is not trimmed. The "
270 "units of the output "
271 "workspace is in TOF. Note that currently only workspaces with "
272 "linearly binned monitor data "
273 "can be handled correctly.";
274}
275
276//----------------------------------------------------------------------------------------------
281 std::make_unique<WorkspaceProperty<Mantid::API::MatrixWorkspace>>("InputWorkspace", "", Direction::Input),
282 "An input workspace.");
284 std::make_unique<WorkspaceProperty<Mantid::API::MatrixWorkspace>>("OutputWorkspace", "", Direction::Output),
285 "An output workspace.");
286 declareProperty<double>("WavelengthMin", specialWavelengthCutoff, "A lower bound of the wavelength range.");
287 declareProperty<double>("WavelengthMax", specialWavelengthCutoff, "An upper bound of the wavelength range.");
288}
289
290//----------------------------------------------------------------------------------------------
294 // The unwrapping happens in three parts
295 // 1. We duplicate the monitor data, by appending the data to itself again.
296 // This means
297 // that at there is an interval in which the data is correct
298 // 2. Data which is outside of a certain equivalent wavelength range will be
299 // set to 0
300 Mantid::API::MatrixWorkspace_sptr inputWorkspace = getProperty("InputWorkspace");
301 const double lowerWavelengthLimit = getProperty("WavelengthMin");
302 const double upperWavelengthLimit = getProperty("WavelengthMax");
303
304 auto outputWorkspace = Mantid::API::MatrixWorkspace_sptr(inputWorkspace->clone());
305 const auto workspaceIndices = getWorkspaceIndicesForMonitors(outputWorkspace.get());
306
307 const auto &spectrumInfo = outputWorkspace->spectrumInfo();
308
309 for (const auto &workspaceIndex : workspaceIndices) {
310 const auto minMaxTof = getMinAndMaxTof(outputWorkspace.get(), spectrumInfo, workspaceIndex, lowerWavelengthLimit,
311 upperWavelengthLimit);
312 auto points = getPoints(outputWorkspace.get(), workspaceIndex);
313 auto counts = getCounts(outputWorkspace.get(), workspaceIndex, minMaxTof, points);
314 // Get the input histogram
315 auto inputHistogram = inputWorkspace->histogram(workspaceIndex);
316 auto spectrumIsHistogramData = inputHistogram.xMode() == Mantid::HistogramData::Histogram::XMode::BinEdges;
317 if (spectrumIsHistogramData) {
318 Mantid::HistogramData::BinEdges binEdges(points);
319 Mantid::HistogramData::Histogram histogram(binEdges, counts);
320 outputWorkspace->setHistogram(workspaceIndex, histogram);
321 } else {
322 Mantid::HistogramData::Histogram histogram(points, counts);
323 outputWorkspace->setHistogram(workspaceIndex, histogram);
324 }
325 }
326 setProperty("OutputWorkspace", outputWorkspace);
327}
328
333std::map<std::string, std::string> UnwrapMonitorsInTOF::validateInputs() {
334 std::map<std::string, std::string> invalidProperties;
335 // The lower wavelength boundary needs to be smaller than the upper wavelength
336 // boundary
337 const double lowerWavelengthLimit = getProperty("WavelengthMin");
338 const double upperWavelengthLimit = getProperty("WavelengthMax");
339 if (lowerWavelengthLimit != specialWavelengthCutoff && lowerWavelengthLimit < 0.0) {
340 invalidProperties["WavelengthMin"] = "The lower wavelength limit must be set to a positive value.";
341 }
342
343 if (upperWavelengthLimit != specialWavelengthCutoff && upperWavelengthLimit < 0.0) {
344 invalidProperties["WavelengthMax"] = "The upper wavelength limit must be set to a positive value.";
345 }
346
347 if (lowerWavelengthLimit != specialWavelengthCutoff && upperWavelengthLimit != specialWavelengthCutoff &&
348 lowerWavelengthLimit >= upperWavelengthLimit) {
349 invalidProperties["WavelengthMin"] = "The lower wavelength limit must be "
350 "smaller than the upper wavelnegth "
351 "limit.";
352 invalidProperties["WavelengthMax"] = "The lower wavelength limit must be "
353 "smaller than the upper wavelnegth "
354 "limit.";
355 }
356
357 return invalidProperties;
358}
359
360} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
IPeaksWorkspace_sptr workspace
Definition: IndexPeaks.cpp:114
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
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
Base MatrixWorkspace Abstract Class.
API::SpectrumInfo is an intermediate step towards a SpectrumInfo that is part of Instrument-2....
Definition: SpectrumInfo.h:53
bool isMonitor(const size_t index) const
Returns true if the detector(s) associated with the spectrum are monitors.
const Geometry::IDetector & detector(const size_t index) const
Return a const reference to the detector or detector group of the spectrum with given index.
A property class for workspaces.
UnwrapMonitorsInTOF : Handles workspaces which contain monitors that recorded data which spills over ...
const std::string category() const override
Algorithm's category for identification.
int version() const override
Algorithm's version for identification.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
void exec() override
Execute the algorithm.
void init() override
Initialize the algorithm's properties.
std::map< std::string, std::string > validateInputs() override
Check the inputs for invalid values.
double getDistance(const IComponent &comp) const override=0
Get the distance of this detector object from another Component.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
static constexpr double NeutronMass
Mass of the neutron in kg.
static constexpr double h
Planck constant in J*s.
STL namespace.
Describes the direction (within an algorithm) of a Property.
Definition: Property.h:50
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54