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 +
10#include "MantidAPI/TableRow.h"
18#include "MantidKernel/Unit.h"
20#include "MantidKernel/V3D.h"
21#include <unordered_map>
22#include <vector>
23
24using namespace Mantid::API;
25using namespace Mantid::Kernel;
26using namespace Mantid::DataObjects;
27using namespace Mantid::Geometry;
28
29namespace Mantid::Algorithms {
30// Register the algorithm into the AlgorithmFactory
31DECLARE_ALGORITHM(CreateDetectorTable)
32
33void CreateDetectorTable::init() {
34 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("InputWorkspace", "", Direction::Input),
35 "The name of the workspace to take as input.");
36
37 declareProperty(std::make_unique<ArrayProperty<int>>("WorkspaceIndices", Direction::Input),
38 "If left empty then all workspace indices are used.");
39 setPropertySettings("WorkspaceIndices",
40 std::make_unique<EnabledWhenWorkspaceIsType<MatrixWorkspace>>("InputWorkspace", true));
41
42 declareProperty("IncludeData", false, "Include the first value from each spectrum.");
43 setPropertySettings("IncludeData",
44 std::make_unique<EnabledWhenWorkspaceIsType<MatrixWorkspace>>("InputWorkspace", true));
45
46 declareProperty<bool>("IncludeDetectorPosition", false,
47 "Include the absolute position of the detector group for each spectrum.", Direction::Input);
48 setPropertySettings("IncludeDetectorPosition",
49 std::make_unique<EnabledWhenWorkspaceIsType<MatrixWorkspace>>("InputWorkspace", true));
50
51 declareProperty<bool>("OneRowPerDetectorID", false,
52 "Order rows in table by detector IDs, with each detector ID having its own row. When "
53 "OneRowPerDetectorID=true the table iterates over all detector IDs, so WorkspaceIndices is "
54 "ignored and the row count equals the detector count.",
56 setPropertySettings("OneRowPerDetectorID",
57 std::make_unique<EnabledWhenWorkspaceIsType<MatrixWorkspace>>("InputWorkspace", true));
58
59 declareProperty(std::make_unique<WorkspaceProperty<TableWorkspace>>("DetectorTableWorkspace", "", Direction::Output,
61 "The name of the outputted detector table workspace, if left empty then "
62 "the input workspace name + \"-Detectors\" is used.");
63}
64
66 API::Workspace_sptr inputWs = getProperty("InputWorkspace");
67 includeData = getProperty("IncludeData");
68 workspaceIndices = getProperty("WorkspaceIndices");
69 includeDetectorPosition = getProperty("IncludeDetectorPosition");
70 oneRowPerDetectorID = getProperty("OneRowPerDetectorID");
71
72 if (auto peaks = std::dynamic_pointer_cast<IPeaksWorkspace>(inputWs)) {
73 table = peaks->createDetectorTable();
75 return;
76 }
77
78 if ((ws = std::dynamic_pointer_cast<MatrixWorkspace>(inputWs))) {
79 if (ws->getInstrument()->getSample() == nullptr) {
80 throw std::runtime_error("Matrix workspace has no instrument information");
81 }
82 setup();
86 } else {
88 }
90 return;
91 }
92
93 throw std::runtime_error("Detector table can only be created for matrix and peaks workspaces.");
94}
95
97 if (table == nullptr) {
98 throw std::runtime_error("Unknown error while creating detector table workspace");
99 }
100 API::Workspace_sptr inputWs = getProperty("InputWorkspace");
101 if (getPropertyValue("DetectorTableWorkspace") == "") {
102 setPropertyValue("DetectorTableWorkspace", inputWs->getName() + "-Detectors");
103 }
104 setProperty("DetectorTableWorkspace", table);
105}
106
107/*
108 * Validate the input parameters
109 * @returns map with keys corresponding to properties with errors and values
110 * containing the error messages.
111 */
112std::map<std::string, std::string> CreateDetectorTable::validateInputs() {
113 // create the map
114 std::map<std::string, std::string> validationOutput;
115
116 Workspace_sptr inputWS = getProperty("InputWorkspace");
117 const auto matrix = std::dynamic_pointer_cast<MatrixWorkspace>(inputWS);
118
119 if (matrix) {
120 const int numSpectra = static_cast<int>(matrix->getNumberHistograms());
121 const std::vector<int> indices = getProperty("WorkspaceIndices");
122
123 if (std::any_of(indices.cbegin(), indices.cend(),
124 [numSpectra](const auto index) { return (index >= numSpectra) || (index < 0); })) {
125 validationOutput["WorkspaceIndices"] = "One or more indices out of range of available spectra.";
126 }
127 }
128
129 return validationOutput;
130}
131
133
134 isScanning = ws->detectorInfo().isScanning();
135
136 spectrumInfo = &ws->spectrumInfo();
137 detectorInfo = &ws->detectorInfo();
138
139 // check if efixed value is available
140 calcQ = true;
141 if (spectrumInfo->hasDetectors(0)) {
142 try {
143 std::shared_ptr<const IDetector> detector(&spectrumInfo->detector(0), Mantid::NoDeleting());
144 ws->getEFixed(detector);
145 } catch (std::invalid_argument &) {
146 calcQ = false;
147 } catch (std::runtime_error &) {
148 calcQ = false;
149 }
150 } else {
151 // No detectors available
152 calcQ = false;
153 }
154
155 hasDiffConstants = (ws->getEMode() == DeltaEMode::Elastic);
156
157 beamAxisIndex = ws->getInstrument()->getReferenceFrame()->pointingAlongBeam();
158 sampleDist = ws->getInstrument()->getSample()->getPos()[beamAxisIndex];
160 showSignedTwoTheta = false; // If true, signedVersion of the two theta
161
162 if (workspaceIndices.empty()) {
163 workspaceIndices = std::vector<int>(ws->getNumberHistograms());
164 std::iota(workspaceIndices.begin(), workspaceIndices.end(), 0);
165 }
166
167 table = WorkspaceFactory::Instance().createTable("TableWorkspace");
168}
169
171 std::vector<std::pair<std::string, std::string>> colNames;
172 colNames.emplace_back("int", "Index");
173 colNames.emplace_back("int", "Spectrum No");
175 colNames.emplace_back("int", "Detector ID(s)");
176 } else {
177 colNames.emplace_back("str", "Detector ID(s)");
178 }
179 if (isScanning)
180 colNames.emplace_back("str", "Time Indexes");
181 if (includeData) {
182 colNames.emplace_back("double", "Data Value");
183 colNames.emplace_back("double", "Data Error");
184 }
185
186 colNames.emplace_back("double", "R");
187 colNames.emplace_back("double", "Theta");
188 if (calcQ) {
189 colNames.emplace_back("double", "Q elastic");
190 }
191 colNames.emplace_back("double", "Phi");
192 colNames.emplace_back("str", "Monitor");
193 if (hasDiffConstants) {
194 colNames.emplace_back("double", "DIFA");
195 colNames.emplace_back("double", "DIFC");
196 colNames.emplace_back("double", "DIFC - Uncalibrated");
197 colNames.emplace_back("double", "TZERO");
198 }
200 colNames.emplace_back("V3D", "Position");
201 }
202
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 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
263 double efixed = ws->getEFixed(det);
264 double usignTheta = spectrumInfo->twoTheta(wsIndex) * 0.5;
265 double q = UnitConversion::convertToElasticQ(usignTheta, efixed);
266 return q;
267 } catch (std::runtime_error &) {
268 // No Efixed
269 return std::nan("");
270 }
271}
272
273void CreateDetectorTable::getDiffConst(size_t wsIndex, double &difa, double &difc, double &difcUnc, double &tzero) {
274 if (!hasDiffConstants) {
275 difa = difc = difcUnc = tzero = 0;
276 return;
277 }
278 if (spectrumInfo->isMonitor(wsIndex)) {
279 difa = difc = difcUnc = tzero = 0;
280 return;
281 }
282 auto diffConsts = spectrumInfo->diffractometerConstants(wsIndex);
283 // map will create an entry with zero value if not present already
284 difa = diffConsts[UnitParams::difa];
285 difc = diffConsts[UnitParams::difc];
286 difcUnc = spectrumInfo->difcUncalibrated(wsIndex);
287 tzero = diffConsts[UnitParams::tzero];
288}
289
291 TableRow colValues = table->getRow(static_cast<size_t>(row));
292 colValues << static_cast<int>(data.wsIndex);
293 colValues << data.specNo;
295 // Populate detector column with first det id in set
296 colValues << static_cast<int>(*data.detIds.begin());
297 } else {
298 // Populate detector column with a truncated string of all det ids
299 colValues << createTruncatedList(data.detIds);
300 }
301 if (isScanning) {
302 colValues << data.timeIndexes;
303 }
304 if (includeData) {
305 colValues << data.dataY0 << data.dataE0; // data
306 }
307 colValues << data.R << data.theta;
308 if (calcQ) {
309 colValues << data.q;
310 }
311 colValues << data.phi; // rtp
312 colValues << data.isMonitor; // monitor
313 if (hasDiffConstants) {
314 colValues << data.difa << data.difc << data.difcUnc << data.tzero;
315 }
317 colValues << data.detPosition;
318 }
319}
320
322 DetectorRowData data;
323
324 // Geometry
325 if (!spectrumInfo->hasDetectors(wsIndex))
326 throw std::runtime_error("No detectors found.");
328 const std::vector<std::string> &parameters =
329 spectrumInfo->detector(wsIndex).getStringParameter("show-signed-theta", true); // recursive
331 (!parameters.empty() && find(parameters.begin(), parameters.end(), "Always") != parameters.end());
333 }
334
335 // wsIndex
336 data.wsIndex = static_cast<int>(wsIndex);
337 // Spec No
338 auto &spectrum = ws->getSpectrum(wsIndex);
339 data.specNo = spectrum.getSpectrumNo();
340 data.detIds = dynamic_cast<const std::set<int> &>(spectrum.getDetectorIDs());
341 // Time indexes
342 data.timeIndexes = getTimeIndexes(wsIndex);
343 // data Y/E
344 data.dataY0 = ws->y(wsIndex)[0];
345 data.dataE0 = ws->e(wsIndex)[0];
346 // R, Theta, Phi
347 getSphericalCoordinates(wsIndex, data.R, data.theta, data.phi);
348 // Q
349 data.q = getQ(wsIndex);
350 // Is monitor
351 data.isMonitor = spectrumInfo->isMonitor(wsIndex) ? "yes" : "no";
352 // Diff consts
353 getDiffConst(wsIndex, data.difa, data.difc, data.difcUnc, data.tzero);
354 // Detector position
355 data.detPosition = spectrumInfo->position(wsIndex);
356 return data;
357}
358
360
361 table->setRowCount(workspaceIndices.size());
362
364 for (int row = 0; row < static_cast<int>(workspaceIndices.size()); row++) {
366
367 auto wsIndex = static_cast<size_t>(workspaceIndices[static_cast<size_t>(row)]);
368
369 try {
370 auto data = calculateWsIdxData(wsIndex);
371 writeRowToTable(row, data);
372
373 } catch (const std::exception &) {
374 DetectorRowData errorData;
375 errorData.wsIndex = static_cast<int>(wsIndex);
376 errorData.specNo = -1;
377 errorData.detIds = {0};
378 errorData.timeIndexes = "0";
379 errorData.dataY0 = ws->y(wsIndex)[0];
380 errorData.dataE0 = ws->e(wsIndex)[0];
381 errorData.isMonitor = "n/a";
382 writeRowToTable(row, errorData);
383 } // End catch for no spectrum
385 }
387}
388
390
391 // Each thread will pick up a workspace index and populate all det ids from that index
392 // Need to find partitions for each workspace index beforehand, since executing in parallel
393 std::vector<size_t> wsIndexToChunkStart(workspaceIndices.size());
394 size_t rowsCounter = 0;
395 for (size_t i = 0; i < workspaceIndices.size(); i++) {
396 wsIndexToChunkStart[i] = rowsCounter;
397 rowsCounter += ws->getSpectrum(static_cast<size_t>(workspaceIndices[i])).getDetectorIDs().size();
398 }
399 std::vector<DetectorRowData> allRowsData(rowsCounter);
400
401 // Executing in parallel, each thread writes to unique chunk in array, avoids contention
402 // NOTE: Attempted a shared dict implementation, too much thread contention for many det ids
403 // Need to stick to an array implementation where each thread writes to unique place in array
404 // NOTE: Iterating variable needs to be signed for omp, can't use size_t
406 for (int i = 0; i < static_cast<int>(workspaceIndices.size()); i++) {
408
409 auto wsIndex = static_cast<size_t>(workspaceIndices[static_cast<size_t>(i)]);
410
411 DetectorRowData data;
412 // TODO: Not entirely sure this catch is necessary
413 // Only necessary if there is a det id in detectorInfo->detectorIDs()
414 // that somehow triggers the crash, but that seems unlikely
415 try {
416 data = calculateWsIdxData(wsIndex);
417 } catch (const std::exception &) {
418 data.wsIndex = static_cast<int>(wsIndex);
419 data.specNo = -1;
420 data.detIds = {0};
421 data.timeIndexes = "0";
422 data.dataY0 = ws->y(wsIndex)[0];
423 data.dataE0 = ws->e(wsIndex)[0];
424 data.isMonitor = "n/a";
425 } // End catch for no spectrum
426
427 size_t chunkStart = wsIndexToChunkStart[static_cast<size_t>(i)];
428 auto detIds = dynamic_cast<const std::set<int> &>(ws->getSpectrum(wsIndex).getDetectorIDs());
429 for (int detId : detIds) {
430 data.detIds = std::set<int>({detId});
431 allRowsData[chunkStart++] = data;
432 }
434 }
436
437 // Build detector id map for easy ordering
438 std::unordered_map<int, DetectorRowData> detIdToData;
439 detIdToData.reserve(allRowsData.size());
440 for (auto &rowData : allRowsData) {
441 if (!rowData.detIds.empty()) {
442 detIdToData[*rowData.detIds.begin()] = std::move(rowData);
443 }
444 }
445 // Write rows in order of component index
446 // Number of rows matches number of detectorIDs exactly
447 const auto &workspaceDetectorIds = detectorInfo->detectorIDs();
448 table->setRowCount(workspaceDetectorIds.size());
449 int row = 0;
450 for (int detId : workspaceDetectorIds) {
451 DetectorRowData data;
452 auto it = detIdToData.find(detId);
453 if (it != detIdToData.end()) {
454 data = it->second;
455 } else {
456 // Rows need to match detectorIDs, if not found, pad with invalid flags
457 data.wsIndex = -1;
458 data.specNo = -1;
459 data.detIds = {detId};
460 data.timeIndexes = "0";
461 data.dataY0 = 0;
462 data.dataE0 = 0;
463 data.isMonitor = "n/a";
464 }
465 writeRowToTable(row++, data);
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:538
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:422
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.
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
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.
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.
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