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 std::shared_ptr<const IDetector> detector(&spectrumInfo->detector(0), Mantid::NoDeleting());
143 ws->getEFixed(detector);
144 } catch (std::invalid_argument &) {
145 calcQ = false;
146 } catch (std::runtime_error &) {
147 calcQ = false;
148 }
149 } else {
150 // No detectors available
151 calcQ = false;
152 }
153
154 hasDiffConstants = (ws->getEMode() == DeltaEMode::Elastic);
155
156 beamAxisIndex = ws->getInstrument()->getReferenceFrame()->pointingAlongBeam();
157 sampleDist = ws->getInstrument()->getSample()->getPos()[beamAxisIndex];
158
159 if (workspaceIndices.empty()) {
160 workspaceIndices = std::vector<int>(ws->getNumberHistograms());
161 std::iota(workspaceIndices.begin(), workspaceIndices.end(), 0);
162 }
163
164 showSignedTwoTheta = retrieveSignedThetaParameter(); // If true, signedVersion of the two theta
165 table = WorkspaceFactory::Instance().createTable("TableWorkspace");
166}
167
169 std::vector<std::pair<std::string, std::string>> colNames;
170 colNames.emplace_back("int", "Index");
171 colNames.emplace_back("int", "Spectrum No");
173 colNames.emplace_back("int", "Detector ID(s)");
174 } else {
175 colNames.emplace_back("str", "Detector ID(s)");
176 }
177 if (isScanning)
178 colNames.emplace_back("str", "Time Indexes");
179 if (includeData) {
180 colNames.emplace_back("double", "Data Value");
181 colNames.emplace_back("double", "Data Error");
182 }
183
184 colNames.emplace_back("double", "R");
185 colNames.emplace_back("double", "Theta");
186 if (calcQ) {
187 colNames.emplace_back("double", "Q elastic");
188 }
189 colNames.emplace_back("double", "Phi");
190 colNames.emplace_back("str", "Monitor");
191 if (hasDiffConstants) {
192 colNames.emplace_back("double", "DIFA");
193 colNames.emplace_back("double", "DIFC");
194 colNames.emplace_back("double", "DIFC - Uncalibrated");
195 colNames.emplace_back("double", "TZERO");
196 }
198 colNames.emplace_back("V3D", "Position");
199 }
200
201 m_columnCache.clear();
202 m_columnCache.reserve(colNames.size());
203 // Set the column names
204 for (size_t col = 0; col < colNames.size(); ++col) {
205 auto column = table->addColumn(colNames.at(col).first, colNames.at(col).second);
206 m_columnCache.push_back(column);
207 column->setPlotType(0);
208 }
209 return;
210}
211
212void CreateDetectorTable::getSphericalCoordinates(size_t wsIndex, double &R, double &theta, double &phi) {
213 // theta used as a dummy variable
214 // Note: phi is the angle around Z, not necessarily the beam direction.
215 spectrumInfo->position(wsIndex).getSpherical(R, theta, phi);
216 // R is actually L2 (same as R if sample is at (0,0,0)), except for
217 // monitors which are handled below.
218 R = spectrumInfo->l2(wsIndex);
219 // Theta is actually 'twoTheta' for detectors (twice the scattering
220 // angle), if Z is the beam direction this corresponds to theta in
221 // spherical coordinates.
222 // For monitors we follow historic behaviour and display theta
223 if (!spectrumInfo->isMonitor(wsIndex)) {
224 try {
226 theta *= 180.0 / M_PI; // To degrees
227 } catch (const std::exception &ex) {
228 // Log the error and leave theta as it is
229 g_log.error(ex.what());
230 }
231 } else {
232 const auto dist = spectrumInfo->position(wsIndex)[beamAxisIndex];
233 theta = sampleDist > dist ? 180.0 : 0.0;
234
235 // If monitors are before the sample in the beam, DetectorInfo
236 // returns a negative l2 distance.
237 R = std::abs(R);
238 }
239}
240
241const std::string CreateDetectorTable::getTimeIndexes(size_t wsIndex) {
242 if (!isScanning) {
243 return "0";
244 }
245 std::set<int> timeIndexSet;
246 for (const auto &def : spectrumInfo->spectrumDefinition(wsIndex)) {
247 timeIndexSet.insert(int(def.second));
248 }
249 const std::string timeIndexes = createTruncatedList(timeIndexSet);
250 return timeIndexes;
251}
252
253double CreateDetectorTable::getQ(size_t wsIndex) {
254 if (!calcQ) {
255 return std::nan("");
256 }
257 if (spectrumInfo->isMonitor(wsIndex)) {
258 // twoTheta is not defined for monitors.
259 return std::nan("");
260 }
261 try {
262 // Get unsigned theta and efixed value
264 double efixed = ws->getEFixed(det);
265 double usignTheta = spectrumInfo->twoTheta(wsIndex) * 0.5;
266 double q = UnitConversion::convertToElasticQ(usignTheta, efixed);
267 return q;
268 } catch (std::runtime_error &) {
269 // No Efixed
270 return std::nan("");
271 }
272}
273
274void CreateDetectorTable::getDiffConst(size_t wsIndex, double &difa, double &difc, double &difcUnc, double &tzero) {
275 if (!hasDiffConstants) {
276 difa = difc = difcUnc = tzero = 0;
277 return;
278 }
279 if (spectrumInfo->isMonitor(wsIndex)) {
280 difa = difc = difcUnc = tzero = 0;
281 return;
282 }
283 auto diffConsts = spectrumInfo->diffractometerConstants(wsIndex);
284 // map will create an entry with zero value if not present already
285 difa = diffConsts[UnitParams::difa];
286 difc = diffConsts[UnitParams::difc];
287 difcUnc = spectrumInfo->difcUncalibrated(wsIndex);
288 tzero = diffConsts[UnitParams::tzero];
289}
290
292 size_t columnIndex = 0;
293 m_columnCache[columnIndex++]->cell<int>(row) = static_cast<int>(data.wsIndex);
294 m_columnCache[columnIndex++]->cell<int>(row) = data.specNo;
296 // Populate detector column with first det id in set
297 m_columnCache[columnIndex++]->cell<int>(row) = static_cast<int>(*data.detIds.begin());
298 } else {
299 // Populate detector column with a truncated string of all det ids
300 m_columnCache[columnIndex++]->cell<std::string>(row) = createTruncatedList(data.detIds);
301 }
302 if (isScanning) {
303 m_columnCache[columnIndex++]->cell<std::string>(row) = data.timeIndexes;
304 }
305 if (includeData) {
306 m_columnCache[columnIndex++]->cell<double>(row) = data.dataY0;
307 m_columnCache[columnIndex++]->cell<double>(row) = data.dataE0;
308 }
309 m_columnCache[columnIndex++]->cell<double>(row) = data.R;
310 m_columnCache[columnIndex++]->cell<double>(row) = data.theta;
311 if (calcQ) {
312 m_columnCache[columnIndex++]->cell<double>(row) = data.q;
313 }
314 m_columnCache[columnIndex++]->cell<double>(row) = data.phi;
315 m_columnCache[columnIndex++]->cell<std::string>(row) = data.isMonitor;
316 if (hasDiffConstants) {
317 m_columnCache[columnIndex++]->cell<double>(row) = data.difa;
318 m_columnCache[columnIndex++]->cell<double>(row) = data.difc;
319 m_columnCache[columnIndex++]->cell<double>(row) = data.difcUnc;
320 m_columnCache[columnIndex++]->cell<double>(row) = data.tzero;
321 }
323 m_columnCache[columnIndex++]->cell<Kernel::V3D>(row) = data.detPosition;
324 }
325}
326
328 const auto firstSpectraWithDetectors =
329 std::find_if(workspaceIndices.begin(), workspaceIndices.end(),
330 [this](const auto idx) { return spectrumInfo->hasDetectors(static_cast<size_t>(idx)); });
331 if (firstSpectraWithDetectors == workspaceIndices.end()) {
332 return false;
333 }
334 const std::vector<std::string> &parameters = spectrumInfo->detector(static_cast<size_t>(*firstSpectraWithDetectors))
335 .getStringParameter("show-signed-theta", true); // recursive
336 return (!parameters.empty() && find(parameters.begin(), parameters.end(), "Always") != parameters.end());
337}
338
340 DetectorRowData data;
341
342 // Geometry
343 if (!spectrumInfo->hasDetectors(wsIndex))
344 throw std::runtime_error("No detectors found.");
345
346 // wsIndex
347 data.wsIndex = static_cast<int>(wsIndex);
348 // Spec No
349 auto &spectrum = ws->getSpectrum(wsIndex);
350 data.specNo = spectrum.getSpectrumNo();
351 data.detIds = dynamic_cast<const std::set<int> &>(spectrum.getDetectorIDs());
352 // Time indexes
353 data.timeIndexes = getTimeIndexes(wsIndex);
354 // data Y/E
355 data.dataY0 = ws->y(wsIndex)[0];
356 data.dataE0 = ws->e(wsIndex)[0];
357 // R, Theta, Phi
358 getSphericalCoordinates(wsIndex, data.R, data.theta, data.phi);
359 // Q
360 data.q = getQ(wsIndex);
361 // Is monitor
362 data.isMonitor = spectrumInfo->isMonitor(wsIndex) ? "yes" : "no";
363 // Diff consts
364 getDiffConst(wsIndex, data.difa, data.difc, data.difcUnc, data.tzero);
365 // Detector position
366 data.detPosition = spectrumInfo->position(wsIndex);
367 return data;
368}
369
371
372 table->setRowCount(workspaceIndices.size());
373
375 for (int row = 0; row < static_cast<int>(workspaceIndices.size()); row++) {
377
378 auto wsIndex = static_cast<size_t>(workspaceIndices[static_cast<size_t>(row)]);
379
380 try {
381 auto data = calculateWsIdxData(wsIndex);
382 writeRowToTable(row, data);
383
384 } catch (const std::exception &) {
385 DetectorRowData errorData;
386 errorData.wsIndex = static_cast<int>(wsIndex);
387 errorData.specNo = -1;
388 errorData.detIds = {0};
389 errorData.timeIndexes = "0";
390 errorData.dataY0 = ws->y(wsIndex)[0];
391 errorData.dataE0 = ws->e(wsIndex)[0];
392 errorData.isMonitor = "n/a";
393 writeRowToTable(row, errorData);
394 } // End catch for no spectrum
396 }
398}
399
401 // Pre-build: detId → row index in final table
402 std::unordered_map<int, size_t> detIdToRow;
403 const auto &wsDetIds = detectorInfo->detectorIDs();
404 detIdToRow.reserve(wsDetIds.size());
405 for (size_t r = 0; r < wsDetIds.size(); ++r)
406 detIdToRow[wsDetIds[r]] = r;
407
408 std::vector<DetectorRowData> rowData(wsDetIds.size());
409 // uint8_t avoids the bit-packing (multiple bools in one byte) of vector<bool>,
410 // which would cause data races when parallel threads write to adjacent indices
411 // in the same byte.
412 std::vector<uint8_t> writtenToRow(wsDetIds.size(), 0);
413
415 for (int i = 0; i < static_cast<int>(workspaceIndices.size()); i++) {
417
418 auto wsIndex = static_cast<size_t>(workspaceIndices[static_cast<size_t>(i)]);
419 DetectorRowData data;
420 // TODO: Not entirely sure this catch is necessary
421 // Only necessary if there is a det id in detectorInfo->detectorIDs()
422 // that somehow triggers the crash, but that seems unlikely
423 try {
424 data = calculateWsIdxData(wsIndex);
425 } catch (const std::exception &) {
426 data.wsIndex = static_cast<int>(wsIndex);
427 data.specNo = -1;
428 data.detIds = {0};
429 data.timeIndexes = "0";
430 data.dataY0 = ws->y(wsIndex)[0];
431 data.dataE0 = ws->e(wsIndex)[0];
432 data.isMonitor = "n/a";
433 } // End catch for no spectrum
434
435 auto detIds = dynamic_cast<const std::set<int> &>(ws->getSpectrum(wsIndex).getDetectorIDs());
436 for (int detId : detIds) {
437 auto it = detIdToRow.find(detId);
438 if (it != detIdToRow.end()) {
439 rowData[it->second] = data;
440 rowData[it->second].detIds = {detId};
441 writtenToRow[it->second] = true;
442 }
443 }
445 }
447
448 // Write rows in order of component index
449 // Number of rows matches number of detectorIDs exactly
450 const auto &workspaceDetectorIds = detectorInfo->detectorIDs();
451 table->setRowCount(workspaceDetectorIds.size());
452 for (int i = 0; i < static_cast<int>(rowData.size()); ++i) {
453 if (writtenToRow[i]) {
454 writeRowToTable(i, rowData[i]);
455 } else {
456 DetectorRowData errorData;
457 errorData.wsIndex = -1;
458 errorData.specNo = -1;
459 errorData.detIds = {wsDetIds[i]};
460 errorData.timeIndexes = "0";
461 errorData.dataY0 = 0;
462 errorData.dataE0 = 0;
463 errorData.isMonitor = "n/a";
464 writeRowToTable(i, std::move(errorData));
465 }
466 }
467}
468
478std::string createTruncatedList(const std::set<int> &elements) {
479 std::string truncated;
480 size_t ndets = elements.size();
481 auto iter = elements.begin();
482 auto itEnd = elements.end();
483 if (ndets > 10) {
484 const Mantid::detid_t first{*iter++}, second{*iter++};
485 truncated = std::to_string(first) + "," + std::to_string(second) + "...(" + std::to_string(ndets - 4) + " more)...";
486 auto revIter = elements.rbegin();
487 const Mantid::detid_t last{*revIter++}, lastm1{*revIter++};
488 truncated += std::to_string(lastm1) + "," + std::to_string(last);
489 } else {
490 for (; iter != itEnd; ++iter) {
491 truncated += std::to_string(*iter) + ",";
492 }
493
494 if (!truncated.empty()) {
495 truncated.pop_back(); // Drop last comma
496 }
497 }
498
499 return truncated;
500}
501
502} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
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:423
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.
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:34
void getSpherical(double &R, double &theta, double &phi) const noexcept
Return the vector's position in spherical coordinates.
Definition V3D.cpp:116
This functor is used as the deleter object of a shared_ptr to effectively erase ownership Raw pointer...
Definition IComponent.h:173
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::shared_ptr< const Mantid::Geometry::IDetector > IDetector_const_sptr
Shared pointer to IDetector (const version)
Definition IDetector.h:102
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:53
@ Output
An output workspace.
Definition Property.h:54