-
Notifications
You must be signed in to change notification settings - Fork 122
/
MaskPeaksWorkspace.cpp
316 lines (282 loc) · 12.5 KB
/
MaskPeaksWorkspace.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
// 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 "MantidCrystal/MaskPeaksWorkspace.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/FunctionFactory.h"
#include "MantidAPI/IPeakFunction.h"
#include "MantidAPI/InstrumentValidator.h"
#include "MantidAPI/TableRow.h"
#include "MantidDataObjects/PeaksWorkspace.h"
#include "MantidDataObjects/TableWorkspace.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/Strings.h"
#include "MantidKernel/VectorHelper.h"
#include <boost/math/special_functions/round.hpp>
namespace Mantid::Crystal {
// Register the class into the algorithm factory
DECLARE_ALGORITHM(MaskPeaksWorkspace)
using namespace Kernel;
using namespace API;
using namespace DataObjects;
using namespace Geometry;
using std::string;
/// Constructor
MaskPeaksWorkspace::MaskPeaksWorkspace() : m_xMin{0}, m_xMax{0}, m_yMin{0}, m_yMax{0}, m_tofMin{0}, m_tofMax{0} {}
/** Initialisation method. Declares properties to be used in algorithm.
*
*/
void MaskPeaksWorkspace::init() {
declareProperty(std::make_unique<WorkspaceProperty<MatrixWorkspace>>("InputWorkspace", "", Direction::Input,
std::make_shared<InstrumentValidator>()),
"A workspace containing one or more rectangular area "
"detectors. Each spectrum needs to correspond to only one "
"pixelID (e.g. no grouping or previous calls to "
"SumNeighbours).");
declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>("InPeaksWorkspace", "", Direction::Input),
"The name of the workspace that will be created. Can replace "
"the input workspace.");
declareProperty("XMin", -2, "Minimum of X (col) Range to mask peak relative to peak's center");
declareProperty("XMax", 2, "Maximum of X (col) Range to mask peak relative to peak's center");
declareProperty("YMin", -2, "Minimum of Y (row) Range to mask peak relative to peak's center");
declareProperty("YMax", 2, "Maximum of Y (row) Range to mask peak relative to peak's center");
declareProperty("TOFMin", EMPTY_DBL(),
"Optional(all TOF if not specified): "
"Minimum TOF relative to peak's "
"center TOF.");
declareProperty("TOFMax", EMPTY_DBL(),
"Optional(all TOF if not specified): "
"Maximum TOF relative to peak's "
"center TOF.");
}
/** Executes the algorithm
*
* @throw Exception::FileError If the grouping file cannot be opened or read
*successfully
*/
void MaskPeaksWorkspace::exec() {
retrieveProperties();
PeaksWorkspace_const_sptr peaksW = getProperty("InPeaksWorkspace");
// To get the workspace index from the detector ID
const detid2index_map pixel_to_wi = m_inputW->getDetectorIDToWorkspaceIndexMap();
// Get some stuff from the input workspace
Geometry::Instrument_const_sptr inst = m_inputW->getInstrument();
// Init a table workspace
DataObjects::TableWorkspace_sptr tablews = std::make_shared<DataObjects::TableWorkspace>();
tablews->addColumn("double", "XMin");
tablews->addColumn("double", "XMax");
tablews->addColumn("str", "SpectraList");
// Loop over peaks
const std::vector<Peak> &peaks = peaksW->getPeaks();
PARALLEL_FOR_IF(Kernel::threadSafe(*m_inputW, *peaksW, *tablews))
for (int i = 0; i < static_cast<int>(peaks.size()); i++) { // NOLINT
PARALLEL_START_INTERRUPT_REGION
const Peak &peak = peaks[i];
// get the peak location on the detector
double col = peak.getCol();
double row = peak.getRow();
int xPeak = boost::math::iround(col) - 1;
int yPeak = boost::math::iround(row) - 1;
g_log.debug() << "Generating information for peak at x=" << xPeak << " y=" << yPeak << "\n";
// the detector component for the peak will have all pixels that we mask
const string &bankName = peak.getBankName();
if (bankName == "None")
continue;
Geometry::IComponent_const_sptr comp = inst->getComponentByName(bankName);
if (!comp) {
g_log.debug() << "Component " + bankName + " does not exist in instrument\n";
continue;
}
// determine the range in time-of-flight
double x0;
double xf;
bool tofRangeSet(false);
size_t wi = this->getWkspIndex(pixel_to_wi, comp, xPeak, yPeak);
if (wi != static_cast<size_t>(EMPTY_INT())) { // scope limit the workspace index
this->getTofRange(x0, xf, peak.getTOF(), m_inputW->x(wi));
tofRangeSet = true;
}
// determine the spectrum numbers to mask
std::set<size_t> spectra;
for (int ix = m_xMin; ix <= m_xMax; ix++) {
for (int iy = m_yMin; iy <= m_yMax; iy++) {
// Find the pixel ID at that XY position on the rectangular detector
size_t wj = this->getWkspIndex(pixel_to_wi, comp, xPeak + ix, yPeak + iy);
if (wj == static_cast<size_t>(EMPTY_INT()))
continue;
spectra.insert(wj);
if (!tofRangeSet) { // scope limit the workspace index
this->getTofRange(x0, xf, peak.getTOF(), m_inputW->x(wj));
tofRangeSet = true;
}
}
}
// sanity check the results
if (!tofRangeSet) {
g_log.warning() << "Failed to set time-of-flight range for peak (x=" << xPeak << ", y=" << yPeak
<< ", tof=" << peak.getTOF() << ")\n";
} else if (spectra.empty()) {
g_log.warning() << "Failed to find spectra for peak (x=" << xPeak << ", y=" << yPeak << ", tof=" << peak.getTOF()
<< ")\n";
continue;
} else
PARALLEL_CRITICAL(tablews) {
// append to the table workspace
API::TableRow newrow = tablews->appendRow();
newrow << x0 << xf << Kernel::Strings::toString(spectra);
}
PARALLEL_END_INTERRUPT_REGION
} // end loop over peaks
PARALLEL_CHECK_INTERRUPT_REGION
// Mask bins
auto maskbinstb = createChildAlgorithm("MaskBinsFromTable", 0.5, 1.0, true);
maskbinstb->setProperty("InputWorkspace", m_inputW);
maskbinstb->setPropertyValue("OutputWorkspace", m_inputW->getName());
maskbinstb->setProperty("MaskingInformation", tablews);
maskbinstb->execute();
}
void MaskPeaksWorkspace::retrieveProperties() {
m_inputW = getProperty("InputWorkspace");
m_xMin = getProperty("XMin");
m_xMax = getProperty("XMax");
if (m_xMin >= m_xMax)
throw std::runtime_error("Must specify Xmin<Xmax");
m_yMin = getProperty("YMin");
m_yMax = getProperty("YMax");
if (m_yMin >= m_yMax)
throw std::runtime_error("Must specify Ymin<Ymax");
// Get the value of TOF range to mask
m_tofMin = getProperty("TOFMin");
m_tofMax = getProperty("TOFMax");
if ((!isEmpty(m_tofMin)) && (!isEmpty(m_tofMax))) {
if (m_tofMin >= m_tofMax)
throw std::runtime_error("Must specify TOFMin < TOFMax");
} else if ((!isEmpty(m_tofMin)) || (!isEmpty(m_tofMax))) // check if only one is empty
{
throw std::runtime_error("Must specify both TOFMin and TOFMax or neither");
}
}
size_t MaskPeaksWorkspace::getWkspIndex(const detid2index_map &pixel_to_wi, const Geometry::IComponent_const_sptr &comp,
const int x, const int y) {
Geometry::RectangularDetector_const_sptr det = std::dynamic_pointer_cast<const Geometry::RectangularDetector>(comp);
Geometry::Instrument_const_sptr Iptr = m_inputW->getInstrument();
if (det) {
if (x >= det->xpixels() || x < 0 || y >= det->ypixels() || y < 0) {
// throw std::runtime_error("Failed to find workspace index for x=" + std::to_string(x) + " y=" +
// std::to_string(y) + "(max x=" + std::to_string(det->xpixels()) +
// ", max y=" + std::to_string(det->ypixels()) + ")"); // Useful for debugging
return EMPTY_INT();
}
int pixelID = det->getAtXY(x, y)->getID();
// Find the corresponding workspace index, if any
auto wiEntry = pixel_to_wi.find(pixelID);
if (wiEntry == pixel_to_wi.end()) {
std::stringstream msg;
msg << "Failed to find workspace index for x=" << x << " y=" << y;
throw std::runtime_error(msg.str());
}
return wiEntry->second;
} else {
std::vector<Geometry::IComponent_const_sptr> children;
std::shared_ptr<const Geometry::ICompAssembly> asmb =
std::dynamic_pointer_cast<const Geometry::ICompAssembly>(comp);
asmb->getChildren(children, false);
std::shared_ptr<const Geometry::ICompAssembly> asmb2 =
std::dynamic_pointer_cast<const Geometry::ICompAssembly>(children[0]);
std::vector<Geometry::IComponent_const_sptr> grandchildren;
asmb2->getChildren(grandchildren, false);
auto NROWS = static_cast<int>(grandchildren.size());
auto NCOLS = static_cast<int>(children.size());
std::ostringstream msg;
if (Iptr->getName().compare("CORELLI") == 0) {
msg << "Instrument is CORELLI\n";
// CORELLI has one extra layer than WISH
std::shared_ptr<const Geometry::ICompAssembly> asmb3 =
std::dynamic_pointer_cast<const Geometry::ICompAssembly>(grandchildren[0]);
std::vector<Geometry::IComponent_const_sptr> greatgrandchildren;
asmb3->getChildren(greatgrandchildren, false);
// update for CORELLI
NCOLS = static_cast<int>(grandchildren.size());
NROWS = static_cast<int>(greatgrandchildren.size());
} else {
msg << "Instrument is WISH\n";
}
// Wish pixels and tubes start at 1 not 0
if (x - 1 >= NCOLS || x - 1 < 0 || y - 1 >= NROWS || y - 1 < 0) {
// useful for future dev in plan
// msg << "--(x,y) = (" << x << "," << y << ")\n"
// << "--NCOLS = " << NCOLS << "\n"
// << "--NROWS = " << NROWS << "\n";
// g_log.warning() << msg.str();
return EMPTY_INT();
}
std::string bankName = comp->getName();
auto it = pixel_to_wi.find(findPixelID(bankName, x, y));
if (it == pixel_to_wi.end())
return EMPTY_INT();
return (it->second);
}
}
/**
* @param tofMin Return value for minimum tof to be masked
* @param tofMax Return value for maximum tof to be masked
* @param tofPeak time-of-flight of the single crystal peak
* @param tof tof-of-flight axis for the spectrum where the peak supposedly
* exists
*/
void MaskPeaksWorkspace::getTofRange(double &tofMin, double &tofMax, const double tofPeak,
const HistogramData::HistogramX &tof) {
tofMin = tof.front();
tofMax = tof.back() - 1;
if (!isEmpty(m_tofMin)) {
tofMin = tofPeak + m_tofMin;
}
if (!isEmpty(m_tofMax)) {
tofMax = tofPeak + m_tofMax;
}
}
/**
* @brief
*
* @param bankName
* @param col
* @param row
* @return int
*/
int MaskPeaksWorkspace::findPixelID(const std::string &bankName, int col, int row) {
Geometry::Instrument_const_sptr Iptr = m_inputW->getInstrument();
std::shared_ptr<const IComponent> parent = Iptr->getComponentByName(bankName);
if (parent->type() == "RectangularDetector") {
std::shared_ptr<const RectangularDetector> RDet = std::dynamic_pointer_cast<const RectangularDetector>(parent);
std::shared_ptr<Detector> pixel = RDet->getAtXY(col, row);
return pixel->getID();
} else if (Iptr->getName().compare("CORELLI") == 0) {
// Checking for CORELLI
// pixel full name example
// /CORELLI/A row/bank10/sixteenpack/tube10/pixel23
// ^ the extra layer that makes CORELLI different from WISH
std::ostringstream pixelString;
pixelString << parent->getFullName() // /CORELLI/A row/bank10
<< "/sixteenpack" // /sixteenpack
<< "/tube" << col // /tube10
<< "/pixel" << row; // /pixel23
std::shared_ptr<const Geometry::IComponent> component = Iptr->getComponentByName(pixelString.str());
std::shared_ptr<const Detector> pixel = std::dynamic_pointer_cast<const Detector>(component);
//
return pixel->getID();
} else {
std::string bankName0 = bankName;
// Only works for WISH
bankName0.erase(0, 4);
std::ostringstream pixelString;
pixelString << Iptr->getName() << "/" << bankName0 << "/" << bankName << "/tube" << std::setw(3)
<< std::setfill('0') << col << "/pixel" << std::setw(4) << std::setfill('0') << row;
std::shared_ptr<const Geometry::IComponent> component = Iptr->getComponentByName(pixelString.str());
std::shared_ptr<const Detector> pixel = std::dynamic_pointer_cast<const Detector>(component);
return pixel->getID();
}
}
} // namespace Mantid::Crystal