-
Notifications
You must be signed in to change notification settings - Fork 122
/
UnwrapMonitorsInTOF.cpp
403 lines (363 loc) · 15.6 KB
/
UnwrapMonitorsInTOF.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source,
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidAlgorithms/UnwrapMonitorsInTOF.h"
#include "MantidAPI/ISpectrum.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/SpectrumInfo.h"
#include "MantidAPI/WorkspaceProperty.h"
#include "MantidGeometry/Instrument.h"
#include "MantidHistogramData/Counts.h"
#include "MantidHistogramData/Histogram.h"
#include "MantidHistogramData/Points.h"
#include "MantidKernel/PhysicalConstants.h"
#include <numeric>
namespace {
const double specialWavelengthCutoff = -1.0;
const double specialTimeOfFlightCutoff = -1.0;
const int specialIndex = -1;
struct MinAndMaxTof {
MinAndMaxTof(double minTof, double maxTof) : minTof(minTof), maxTof(maxTof) {}
double minTof;
double maxTof;
};
struct MinAndMaxIndex {
MinAndMaxIndex(int minIndex, int maxIndex)
: minIndex(minIndex), maxIndex(maxIndex) {}
int minIndex;
int maxIndex;
};
/**
* For a given distance from the source and lower and upper wavelength bound the
*function calculates the lower and upper bound of the
* TOF for this distance.
*
* To get the time: T = L/V and V = h/(m*lambda) --> T(lambda) = (L*m/h)*lambda
* In addition we need to divide the wavelength by 10^10 since the input is in
* Angstrom and we need to multiply by 10^6 since the
* output is in microseconds
* @param distanceFromSource the distance from the source in meters
* @param lowerWavelengthLimit the lower bound of the wavelength in Angstrom
* @param upperWavelengthLimit the upper bound of the wavelength in Angstrom
* @return the upper and lower bound of the TOF in microseconds
*/
MinAndMaxTof getMinAndMaxTofForDistanceFromSoure(double distanceFromSource,
double lowerWavelengthLimit,
double upperWavelengthLimit) {
// Calculate and set the constant factor for the conversion to wavelength
const double angstromConversion = 1e10;
const double microsecondConversion = 1e6;
const double conversionConstant =
(distanceFromSource * Mantid::PhysicalConstants::NeutronMass *
microsecondConversion) /
(Mantid::PhysicalConstants::h * angstromConversion);
const double minTof = lowerWavelengthLimit == specialWavelengthCutoff
? specialTimeOfFlightCutoff
: conversionConstant * lowerWavelengthLimit;
const double maxTof = upperWavelengthLimit == specialWavelengthCutoff
? specialTimeOfFlightCutoff
: conversionConstant * upperWavelengthLimit;
return MinAndMaxTof(minTof, maxTof);
}
double getDistanceFromSourceForWorkspaceIndex(
Mantid::API::MatrixWorkspace *workspace,
const Mantid::API::SpectrumInfo &spectrumInfo, size_t workspaceIndex) {
const auto &detector = spectrumInfo.detector(workspaceIndex);
return detector.getDistance(*(workspace->getInstrument()->getSource()));
}
MinAndMaxTof getMinAndMaxTof(Mantid::API::MatrixWorkspace *workspace,
const Mantid::API::SpectrumInfo &spectrumInfo,
size_t workspaceIndex, double lowerWavelengthLimit,
double upperWavelengthLimit) {
const auto distanceFromSource = getDistanceFromSourceForWorkspaceIndex(
workspace, spectrumInfo, workspaceIndex);
return getMinAndMaxTofForDistanceFromSoure(
distanceFromSource, lowerWavelengthLimit, upperWavelengthLimit);
}
/**
* Doubles up the x points. This means that if the current points are 1, 2, 3, 4
* then the new
* bin edges are 1, 2, 3, 4, 5, 6, 7, 8
* @param workspace the workspace from which we grab the x data
* @param workspaceIndex the particular histogram index
* @return a BinEdges object
*/
Mantid::HistogramData::Points getPoints(Mantid::API::MatrixWorkspace *workspace,
size_t workspaceIndex) {
auto points = workspace->points(workspaceIndex);
std::vector<double> doubledData(2 * points.size(), 0);
std::copy(std::begin(points), std::end(points), std::begin(doubledData));
// Calculate the doubled up bit. To do this we need to:
// 1. Get the last element of the original BinEdges
// 2. For each entry in the BinEdges calcualte the increment and add it to the
// last element. In addition
// we have to make sure that there is a spacing between the last element
// and the newly append last+1 element
// This spacing is taken to be the difference between the first and the
// second element
auto firstTof = points.front();
auto lastTof = points.back();
auto doubledDataIterator = doubledData.begin();
std::advance(doubledDataIterator, points.size());
double lastElementToNewElementSpacing = 0.0;
if (doubledData.size() > 1) {
lastElementToNewElementSpacing = doubledData[1] - doubledData[0];
}
for (auto pointsIterator = points.begin();
doubledDataIterator != doubledData.end();
++doubledDataIterator, ++pointsIterator) {
auto newValue =
lastTof + lastElementToNewElementSpacing + (*pointsIterator - firstTof);
*doubledDataIterator = newValue;
}
return Mantid::HistogramData::Points{doubledData};
}
MinAndMaxIndex getMinAndMaxIndex(const MinAndMaxTof &minMaxTof,
const Mantid::HistogramData::Points &points) {
int minIndex = specialIndex;
int maxIndex = specialIndex;
const auto minCutOff = minMaxTof.minTof;
const auto maxCutOff = minMaxTof.maxTof;
int index = 0;
for (const auto &element : points) {
if (element < minCutOff) {
minIndex = index;
}
if (element > maxCutOff) {
maxIndex = index;
// We can break here since the min would have been set before the max
break;
}
++index;
}
// We have to take care since the maxIndex can be a special index for two
// reasons
// 1. The maxCutOff is smaller than the smallest time of flight value of the
// workspace, then the special index index is correct
// 2. The maxCutOff is larger then the largest time of flight value of the
// workspace, then the last index is correct
if (maxIndex == specialIndex) {
if (points[points.size() - 1] < maxCutOff) {
maxIndex = static_cast<int>(points.size()) - 1;
}
}
// The min index is the index which is the largest lower bound index which is
// not in the TOF region, hence we need index + 1 as the
// lower bound index
minIndex += 1;
return MinAndMaxIndex(minIndex, maxIndex);
}
void setTofBelowLowerBoundToZero(std::vector<double> &doubledData,
int minIndex) {
if (minIndex == specialIndex) {
return;
}
auto begin = doubledData.begin();
auto end = begin;
std::advance(end, minIndex);
std::fill(begin, end, 0.0);
}
void setTofAboveUpperBoundToZero(std::vector<double> &doubledData,
int maxIndex) {
if (maxIndex >= static_cast<int>(doubledData.size()) - 1) {
return;
}
auto begin = doubledData.begin();
std::advance(begin, maxIndex);
auto end = doubledData.end();
std::fill(begin, end, 0.0);
}
/**
* Creates a doubled up version of the counts for a particular histogram. It
* sets everything to zero which
* is not in the valid TOF range
* @param workspace the workspace from which we grab the valid counts
* @param workspaceIndex the particular histogram index
* @param minMaxTof the upper and lower boundary of the TOF.
* @param binEdges the TOF BinEdges object
* @return a Counts object
*/
Mantid::HistogramData::Counts
getCounts(Mantid::API::MatrixWorkspace *workspace, size_t workspaceIndex,
const MinAndMaxTof &minMaxTof,
const Mantid::HistogramData::Points &points) {
// Create the data twice
auto counts = workspace->counts(workspaceIndex);
std::vector<double> doubledData(2 * counts.size(), 0);
auto doubledDataIterator = doubledData.begin();
std::copy(std::begin(counts), std::end(counts), doubledDataIterator);
std::advance(doubledDataIterator, counts.size());
std::copy(std::begin(counts), std::end(counts), doubledDataIterator);
// Now set everything to zero entires which correspond to TOF which is less
// than minTof and more than maxTof
auto minAndMaxIndex = getMinAndMaxIndex(minMaxTof, points);
if (minMaxTof.minTof != specialTimeOfFlightCutoff) {
setTofBelowLowerBoundToZero(doubledData, minAndMaxIndex.minIndex);
}
if (minMaxTof.maxTof != specialTimeOfFlightCutoff) {
setTofAboveUpperBoundToZero(doubledData, minAndMaxIndex.maxIndex);
}
return Mantid::HistogramData::Counts(doubledData);
}
/**
* Get the workspace indices which correspond to the monitors of a workspace.
* There are three options
* 1. It is mixed
* 2. It is purely a monitor workspace
* 3. There are no moniors
* @param workspace
* @return
*/
std::vector<size_t>
getWorkspaceIndicesForMonitors(Mantid::API::MatrixWorkspace *workspace) {
std::vector<size_t> workspaceIndices;
auto monitorWorkspace = workspace->monitorWorkspace();
if (monitorWorkspace) {
auto numberOfHistograms = monitorWorkspace->getNumberHistograms();
for (size_t index = 0; index < numberOfHistograms; ++index) {
const auto &spectrum = workspace->getSpectrum(index);
auto spectrumNumber = spectrum.getSpectrumNo();
auto workspaceIndex =
workspace->getIndexFromSpectrumNumber(spectrumNumber);
workspaceIndices.emplace_back(workspaceIndex);
}
} else {
auto numberOfHistograms = workspace->getNumberHistograms();
const auto &spectrumInfo = workspace->spectrumInfo();
for (size_t workspaceIndex = 0; workspaceIndex < numberOfHistograms;
++workspaceIndex) {
if (spectrumInfo.isMonitor(workspaceIndex)) {
workspaceIndices.emplace_back(workspaceIndex);
}
}
}
return workspaceIndices;
}
} // namespace
namespace Mantid {
namespace Algorithms {
using Mantid::API::WorkspaceProperty;
using Mantid::Kernel::Direction;
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(UnwrapMonitorsInTOF)
//----------------------------------------------------------------------------------------------
/// Algorithms name for identification. @see Algorithm::name
const std::string UnwrapMonitorsInTOF::name() const {
return "UnwrapMonitorsInTOF";
}
/// Algorithm's version for identification. @see Algorithm::version
int UnwrapMonitorsInTOF::version() const { return 1; }
/// Algorithm's category for identification. @see Algorithm::category
const std::string UnwrapMonitorsInTOF::category() const {
return "CorrectionFunctions\\InstrumentCorrections";
}
/// Algorithm's summary for use in the GUI and help. @see Algorithm::summary
const std::string UnwrapMonitorsInTOF::summary() const {
return "Takes a TOF input workspace that contains 'raw' data and unwraps "
"monitor data "
"according to a specified wavelength range. The monitor spectra are "
"essentially "
"doubled and then trimmed to the specified wavelength range. If no "
"wavelength "
"range is specified (-1), then the doubled data is not trimmed. The "
"units of the output "
"workspace is in TOF. Note that currently only workspaces with "
"linearly binned monitor data "
"can be handled correctly.";
}
//----------------------------------------------------------------------------------------------
/** Initialize the algorithm's properties.
*/
void UnwrapMonitorsInTOF::init() {
declareProperty(
std::make_unique<WorkspaceProperty<Mantid::API::MatrixWorkspace>>(
"InputWorkspace", "", Direction::Input),
"An input workspace.");
declareProperty(
std::make_unique<WorkspaceProperty<Mantid::API::MatrixWorkspace>>(
"OutputWorkspace", "", Direction::Output),
"An output workspace.");
declareProperty<double>("WavelengthMin", specialWavelengthCutoff,
"A lower bound of the wavelength range.");
declareProperty<double>("WavelengthMax", specialWavelengthCutoff,
"An upper bound of the wavelength range.");
}
//----------------------------------------------------------------------------------------------
/** Execute the algorithm.
*/
void UnwrapMonitorsInTOF::exec() {
// The unwrapping happens in three parts
// 1. We duplicate the monitor data, by appending the data to itself again.
// This means
// that at there is an interval in which the data is correct
// 2. Data which is outside of a certain equivalent wavelength range will be
// set to 0
Mantid::API::MatrixWorkspace_sptr inputWorkspace =
getProperty("InputWorkspace");
const double lowerWavelengthLimit = getProperty("WavelengthMin");
const double upperWavelengthLimit = getProperty("WavelengthMax");
auto outputWorkspace =
Mantid::API::MatrixWorkspace_sptr(inputWorkspace->clone());
const auto workspaceIndices =
getWorkspaceIndicesForMonitors(outputWorkspace.get());
const auto &spectrumInfo = outputWorkspace->spectrumInfo();
for (const auto &workspaceIndex : workspaceIndices) {
const auto minMaxTof =
getMinAndMaxTof(outputWorkspace.get(), spectrumInfo, workspaceIndex,
lowerWavelengthLimit, upperWavelengthLimit);
auto points = getPoints(outputWorkspace.get(), workspaceIndex);
auto counts =
getCounts(outputWorkspace.get(), workspaceIndex, minMaxTof, points);
// Get the input histogram
auto inputHistogram = inputWorkspace->histogram(workspaceIndex);
auto spectrumIsHistogramData =
inputHistogram.xMode() ==
Mantid::HistogramData::Histogram::XMode::BinEdges;
if (spectrumIsHistogramData) {
Mantid::HistogramData::BinEdges binEdges(points);
Mantid::HistogramData::Histogram histogram(binEdges, counts);
outputWorkspace->setHistogram(workspaceIndex, histogram);
} else {
Mantid::HistogramData::Histogram histogram(points, counts);
outputWorkspace->setHistogram(workspaceIndex, histogram);
}
}
setProperty("OutputWorkspace", outputWorkspace);
}
/**
* Check the inputs for invalid values
* @returns A map with validation warnings.
*/
std::map<std::string, std::string> UnwrapMonitorsInTOF::validateInputs() {
std::map<std::string, std::string> invalidProperties;
// The lower wavelength boundary needs to be smaller than the upper wavelength
// boundary
const double lowerWavelengthLimit = getProperty("WavelengthMin");
const double upperWavelengthLimit = getProperty("WavelengthMax");
if (lowerWavelengthLimit != specialWavelengthCutoff &&
lowerWavelengthLimit < 0.0) {
invalidProperties["WavelengthMin"] =
"The lower wavelength limit must be set to a positive value.";
}
if (upperWavelengthLimit != specialWavelengthCutoff &&
upperWavelengthLimit < 0.0) {
invalidProperties["WavelengthMax"] =
"The upper wavelength limit must be set to a positive value.";
}
if (lowerWavelengthLimit != specialWavelengthCutoff &&
upperWavelengthLimit != specialWavelengthCutoff &&
lowerWavelengthLimit >= upperWavelengthLimit) {
invalidProperties["WavelengthMin"] = "The lower wavelength limit must be "
"smaller than the upper wavelnegth "
"limit.";
invalidProperties["WavelengthMax"] = "The lower wavelength limit must be "
"smaller than the upper wavelnegth "
"limit.";
}
return invalidProperties;
}
} // namespace Algorithms
} // namespace Mantid