-
Notifications
You must be signed in to change notification settings - Fork 122
/
CreatePSDBleedMask.cpp
277 lines (241 loc) · 9.75 KB
/
CreatePSDBleedMask.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
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAlgorithms/CreatePSDBleedMask.h"
#include "MantidAPI/WorkspaceProperty.h"
#include "MantidGeometry/Instrument/DetectorGroup.h"
#include "MantidKernel/BoundedValidator.h"
#include "MantidKernel/MultiThreaded.h"
#include "MantidKernel/NullValidator.h"
#include <cfloat>
#include <iterator>
#include <list>
#include <map>
namespace Mantid {
namespace Algorithms {
// Register the class
DECLARE_ALGORITHM(CreatePSDBleedMask)
const std::string CreatePSDBleedMask::category() const { return "Diagnostics"; }
using API::MatrixWorkspace_sptr;
using API::MatrixWorkspace_const_sptr;
using DataObjects::MaskWorkspace_sptr;
//----------------------------------------------------------------------
// Public methods
//----------------------------------------------------------------------
/// Default constructor
CreatePSDBleedMask::CreatePSDBleedMask() {}
//----------------------------------------------------------------------
// Private methods
//----------------------------------------------------------------------
/// Initialize the algorithm properties
void CreatePSDBleedMask::init() {
using API::WorkspaceProperty;
using Kernel::Direction;
using Kernel::BoundedValidator;
declareProperty(Kernel::make_unique<WorkspaceProperty<>>("InputWorkspace", "",
Direction::Input),
"The name of the input workspace.");
declareProperty(
Kernel::make_unique<WorkspaceProperty<>>("OutputWorkspace", "",
Direction::Output),
"The name of the output MaskWorkspace which will contain the result "
"masks.");
auto mustBePosDbl = boost::make_shared<BoundedValidator<double>>();
mustBePosDbl->setLower(0.0);
declareProperty("MaxTubeFramerate", -1.0, mustBePosDbl,
"The maximum rate allowed for a tube in counts/us/frame.");
auto mustBePosInt = boost::make_shared<BoundedValidator<int>>();
mustBePosInt->setLower(0);
declareProperty("NIgnoredCentralPixels", 80, mustBePosInt,
"The number of pixels about the centre to ignore.");
declareProperty("NumberOfFailures", 0,
boost::make_shared<Kernel::NullValidator>(),
"An output property containing the number of masked tubes",
Direction::Output);
}
/// Execute the algorithm
void CreatePSDBleedMask::exec() {
using Geometry::IDetector_const_sptr;
MatrixWorkspace_const_sptr inputWorkspace = getProperty("InputWorkspace");
// We require the number of good frames. Check that we have this
if (!inputWorkspace->run().hasProperty("goodfrm")) {
throw std::invalid_argument(
"InputWorkspace does not contain the number of \"good frames\".\n"
"(The sample log named: goodfrm with value, specifying number of good "
"frames)");
}
Kernel::PropertyWithValue<int> *frameProp =
dynamic_cast<Kernel::PropertyWithValue<int> *>(
inputWorkspace->run().getProperty("goodfrm"));
if (!frameProp) {
throw std::invalid_argument("InputWorkspace has the number of \"good "
"frames\" property (goodfrm log value)"
"but this property value is not integer.");
}
int goodFrames = (*frameProp)();
// Store the other properties
double maxFramerate = getProperty("MaxTubeFramerate");
// Multiply by the frames to save a division for each bin when we loop over
// them later
double maxRate = maxFramerate * goodFrames;
int numIgnoredPixels = getProperty("NIgnoredCentralPixels");
// This algorithm assumes that the instrument geometry is tube based, i.e. the
// parent CompAssembly
// of the lowest detector in the tree is a "tube" and that all pixels in a
// tube are consecutively ordered
// with respect to spectra number
const int numSpectra =
static_cast<int>(inputWorkspace->getNumberHistograms());
// Keep track of a map of tubes to lists of indices
typedef std::map<Geometry::ComponentID, std::vector<int>> TubeIndex;
TubeIndex tubeMap;
API::Progress progress(this, 0, 1, numSpectra);
// NOTE: This loop is intentionally left unparallelized as the majority of the
// work requires a lock around it which actually slows down the loop.
// Another benefit of keep it serial is losing the need for a call to 'sort'
// when
// performing the bleed test as the list of indices will already be in the
// correct
// order
for (int i = 0; i < numSpectra; ++i) {
IDetector_const_sptr det;
try {
det = inputWorkspace->getDetector(i);
} catch (Kernel::Exception::NotFoundError &) {
continue;
}
if (det->isMonitor())
continue;
boost::shared_ptr<const Geometry::DetectorGroup> group =
boost::dynamic_pointer_cast<const Geometry::DetectorGroup>(det);
if (group) {
det = group->getDetectors().front();
if (!det)
continue;
}
boost::shared_ptr<const Geometry::IComponent> parent = det->getParent();
if (!parent)
continue;
Geometry::ComponentID parentID = parent->getComponentID();
// Already have this component
if (tubeMap.find(parentID) != tubeMap.end()) {
tubeMap[parentID].push_back(i);
}
// New tube
else {
tubeMap.emplace(parentID, TubeIndex::mapped_type(1, i));
}
progress.report();
}
// Now process the tubes in parallel
const int numTubes = static_cast<int>(tubeMap.size());
g_log.information() << "Found " << numTubes << " tubes.\n";
int numSpectraMasked(0), numTubesMasked(0);
// Create a mask workspace for output
MaskWorkspace_sptr outputWorkspace = this->generateEmptyMask(inputWorkspace);
progress.resetNumSteps(numTubes, 0, 1);
PARALLEL_FOR2(inputWorkspace, outputWorkspace)
for (int i = 0; i < numTubes; ++i) {
PARALLEL_START_INTERUPT_REGION
auto current = tubeMap.begin();
std::advance(current, i);
const TubeIndex::mapped_type tubeIndices = current->second;
bool mask = performBleedTest(tubeIndices, inputWorkspace, maxRate,
numIgnoredPixels);
if (mask) {
maskTube(tubeIndices, outputWorkspace);
PARALLEL_ATOMIC
numSpectraMasked += static_cast<int>(tubeIndices.size());
PARALLEL_ATOMIC
numTubesMasked += 1;
}
progress.report("Performing Bleed Test");
PARALLEL_END_INTERUPT_REGION
}
PARALLEL_CHECK_INTERUPT_REGION
g_log.information() << numTubesMasked << " tube(s) failed the bleed tests.";
if (numTubesMasked > 0) {
g_log.information()
<< " The " << numSpectraMasked
<< " spectra have been masked on the output workspace.\n";
} else {
g_log.information() << '\n';
}
setProperty("NumberOfFailures", numSpectraMasked);
setProperty("OutputWorkspace", outputWorkspace);
}
/**
* Process a tube whose indices are given
* @param tubeIndices :: A list of workspace indices that point to members of a
* single tube
* @param inputWS :: The workspace containing the rates or counts for each bin
* @param maxRate :: Maximum allowed rate
* @param numIgnoredPixels :: Number of ignored pixels
* @returns True if the tube is to be masked, false otherwise
*/
bool CreatePSDBleedMask::performBleedTest(
const std::vector<int> &tubeIndices,
API::MatrixWorkspace_const_sptr inputWS, double maxRate,
int numIgnoredPixels) {
// Require ordered pixels so that we can define the centre.
// This of course assumes that the pixel IDs increase monotonically with the
// workspace index
// and that the above loop that searched for the tubes was NOT run in parallel
const size_t numSpectra(tubeIndices.size());
const size_t midIndex(numSpectra / 2);
const size_t topEnd(midIndex - numIgnoredPixels / 2);
const size_t bottomBegin(midIndex + numIgnoredPixels / 2);
/// Is the input a distribution or raw counts. If true then bin width division
/// is necessary when calculating the rate
bool isRawCounts = !(inputWS->isDistribution());
const int numBins = static_cast<int>(inputWS->blocksize());
std::vector<double> totalRate(numBins, 0.0);
size_t top = 0, bot = bottomBegin;
for (; top < topEnd; ++top, ++bot) {
const int topIndex = tubeIndices[top];
const int botIndex = tubeIndices[bot];
auto &topY = inputWS->y(topIndex);
auto &botY = inputWS->y(botIndex);
auto &topX = inputWS->x(topIndex);
auto &botX = inputWS->x(botIndex);
for (int j = 0; j < numBins; ++j) {
double topRate(topY[j]), botRate(botY[j]);
if (isRawCounts) {
topRate /= (topX[j + 1] - topX[j]);
botRate /= (botX[j + 1] - botX[j]);
}
totalRate[j] += topRate + botRate;
// If by now any have hit the allowed maximum then mark this to be masked
if (totalRate[j] > maxRate) {
return true;
}
}
}
if (top != topEnd) {
g_log.error()
<< "Error in tube processing, loop variable has an unexpected value.\n";
throw std::runtime_error(
"top != topEnd in CreatePSDBleedMask::performBleedTest()");
}
if (bot != numSpectra) {
g_log.error()
<< "Error in tube processing, loop variable has an unexpected value.\n";
throw std::runtime_error(
"bot != numSpectra in CreatePSDBleedMask::performBleedTest()");
}
return false;
}
/**
* Mask a tube with the given workspace indices
* @param tubeIndices :: A list of the workspaces indices for the tube
* @param workspace :: The workspace to accumulate the masking
*/
void CreatePSDBleedMask::maskTube(const std::vector<int> &tubeIndices,
API::MatrixWorkspace_sptr workspace) {
const double deadValue(1.0); // delete the data
for (auto tubeIndice : tubeIndices) {
workspace->mutableY(tubeIndice)[0] = deadValue;
}
}
}
}