Mantid
Loading...
Searching...
No Matches
UnwrapMonitor.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#include "MantidAPI/Axis.h"
22
23namespace Mantid::Algorithms {
24
25DECLARE_ALGORITHM(UnwrapMonitor)
26
27using namespace Kernel;
28using namespace API;
29
32 : m_conversionConstant(0.), m_inputWS(), m_LRef(0.), m_Tmin(0.), m_Tmax(0.), m_XSize(0) {}
33
36 auto wsValidator = std::make_shared<CompositeValidator>();
37 wsValidator->add<WorkspaceUnitValidator>("TOF");
38 wsValidator->add<HistogramValidator>();
39 wsValidator->add<RawCountValidator>();
40 wsValidator->add<InstrumentValidator>();
42 std::make_unique<WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Direction::Input, wsValidator),
43 "A workspace with x values in units of TOF and y values in counts");
44 declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("OutputWorkspace", "", Direction::Output),
45 "The name of the workspace to be created as the output of the algorithm");
46
47 auto validator = std::make_shared<BoundedValidator<double>>();
48 validator->setLower(0.01);
49 declareProperty("LRef", 0.0, validator, "The length of the reference flight path (in metres)");
50
51 declareProperty("JoinWavelength", 0.0, Direction::Output);
52
53 // Calculate and set the constant factor for the conversion to wavelength
54 const double TOFisinMicroseconds = 1e6;
55 const double toAngstroms = 1e10;
56 m_conversionConstant = (PhysicalConstants::h * toAngstroms) / (PhysicalConstants::NeutronMass * TOFisinMicroseconds);
57}
58
67 // Get the input workspace
68 m_inputWS = getProperty("InputWorkspace");
69 // Get the number of spectra in this workspace
70 const auto numberOfSpectra = static_cast<int>(m_inputWS->getNumberHistograms());
71 g_log.debug() << "Number of spectra in input workspace: " << numberOfSpectra << '\n';
72
73 // Get the "reference" flightpath (currently passed in as a property)
74 m_LRef = getProperty("LRef");
75 // Get the min & max frame values
76 m_Tmin = m_inputWS->x(0).front();
77 m_Tmax = m_inputWS->x(0).back();
78 g_log.debug() << "Frame range in microseconds is: " << m_Tmin << " - " << m_Tmax << '\n';
79 m_XSize = m_inputWS->x(0).size();
80
81 // Need a new workspace. Will just be used temporarily until the data is
82 // rebinned.
83 MatrixWorkspace_sptr tempWS = WorkspaceFactory::Instance().create(m_inputWS, numberOfSpectra, m_XSize, m_XSize - 1);
84 // Set the correct X unit on the output workspace
85 tempWS->getAxis(0)->unit() = UnitFactory::Instance().create("Wavelength");
86
87 // This will be used later to store the maximum number of bin BOUNDARIES for
88 // the rebinning
89 size_t max_bins = 0;
90
91 const auto &spectrumInfo = m_inputWS->spectrumInfo();
92 const double L1 = spectrumInfo.l1();
93
94 m_progress = std::make_unique<Progress>(this, 0.0, 1.0, numberOfSpectra);
95 // Loop over the histograms (detector spectra)
96 for (int i = 0; i < numberOfSpectra; ++i) {
97 if (!spectrumInfo.hasDetectors(i)) {
98 // If the detector flightpath is missing, zero the data
99 g_log.debug() << "Detector information for workspace index " << i << " is not available.\n";
100 tempWS->mutableX(i) = 0.0;
101 tempWS->mutableY(i) = 0.0;
102 tempWS->mutableE(i) = 0.0;
103 continue;
104 }
105
106 // Getting the unwrapped data is a three step process.
107 // 1. We ge the unwrapped version of the x data.
108 // 2. Then we get the unwrapped version of the y and e data for
109 // which we require the x data from the previous step
110 // 3. Finally we need to set the newly unwrapped data on the
111 // histogram
112
113 // Unwrap the x data. Returns the bin ranges that end up being used
114 const double Ld = L1 + spectrumInfo.l2(i);
115 std::vector<double> newX;
116 const std::vector<int> rangeBounds = this->unwrapX(newX, i, Ld);
117
118 // Unwrap the y & e data according to the ranges found above
119 std::vector<double> newY;
120 std::vector<double> newE;
121 this->unwrapYandE(tempWS, i, rangeBounds, newY, newE);
122
123 // Now set the new X, Y and E values on the Histogram
124 auto histogram = tempWS->histogram(i);
125 tempWS->setHistogram(i, Mantid::HistogramData::BinEdges(std::move(newX)),
126 Mantid::HistogramData::Counts(std::move(newY)),
127 Mantid::HistogramData::CountStandardDeviations(std::move(newE)));
128
129 // Get the maximum number of bins (excluding monitors) for the rebinning
130 // below
131 if (!spectrumInfo.isMonitor(i)) {
132 const size_t XLen = tempWS->x(i).size();
133 if (XLen > max_bins)
134 max_bins = XLen;
135 }
136 m_progress->report();
137 } // loop over spectra
138
139 // Only rebin if more that just a single monitor spectrum in the input WS
140 if (numberOfSpectra > 1) {
141 // Calculate the minimum and maximum possible wavelengths for the rebinning
142 const double minLambda = (m_conversionConstant * m_Tmin) / m_LRef;
143 const double maxLambda = (m_conversionConstant * m_Tmax) / m_LRef;
144 // Rebin the data into common wavelength bins
145 MatrixWorkspace_sptr outputWS = this->rebin(tempWS, minLambda, maxLambda, max_bins - 1);
146
147 g_log.debug() << "Rebinned workspace has " << outputWS->getNumberHistograms() << " histograms of "
148 << outputWS->blocksize() << " bins each\n";
149 setProperty("OutputWorkspace", outputWS);
150 } else
151 setProperty("OutputWorkspace", tempWS);
152
153 m_inputWS.reset();
154}
155
164const std::vector<int> UnwrapMonitor::unwrapX(std::vector<double> &newX, const int &spectrum, const double &Ld) {
165 // Create and initalise the vector that will store the bin ranges, and will be
166 // returned
167 // Elements are: 0 - Lower range start, 1 - Lower range end, 2 - Upper range
168 // start
169 std::vector<int> binRange(3, -1);
170
171 // Calculate cut-off times
172 const double T1 = m_Tmax - (m_Tmin * (1 - (Ld / m_LRef)));
173 const double T2 = m_Tmax * (Ld / m_LRef);
174
175 // Create a temporary vector to store the lower range of the unwrapped
176 // histograms
177 std::vector<double> tempX_L;
178 tempX_L.reserve(m_XSize); // Doing this possible gives a small efficiency increase
179 // Create a vector for the upper range. Make it a reference to the output
180 // histogram to save an assignment later
181 newX.clear();
182 newX.reserve(m_XSize);
183
184 // Get a reference to the input x data
185 const auto &xdata = m_inputWS->x(spectrum);
186 // Loop over histogram, selecting bins in appropriate ranges.
187 // At the moment, the data in the bin in which a cut-off sits is excluded.
188 for (unsigned int bin = 0; bin < m_XSize; ++bin) {
189 // This is the time-of-flight value under consideration in the current
190 // iteration of the loop
191 const double tof = xdata[bin];
192 // First deal with bins where m_Tmin < tof < T2
193 if (tof < T2) {
194 const double wavelength = (m_conversionConstant * tof) / Ld;
195 tempX_L.emplace_back(wavelength);
196 // Record the bins that fall in this range for copying over the data &
197 // errors
198 if (binRange[0] == -1)
199 binRange[0] = bin;
200 binRange[1] = bin;
201 }
202 // Now do the bins where T1 < tof < m_Tmax
203 else if (tof > T1) {
204 const double velocity = Ld / (tof - m_Tmax + m_Tmin);
205 const double wavelength = m_conversionConstant / velocity;
206 newX.emplace_back(wavelength);
207 // Remove the duplicate boundary bin
208 if (tof == m_Tmax && Kernel::withinAbsoluteDifference(wavelength, tempX_L.front(), 1.0e-5))
209 newX.pop_back();
210 // Record the bins that fall in this range for copying over the data &
211 // errors
212 if (binRange[2] == -1)
213 binRange[2] = bin;
214 }
215 } // loop over X values
216
217 // Deal with the (rare) case that a detector (e.g. downstream monitor) is at a
218 // longer flightpath than m_LRef
219 if (Ld > m_LRef) {
220 std::pair<int, int> binLimits = this->handleFrameOverlapped(xdata, Ld, tempX_L);
221 binRange[0] = binLimits.first;
222 binRange[1] = binLimits.second;
223 }
224
225 // Record the point at which the unwrapped sections are joined, first time
226 // through only
227 Property const *join = getProperty("JoinWavelength");
228 if (join->isDefault()) {
229 g_log.information() << "Joining wavelength: " << tempX_L.front() << " Angstrom\n";
230 setProperty("JoinWavelength", tempX_L.front());
231 }
232
233 // Append first vector to back of second
234 newX.insert(newX.end(), tempX_L.begin(), tempX_L.end());
235
236 return binRange;
237}
238
242std::pair<int, int> UnwrapMonitor::handleFrameOverlapped(const Mantid::HistogramData::HistogramX &xdata,
243 const double &Ld, std::vector<double> &tempX) {
244 // Calculate the interval to exclude
245 const double Dt = (m_Tmax - m_Tmin) * (1 - (m_LRef / Ld));
246 // This gives us new minimum & maximum tof values
247 const double minT = m_Tmin + Dt;
248 const double maxT = m_Tmax - Dt;
249 // Can have situation where Ld is so much larger than Lref that everything
250 // would be excluded.
251 // This is an invalid input - warn and leave spectrum unwrapped
252 if (minT > maxT) {
253 g_log.warning() << "Invalid input, Ld (" << Ld << ") >> Lref (" << m_LRef
254 << "). Current spectrum will not be unwrapped.\n";
255 return std::make_pair(0, static_cast<int>(xdata.size() - 1));
256 }
257
258 int min = 0, max = static_cast<int>(xdata.size());
259 for (unsigned int j = 0; j < m_XSize; ++j) {
260 const double T = xdata[j];
261 if (T < minT) {
262 min = j + 1;
263 tempX.erase(tempX.begin());
264 } else if (T > maxT) {
265 tempX.erase(tempX.end() - (max - j), tempX.end());
266 max = j - 1;
267 break;
268 }
269 }
270 return std::make_pair(min, max);
271}
272
282void UnwrapMonitor::unwrapYandE(const API::MatrixWorkspace_sptr &tempWS, const int &spectrum,
283 const std::vector<int> &rangeBounds, std::vector<double> &newY,
284 std::vector<double> &newE) {
285 // Copy over the relevant ranges of Y & E data
286 std::vector<double> &Y = newY;
287 std::vector<double> &E = newE;
288 // Get references to the input data
289 const auto &YIn = m_inputWS->y(spectrum);
290 const auto &EIn = m_inputWS->e(spectrum);
291 if (rangeBounds[2] != -1) {
292 // Copy in the upper range
293 Y.assign(YIn.begin() + rangeBounds[2], YIn.end());
294 E.assign(EIn.begin() + rangeBounds[2], EIn.end());
295 // Propagate masking, if necessary
296 if (m_inputWS->hasMaskedBins(spectrum)) {
297 const MatrixWorkspace::MaskList &inputMasks = m_inputWS->maskedBins(spectrum);
298 MatrixWorkspace::MaskList::const_iterator it;
299 for (it = inputMasks.begin(); it != inputMasks.end(); ++it) {
300 if (static_cast<int>((*it).first) >= rangeBounds[2])
301 tempWS->flagMasked(spectrum, (*it).first - rangeBounds[2], (*it).second);
302 }
303 }
304 } else {
305 // Y & E are references to existing vector. Assign above clears them, so
306 // need to explicitly here
307 Y.clear();
308 E.clear();
309 }
310 if (rangeBounds[0] != -1 && rangeBounds[1] > 0) {
311 // Now append the lower range
312 auto YStart = YIn.begin();
313 auto EStart = EIn.begin();
314 Y.insert(Y.end(), YStart + rangeBounds[0], YStart + rangeBounds[1]);
315 E.insert(E.end(), EStart + rangeBounds[0], EStart + rangeBounds[1]);
316 // Propagate masking, if necessary
317 if (m_inputWS->hasMaskedBins(spectrum)) {
318 const MatrixWorkspace::MaskList &inputMasks = m_inputWS->maskedBins(spectrum);
319 for (const auto &inputMask : inputMasks) {
320 const auto maskIndex = static_cast<int>(inputMask.first);
321 if (maskIndex >= rangeBounds[0] && maskIndex < rangeBounds[1])
322 tempWS->flagMasked(spectrum, maskIndex - rangeBounds[0], inputMask.second);
323 }
324 }
325 }
326}
327
337 const double &max, const size_t &numBins) {
338 // Calculate the width of a bin
339 const double step = (max - min) / static_cast<double>(numBins);
340
341 // Create a Rebin child algorithm
342 auto childAlg = createChildAlgorithm("Rebin");
343 childAlg->setProperty<MatrixWorkspace_sptr>("InputWorkspace", workspace);
344 childAlg->setPropertyValue("OutputWorkspace", "Anonymous");
345
346 // Construct the vector that holds the rebin parameters and set the property
347 std::vector<double> paramArray = {min, step, max};
348 childAlg->setProperty<std::vector<double>>("Params", paramArray);
349 g_log.debug() << "Rebinning unwrapped data into " << numBins << " bins of width " << step
350 << " Angstroms, running from " << min << " to " << max << '\n';
351
352 childAlg->executeAsChildAlg();
353 return childAlg->getProperty("OutputWorkspace");
354}
355
356} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
IPeaksWorkspace_sptr workspace
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.
Kernel::Logger & g_log
Definition Algorithm.h:422
A validator which checks that a workspace contains histogram data (the default) or point data as requ...
A validator which checks that a workspace has a valid instrument.
std::map< size_t, double > MaskList
Masked bins for each spectrum are stored as a set of pairs containing <bin index, weight>
A validator which checks that a workspace contains raw counts in its bins.
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
UnwrapMonitor()
Default constructor.
API::MatrixWorkspace_const_sptr m_inputWS
to wavelength
double m_LRef
The 'reference' flightpath.
double m_Tmin
The start of the time-of-flight frame.
void init() override
Initialisation method.
size_t m_XSize
The size of the X vectors in the input workspace.
std::unique_ptr< API::Progress > m_progress
Progress reporting.
API::MatrixWorkspace_sptr rebin(const API::MatrixWorkspace_sptr &workspace, const double &min, const double &max, const size_t &numBins)
Rebins the data into common bins of wavelength.
std::pair< int, int > handleFrameOverlapped(const Mantid::HistogramData::HistogramX &xdata, const double &Ld, std::vector< double > &tempX)
Deals with the (rare) case where the flightpath is longer than the reference Note that in this case b...
double m_Tmax
The end of the time-of-flight frame.
const std::vector< int > unwrapX(std::vector< double > &newX, const int &spectrum, const double &Ld)
Unwraps an X array, converting the units to wavelength along the way.
double m_conversionConstant
The constant used in the conversion from TOF.
void unwrapYandE(const API::MatrixWorkspace_sptr &tempWS, const int &spectrum, const std::vector< int > &rangeBounds, std::vector< double > &newY, std::vector< double > &newE)
Unwraps the Y & E vectors of a spectrum according to the ranges found in unwrapX.
void exec() override
Executes the algorithm.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
Base class for properties.
Definition Property.h:94
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
MANTID_KERNEL_DLL bool withinAbsoluteDifference(T const x, T const y, S const tolerance)
Test whether x, y are within absolute tolerance tol.
static constexpr double NeutronMass
Mass of the neutron in kg.
static constexpr double h
Planck constant in J*s.
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54