Mantid
Loading...
Searching...
No Matches
CreateDetectorTable.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2019 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 +
17#include "MantidKernel/Unit.h"
19#include "MantidKernel/V3D.h"
20#include <unordered_map>
21#include <vector>
22
23using namespace Mantid::API;
24using namespace Mantid::Kernel;
25using namespace Mantid::DataObjects;
26using namespace Mantid::Geometry;
27
28namespace Mantid::Algorithms {
29// Register the algorithm into the AlgorithmFactory
30DECLARE_ALGORITHM(CreateDetectorTable)
31
32void CreateDetectorTable::init() {
33 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("InputWorkspace", "", Direction::Input),
34 "The name of the workspace to take as input.");
35
36 declareProperty(std::make_unique<ArrayProperty<int>>("WorkspaceIndices", Direction::Input),
37 "If left empty then all workspace indices are used.");
38 setPropertySettings("WorkspaceIndices",
39 std::make_unique<EnabledWhenWorkspaceIsType<MatrixWorkspace>>("InputWorkspace", true));
40
41 declareProperty("IncludeData", false, "Include the first value from each spectrum.");
42 setPropertySettings("IncludeData",
43 std::make_unique<EnabledWhenWorkspaceIsType<MatrixWorkspace>>("InputWorkspace", true));
44
45 declareProperty<bool>("IncludeDetectorPosition", false,
46 "Include the absolute position of the detector group for each spectrum.", Direction::Input);
47 setPropertySettings("IncludeDetectorPosition",
48 std::make_unique<EnabledWhenWorkspaceIsType<MatrixWorkspace>>("InputWorkspace", true));
49
50 declareProperty<bool>("OneRowPerDetectorID", false,
51 "Order rows in table by detector IDs, with each detector ID having its own row. When "
52 "OneRowPerDetectorID=true the table iterates over all detector IDs, so WorkspaceIndices is "
53 "ignored and the row count equals the detector count.",
55 setPropertySettings("OneRowPerDetectorID",
56 std::make_unique<EnabledWhenWorkspaceIsType<MatrixWorkspace>>("InputWorkspace", true));
57
58 declareProperty(std::make_unique<WorkspaceProperty<TableWorkspace>>("DetectorTableWorkspace", "", Direction::Output,
60 "The name of the outputted detector table workspace, if left empty then "
61 "the input workspace name + \"-Detectors\" is used.");
62}
63
65 API::Workspace_sptr inputWs = getProperty("InputWorkspace");
66 includeData = getProperty("IncludeData");
67 workspaceIndices = getProperty("WorkspaceIndices");
68 includeDetectorPosition = getProperty("IncludeDetectorPosition");
69 oneRowPerDetectorID = getProperty("OneRowPerDetectorID");
70
71 if (auto peaks = std::dynamic_pointer_cast<IPeaksWorkspace>(inputWs)) {
72 table = peaks->createDetectorTable();
74 return;
75 }
76
77 if ((ws = std::dynamic_pointer_cast<MatrixWorkspace>(inputWs))) {
78 if (ws->getInstrument()->getSample() == nullptr) {
79 throw std::runtime_error("Matrix workspace has no instrument information");
80 }
81 setup();
85 } else {
87 }
89 return;
90 }
91
92 throw std::runtime_error("Detector table can only be created for matrix and peaks workspaces.");
93}
94
96 if (table == nullptr) {
97 throw std::runtime_error("Unknown error while creating detector table workspace");
98 }
99 API::Workspace_sptr inputWs = getProperty("InputWorkspace");
100 if (getPropertyValue("DetectorTableWorkspace") == "") {
101 setPropertyValue("DetectorTableWorkspace", inputWs->getName() + "-Detectors");
102 }
103 setProperty("DetectorTableWorkspace", table);
104}
105
106/*
107 * Validate the input parameters
108 * @returns map with keys corresponding to properties with errors and values
109 * containing the error messages.
110 */
111std::map<std::string, std::string> CreateDetectorTable::validateInputs() {
112 // create the map
113 std::map<std::string, std::string> validationOutput;
114
115 Workspace_sptr inputWS = getProperty("InputWorkspace");
116 const auto matrix = std::dynamic_pointer_cast<MatrixWorkspace>(inputWS);
117
118 if (matrix) {
119 const int numSpectra = static_cast<int>(matrix->getNumberHistograms());
120 const std::vector<int> indices = getProperty("WorkspaceIndices");
121
122 if (std::any_of(indices.cbegin(), indices.cend(),
123 [numSpectra](const auto index) { return (index >= numSpectra) || (index < 0); })) {
124 validationOutput["WorkspaceIndices"] = "One or more indices out of range of available spectra.";
125 }
126 }
127
128 return validationOutput;
129}
130
132
133 isScanning = ws->detectorInfo().isScanning();
134
135 spectrumInfo = &ws->spectrumInfo();
136 detectorInfo = &ws->detectorInfo();
137
138 // check if efixed value is available
139 calcQ = true;
140 if (spectrumInfo->hasDetectors(0)) {
141 try {
142 ws->getEFixed(spectrumInfo->firstDetectorID(0));
143 } catch (std::invalid_argument &) {
144 calcQ = false;
145 } catch (std::runtime_error &) {
146 calcQ = false;
147 }
148 } else {
149 // No detectors available
150 calcQ = false;
151 }
152
153 hasDiffConstants = (ws->getEMode() == DeltaEMode::Elastic);
154
155 beamAxisIndex = ws->getInstrument()->getReferenceFrame()->pointingAlongBeam();
156 sampleDist = ws->getInstrument()->getSample()->getPos()[beamAxisIndex];
157
158 if (workspaceIndices.empty()) {
159 workspaceIndices = std::vector<int>(ws->getNumberHistograms());
160 std::iota(workspaceIndices.begin(), workspaceIndices.end(), 0);
161 }
162
163 showSignedTwoTheta = retrieveSignedThetaParameter(); // If true, signedVersion of the two theta
164 table = WorkspaceFactory::Instance().createTable("TableWorkspace");
165}
166
168 std::vector<std::pair<std::string, std::string>> colNames;
169 colNames.emplace_back("int", "Index");
170 colNames.emplace_back("int", "Spectrum No");
172 colNames.emplace_back("int", "Detector ID(s)");
173 } else {
174 colNames.emplace_back("str", "Detector ID(s)");
175 }
176 if (isScanning)
177 colNames.emplace_back("str", "Time Indexes");
178 if (includeData) {
179 colNames.emplace_back("double", "Data Value");
180 colNames.emplace_back("double", "Data Error");
181 }
182
183 colNames.emplace_back("double", "R");
184 colNames.emplace_back("double", "Theta");
185 if (calcQ) {
186 colNames.emplace_back("double", "Q elastic");
187 }
188 colNames.emplace_back("double", "Phi");
189 colNames.emplace_back("str", "Monitor");
190 if (hasDiffConstants) {
191 colNames.emplace_back("double", "DIFA");
192 colNames.emplace_back("double", "DIFC");
193 colNames.emplace_back("double", "DIFC - Uncalibrated");
194 colNames.emplace_back("double", "TZERO");
195 }
197 colNames.emplace_back("V3D", "Position");
198 }
199
200 m_columnCache.clear();
201 m_columnCache.reserve(colNames.size());
202 // Set the column names
203 for (size_t col = 0; col < colNames.size(); ++col) {
204 auto column = table->addColumn(colNames.at(col).first, colNames.at(col).second);
205 m_columnCache.push_back(column);
206 column->setPlotType(0);
207 }
208 return;
209}
210
211void CreateDetectorTable::getSphericalCoordinates(size_t wsIndex, double &R, double &theta, double &phi) {
212 // theta used as a dummy variable
213 // Note: phi is the angle around Z, not necessarily the beam direction.
214 spectrumInfo->position(wsIndex).getSpherical(R, theta, phi);
215 // R is actually L2 (same as R if sample is at (0,0,0)), except for
216 // monitors which are handled below.
217 R = spectrumInfo->l2(wsIndex);
218 // Theta is actually 'twoTheta' for detectors (twice the scattering
219 // angle), if Z is the beam direction this corresponds to theta in
220 // spherical coordinates.
221 // For monitors we follow historic behaviour and display theta
222 if (!spectrumInfo->isMonitor(wsIndex)) {
223 try {
225 theta *= 180.0 / M_PI; // To degrees
226 } catch (const std::exception &ex) {
227 // Log the error and leave theta as it is
228 g_log.error(ex.what());
229 }
230 } else {
231 const auto dist = spectrumInfo->position(wsIndex)[beamAxisIndex];
232 theta = sampleDist > dist ? 180.0 : 0.0;
233
234 // If monitors are before the sample in the beam, DetectorInfo
235 // returns a negative l2 distance.
236 R = std::abs(R);
237 }
238}
239
240const std::string CreateDetectorTable::getTimeIndexes(size_t wsIndex) {
241 if (!isScanning) {
242 return "0";
243 }
244 std::set<int> timeIndexSet;
245 for (const auto &def : spectrumInfo->spectrumDefinition(wsIndex)) {
246 timeIndexSet.insert(int(def.second));
247 }
248 const std::string timeIndexes = createTruncatedList(timeIndexSet);
249 return timeIndexes;
250}
251
252double CreateDetectorTable::getQ(size_t wsIndex) {
253 if (!calcQ) {
254 return std::nan("");
255 }
256 if (spectrumInfo->isMonitor(wsIndex)) {
257 // twoTheta is not defined for monitors.
258 return std::nan("");
259 }
260 try {
261 // Get unsigned theta and efixed value
262 double efixed = ws->getEFixed(spectrumInfo->firstDetectorID(wsIndex));
263 double usignTheta = spectrumInfo->twoTheta(wsIndex) * 0.5;
264 double q = UnitConversion::convertToElasticQ(usignTheta, efixed);
265 return q;
266 } catch (std::runtime_error &) {
267 // No Efixed
268 return std::nan("");
269 }
270}
271
272void CreateDetectorTable::getDiffConst(size_t wsIndex, double &difa, double &difc, double &difcUnc, double &tzero) {
273 if (!hasDiffConstants) {
274 difa = difc = difcUnc = tzero = 0;
275 return;
276 }
277 if (spectrumInfo->isMonitor(wsIndex)) {
278 difa = difc = difcUnc = tzero = 0;
279 return;
280 }
281 auto diffConsts = spectrumInfo->diffractometerConstants(wsIndex);
282 // map will create an entry with zero value if not present already
283 difa = diffConsts[UnitParams::difa];
284 difc = diffConsts[UnitParams::difc];
285 difcUnc = spectrumInfo->difcUncalibrated(wsIndex);
286 tzero = diffConsts[UnitParams::tzero];
287}
288
290 size_t columnIndex = 0;
291 m_columnCache[columnIndex++]->cell<int>(row) = static_cast<int>(data.wsIndex);
292 m_columnCache[columnIndex++]->cell<int>(row) = data.specNo;
294 // Populate detector column with first det id in set
295 m_columnCache[columnIndex++]->cell<int>(row) = static_cast<int>(*data.detIds.begin());
296 } else {
297 // Populate detector column with a truncated string of all det ids
298 m_columnCache[columnIndex++]->cell<std::string>(row) = createTruncatedList(data.detIds);
299 }
300 if (isScanning) {
301 m_columnCache[columnIndex++]->cell<std::string>(row) = data.timeIndexes;
302 }
303 if (includeData) {
304 m_columnCache[columnIndex++]->cell<double>(row) = data.dataY0;
305 m_columnCache[columnIndex++]->cell<double>(row) = data.dataE0;
306 }
307 m_columnCache[columnIndex++]->cell<double>(row) = data.R;
308 m_columnCache[columnIndex++]->cell<double>(row) = data.theta;
309 if (calcQ) {
310 m_columnCache[columnIndex++]->cell<double>(row) = data.q;
311 }
312 m_columnCache[columnIndex++]->cell<double>(row) = data.phi;
313 m_columnCache[columnIndex++]->cell<std::string>(row) = data.isMonitor;
314 if (hasDiffConstants) {
315 m_columnCache[columnIndex++]->cell<double>(row) = data.difa;
316 m_columnCache[columnIndex++]->cell<double>(row) = data.difc;
317 m_columnCache[columnIndex++]->cell<double>(row) = data.difcUnc;
318 m_columnCache[columnIndex++]->cell<double>(row) = data.tzero;
319 }
321 m_columnCache[columnIndex++]->cell<Kernel::V3D>(row) = data.detPosition;
322 }
323}
324
326 const auto firstSpectraWithDetectors =
327 std::find_if(workspaceIndices.begin(), workspaceIndices.end(),
328 [this](const auto idx) { return spectrumInfo->hasDetectors(static_cast<size_t>(idx)); });
329 if (firstSpectraWithDetectors == workspaceIndices.end()) {
330 return false;
331 }
332 const std::vector<std::string> &parameters = spectrumInfo->detector(static_cast<size_t>(*firstSpectraWithDetectors))
333 .getStringParameter("show-signed-theta", true); // recursive
334 return (!parameters.empty() && find(parameters.begin(), parameters.end(), "Always") != parameters.end());
335}
336
338 DetectorRowData data;
339
340 // Geometry
341 if (!spectrumInfo->hasDetectors(wsIndex))
342 throw std::runtime_error("No detectors found.");
343
344 // wsIndex
345 data.wsIndex = static_cast<int>(wsIndex);
346 // Spec No
347 auto &spectrum = ws->getSpectrum(wsIndex);
348 data.specNo = spectrum.getSpectrumNo();
349 data.detIds = dynamic_cast<const std::set<int> &>(spectrum.getDetectorIDs());
350 // Time indexes
351 data.timeIndexes = getTimeIndexes(wsIndex);
352 // data Y/E
353 data.dataY0 = ws->y(wsIndex)[0];
354 data.dataE0 = ws->e(wsIndex)[0];
355 // R, Theta, Phi
356 getSphericalCoordinates(wsIndex, data.R, data.theta, data.phi);
357 // Q
358 data.q = getQ(wsIndex);
359 // Is monitor
360 data.isMonitor = spectrumInfo->isMonitor(wsIndex) ? "yes" : "no";
361 // Diff consts
362 getDiffConst(wsIndex, data.difa, data.difc, data.difcUnc, data.tzero);
363 // Detector position
364 data.detPosition = spectrumInfo->position(wsIndex);
365 return data;
366}
367
369
370 table->setRowCount(workspaceIndices.size());
371
373 for (int row = 0; row < static_cast<int>(workspaceIndices.size()); row++) {
375
376 auto wsIndex = static_cast<size_t>(workspaceIndices[static_cast<size_t>(row)]);
377
378 try {
379 auto data = calculateWsIdxData(wsIndex);
380 writeRowToTable(row, data);
381
382 } catch (const std::exception &) {
383 DetectorRowData errorData;
384 errorData.wsIndex = static_cast<int>(wsIndex);
385 errorData.specNo = -1;
386 errorData.detIds = {0};
387 errorData.timeIndexes = "0";
388 errorData.dataY0 = ws->y(wsIndex)[0];
389 errorData.dataE0 = ws->e(wsIndex)[0];
390 errorData.isMonitor = "n/a";
391 writeRowToTable(row, errorData);
392 } // End catch for no spectrum
394 }
396}
397
399 // Pre-build: detId → row index in final table
400 std::unordered_map<int, size_t> detIdToRow;
401 const auto &wsDetIds = detectorInfo->detectorIDs();
402 detIdToRow.reserve(wsDetIds.size());
403 for (size_t r = 0; r < wsDetIds.size(); ++r)
404 detIdToRow[wsDetIds[r]] = r;
405
406 std::vector<DetectorRowData> rowData(wsDetIds.size());
407 // uint8_t avoids the bit-packing (multiple bools in one byte) of vector<bool>,
408 // which would cause data races when parallel threads write to adjacent indices
409 // in the same byte.
410 std::vector<uint8_t> writtenToRow(wsDetIds.size(), 0);
411
413 for (int i = 0; i < static_cast<int>(workspaceIndices.size()); i++) {
415
416 auto wsIndex = static_cast<size_t>(workspaceIndices[static_cast<size_t>(i)]);
417 DetectorRowData data;
418 // TODO: Not entirely sure this catch is necessary
419 // Only necessary if there is a det id in detectorInfo->detectorIDs()
420 // that somehow triggers the crash, but that seems unlikely
421 try {
422 data = calculateWsIdxData(wsIndex);
423 } catch (const std::exception &) {
424 data.wsIndex = static_cast<int>(wsIndex);
425 data.specNo = -1;
426 data.detIds = {0};
427 data.timeIndexes = "0";
428 data.dataY0 = ws->y(wsIndex)[0];
429 data.dataE0 = ws->e(wsIndex)[0];
430 data.isMonitor = "n/a";
431 } // End catch for no spectrum
432
433 auto detIds = dynamic_cast<const std::set<int> &>(ws->getSpectrum(wsIndex).getDetectorIDs());
434 for (int detId : detIds) {
435 auto it = detIdToRow.find(detId);
436 if (it != detIdToRow.end()) {
437 rowData[it->second] = data;
438 rowData[it->second].detIds = {detId};
439 writtenToRow[it->second] = true;
440 }
441 }
443 }
445
446 // Write rows in order of component index
447 // Number of rows matches number of detectorIDs exactly
448 const auto &workspaceDetectorIds = detectorInfo->detectorIDs();
449 table->setRowCount(workspaceDetectorIds.size());
450 for (int i = 0; i < static_cast<int>(rowData.size()); ++i) {
451 if (writtenToRow[i]) {
452 writeRowToTable(i, rowData[i]);
453 } else {
454 DetectorRowData errorData;
455 errorData.wsIndex = -1;
456 errorData.specNo = -1;
457 errorData.detIds = {wsDetIds[i]};
458 errorData.timeIndexes = "0";
459 errorData.dataY0 = 0;
460 errorData.dataE0 = 0;
461 errorData.isMonitor = "n/a";
462 writeRowToTable(i, std::move(errorData));
463 }
464 }
465}
466
476std::string createTruncatedList(const std::set<int> &elements) {
477 std::string truncated;
478 size_t ndets = elements.size();
479 auto iter = elements.begin();
480 auto itEnd = elements.end();
481 if (ndets > 10) {
482 const Mantid::detid_t first{*iter++}, second{*iter++};
483 truncated = std::to_string(first) + "," + std::to_string(second) + "...(" + std::to_string(ndets - 4) + " more)...";
484 auto revIter = elements.rbegin();
485 const Mantid::detid_t last{*revIter++}, lastm1{*revIter++};
486 truncated += std::to_string(lastm1) + "," + std::to_string(last);
487 } else {
488 for (; iter != itEnd; ++iter) {
489 truncated += std::to_string(*iter) + ",";
490 }
491
492 if (!truncated.empty()) {
493 truncated.pop_back(); // Drop last comma
494 }
495 }
496
497 return truncated;
498}
499
500} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:547
std::map< DeltaEMode::Type, std::string > index
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:426
void setPropertyValue(const std::string &name, const std::string &value) override
Set the value of a property by string N.B.
Show a property as enabled when the workspace pointed to by another is of a given type.
double signedTwoTheta(const size_t index) const
Returns the signed scattering angle 2 theta in radians (angle w.r.t.
bool isMonitor(const size_t index) const
Returns true if the detector(s) associated with the spectrum are monitors.
Kernel::UnitParametersMap diffractometerConstants(const size_t index, std::vector< detid_t > &uncalibratedDets) const
Calculate average diffractometer constants (DIFA, DIFC, TZERO) of detectors associated with this spec...
bool hasDetectors(const size_t index) const
Returns true if the spectrum is associated with detectors in the instrument.
detid_t firstDetectorID(const size_t index) const
Returns the detector ID of the first detector of the spectrum with given index.
double twoTheta(const size_t index) const
Returns the scattering angle 2 theta in radians (angle w.r.t.
double difcUncalibrated(const size_t index) const
Calculate average uncalibrated DIFC value of detectors associated with this spectrum.
Kernel::V3D position(const size_t index) const
Returns the position of the spectrum with given index.
double l2(const size_t index) const
Returns L2 (distance from sample to spectrum).
const SpectrumDefinition & spectrumDefinition(const size_t index) const
Returns a const reference to the SpectrumDefinition of the spectrum.
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.
void setup()
Creates table workspace of detector information from a given workspace.
const std::string getTimeIndexes(size_t wsIndex)
void writeRowToTable(const int row, const DetectorRowData &data)
void getSphericalCoordinates(size_t wsIndex, double &R, double &theta, double &phi)
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
std::vector< API::Column_sptr > m_columnCache
DetectorRowData calculateWsIdxData(size_t wsIndex)
void getDiffConst(size_t wsIndex, double &difa, double &difc, double &difcUnc, double &tzero)
const Geometry::DetectorInfo * detectorInfo
const std::vector< detid_t > & detectorIDs() const
Returns a sorted vector of all detector IDs.
virtual std::vector< std::string > getStringParameter(const std::string &pname, bool recursive=true) const =0
Get a parameter defined as a string.
Support for a property that holds an array of values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
static double convertToElasticQ(const double theta, const double efixed)
Convert to ElasticQ from Energy.
Class for 3D vectors.
Definition V3D.h:35
void getSpherical(double &R, double &theta, double &phi) const noexcept
Return the vector's position in spherical coordinates.
Definition V3D.cpp:116
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::string createTruncatedList(const std::set< int > &elements)
Converts a list to a string, shortened if necessary.
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
int32_t detid_t
Typedef for a detector ID.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:54
@ Output
An output workspace.
Definition Property.h:55