Mantid
Loading...
Searching...
No Matches
CombineDiffCal.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2021 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 +
7
10#include "MantidAPI/TableRow.h"
14
15namespace Mantid::Algorithms {
18
19// Register the algorithm into the AlgorithmFactory
20DECLARE_ALGORITHM(CombineDiffCal)
21
22//----------------------------------------------------------------------------------------------
23
24namespace { // anonymous namespace
25namespace ColNames {
26const std::string DETID("detid");
27const std::string DIFC("difc");
28const std::string DIFA("difa");
29const std::string TZERO("tzero");
30} // namespace ColNames
31
32namespace PropertyNames {
33const std::string PIXEL_CALIB("PixelCalibration");
34const std::string GROUP_CALIB("GroupedCalibration");
35const std::string ARB_CALIB("CalibrationWorkspace");
36const std::string OUTPUT_WKSP("OutputWorkspace");
37const std::string MASK_WKSP("MaskWorkspace");
38} // namespace PropertyNames
39
40int getDetId(const std::shared_ptr<const API::Column> &detIdColumn, const std::size_t rowNum) {
41 return static_cast<int>((*detIdColumn)[rowNum]);
42}
43} // anonymous namespace
44
45//----------------------------------------------------------------------------------------------
46
48const std::string CombineDiffCal::name() const { return "CombineDiffCal"; }
49
51int CombineDiffCal::version() const { return 1; }
52
54const std::string CombineDiffCal::category() const { return "Diffraction\\Utility"; }
55
57const std::string CombineDiffCal::summary() const {
58 return "Combine a per-pixel calibration with a grouped spectrum calibration";
59}
60
61//----------------------------------------------------------------------------------------------
65 declareProperty(std::make_unique<WorkspaceProperty<DataObjects::TableWorkspace>>(PropertyNames::PIXEL_CALIB, "",
67 "DiffCal TableWorkspace that will be updated. This is often generated from cross-correlation. "
68 "These are the \"prev\" values in the documentation.");
70 std::make_unique<WorkspaceProperty<DataObjects::TableWorkspace>>(PropertyNames::GROUP_CALIB, "",
72 "DiffCal table generated from calibrating grouped spectra. This is the \"DIFCpd\" value in the documentation.");
74 std::make_unique<WorkspaceProperty<API::MatrixWorkspace>>(PropertyNames::ARB_CALIB, "", Direction::Input),
75 "Workspace where conversion from d-spacing to time-of-flight for each spectrum is determined from. "
76 "This is the \"DIFCarb\" value in the documentation.");
79 "DiffCal table generated from calibrating grouped spectra");
81 PropertyNames::MASK_WKSP, "", Direction::Input, API::PropertyMode::Optional),
82 "MaskedWorkspace for PixelCalibration");
83}
84
87 auto alg = createChildAlgorithm("SortTableWorkspace");
88 alg->setLoggingOffset(1);
89 alg->setProperty("InputWorkspace", table);
90 alg->setProperty("OutputWorkspace", table);
91 alg->setProperty("Columns", ColNames::DETID);
92 alg->executeAsChildAlg();
93
94 API::ITableWorkspace_sptr output = alg->getProperty("OutputWorkspace");
95 return std::dynamic_pointer_cast<DataObjects::TableWorkspace>(output);
96}
97
98bool findColumn(const std::vector<std::string> &columnNames, const std::string &name) {
99 return std::find(columnNames.begin(), columnNames.end(), name) != columnNames.end();
100}
101
103 const std::vector<std::string> columnNames = ws->getColumnNames();
104
105 std::stringstream error;
106 if (!findColumn(columnNames, ColNames::DETID))
107 error << ColNames::DETID << " ";
108 if (!findColumn(columnNames, ColNames::DIFC))
109 error << ColNames::DIFC << " ";
110 if (!findColumn(columnNames, ColNames::DIFA))
111 error << ColNames::DIFA << " ";
112 if (!findColumn(columnNames, ColNames::TZERO))
113 error << ColNames::TZERO << " ";
114
115 return error.str();
116}
117
118std::map<std::string, std::string> CombineDiffCal::validateInputs() {
119 std::map<std::string, std::string> results;
120
121 const DataObjects::TableWorkspace_sptr groupedCalibrationWS = getProperty(PropertyNames::GROUP_CALIB);
122 const DataObjects::TableWorkspace_sptr pixelCalibrationWS = getProperty(PropertyNames::PIXEL_CALIB);
123 Mantid::API::MatrixWorkspace_sptr calibrationWS = getProperty(PropertyNames::ARB_CALIB);
124
125 const auto groupedResult = generateErrorString(groupedCalibrationWS);
126 if (!groupedResult.empty()) {
127 results[PropertyNames::GROUP_CALIB] = "The GroupedCalibration Workspace is missing [ " + groupedResult + "]";
128 } else {
129 // the equations haven't been derived for difa != tzero != 0
130 std::stringstream msg("");
131 const auto difa = groupedCalibrationWS->getColumn(ColNames::DIFA);
132 const auto tzero = groupedCalibrationWS->getColumn(ColNames::TZERO);
133 const auto numRows = groupedCalibrationWS->rowCount();
134 for (std::size_t i = 0; i < numRows; ++i) {
135 if (difa->toDouble(i) != 0.) {
136 msg << "found nonzero difa in row " << i;
137 break;
138 } else if (tzero->toDouble(i) != 0.) {
139 msg << "found nonzero tzero in row " << i;
140 break;
141 }
142 }
143 if (!msg.str().empty()) {
144 results[PropertyNames::GROUP_CALIB] = msg.str();
145 }
146 }
147
148 const auto pixelResult = generateErrorString(pixelCalibrationWS);
149 if (!pixelResult.empty())
150 results[PropertyNames::PIXEL_CALIB] = "The PixelCalibration Workspace is missing [ " + pixelResult + "]";
151
152 // Ensure compatible detector IDs in tables
153 // Ensure all detids in GroupedCalibration(detid_pd) and PixelCalibration(detid_prev)
154 // can be found in CalibrationWorkspace (detid_arb)
155
156 // put all detector IDs into sets, for easier searching
157 auto prev_col = pixelCalibrationWS->getColVector<detid_t>(ColNames::DETID);
158 auto pd_col = groupedCalibrationWS->getColVector<detid_t>(ColNames::DETID);
159 auto arb_col = calibrationWS->detectorInfo().detectorIDs();
160 std::set<detid_t> detid_pd(std::make_move_iterator(pd_col.begin()), std::make_move_iterator(pd_col.end()));
161 std::set<detid_t> detid_prev(std::make_move_iterator(prev_col.begin()), std::make_move_iterator(prev_col.end()));
162 std::set<detid_t> detid_arb(std::make_move_iterator(arb_col.begin()), std::make_move_iterator(arb_col.end()));
163
164 std::set<std::string> unmatched;
165 // check that all detector IDs from PixelCalibration are present in CalibrationWorkspace
166 if (!std::includes(detid_arb.begin(), detid_arb.end(), detid_prev.begin(), detid_prev.end())) {
167 unmatched.insert(PropertyNames::PIXEL_CALIB);
168 unmatched.insert(PropertyNames::ARB_CALIB);
169 }
170 // check that all detector IDs from GroupedCalibration are present in CalibrationWorkspace
171 if (!std::includes(detid_arb.begin(), detid_arb.end(), detid_pd.begin(), detid_pd.end())) {
172 unmatched.insert(PropertyNames::GROUP_CALIB);
173 unmatched.insert(PropertyNames::ARB_CALIB);
174 }
175 // if any workspaces do not match, throw an error
176 if (!unmatched.empty()) {
177 std::stringstream msg("");
178 msg << "Inconsistent detector IDs between";
179 for (std::string const &x : unmatched) {
180 msg << " " << x;
181 }
182 msg << ".";
183 for (std::string const &x : unmatched) {
184 results[x] = msg.str();
185 }
186 }
187
188 return results;
189}
190
191std::shared_ptr<Mantid::API::TableRow> binarySearchForRow(const API::ITableWorkspace_sptr &ws, const int detid,
192 const size_t lastStart) {
193 size_t start = lastStart;
194 size_t end = ws->rowCount() - 1;
195
196 // looking at the detid column is faster
197 std::shared_ptr<const API::Column> detIdColumn = ws->getColumn(ColNames::DETID);
198
199 // since both tables are already sorted, linear search the first two rows
200 if (getDetId(detIdColumn, start) == detid) {
201 return std::make_shared<Mantid::API::TableRow>(ws->getRow(start));
202 } else {
203 start += 1; // advance to the second position
204 if ((end >= start) && (getDetId(detIdColumn, start) == detid)) {
205 return std::make_shared<Mantid::API::TableRow>(ws->getRow(start));
206 } else {
207 start += 1; // don't need to include this index in the bisect
208 }
209 }
210
211 // do bisecting search
212 while (end >= start) {
213 const size_t currentPosition = start + ((end - start) / 2);
214
215 const auto detIdInRow = getDetId(detIdColumn, currentPosition);
216 if (detIdInRow > detid) {
217 end = currentPosition - 1;
218 } else if (detIdInRow < detid) {
219 start = currentPosition + 1;
220 } else {
221 return std::make_shared<Mantid::API::TableRow>(ws->getRow(currentPosition));
222 }
223 }
224
225 // did not find the row
226 return nullptr;
227}
228
230 const DataObjects::TableWorkspace_sptr &previousTable, const std::size_t rowNum) {
231 // get the old values
232 const int detid = previousTable->cell_cast<int>(rowNum, 0);
233 const double difc = previousTable->cell_cast<double>(rowNum, 1);
234 const double difa = previousTable->cell_cast<double>(rowNum, 2);
235 const double tzero = previousTable->cell_cast<double>(rowNum, 3);
236
237 // copy to new table workspace
238 Mantid::API::TableRow newRow = ws->appendRow();
239 newRow << detid << difc << difa << tzero;
240}
241
242// Per Pixel:
243//
244// DIFC{eff} = (DIFC{pd}/DIFC{arb}) * DIFC{prev}
245//
246// DIFC{eff} = Output of this Alg, the combined DIFC
247// DIFC{pd} = The DIFC produced by PDCalibration, found in the "GroupedCalibration"
248// DIFC{arb} = found in the "CalibrationWorkspace" param
249// DIFC{prev} = The previous DIFCs, found in "PixelCalibration", as per description this was the set generated by CC
250
251//----------------------------------------------------------------------------------------------
255 // read in properties and sort copies of the tables
256 const API::MatrixWorkspace_sptr calibrationWS = getProperty(PropertyNames::ARB_CALIB);
257
258 DataObjects::TableWorkspace_sptr presortedGroupedCalibrationWS = getProperty(PropertyNames::GROUP_CALIB);
259 const auto groupedCalibrationWS = sortTableWorkspace(presortedGroupedCalibrationWS);
260
261 DataObjects::TableWorkspace_sptr presortedPixelCalibrationWS = getProperty(PropertyNames::PIXEL_CALIB);
262 const auto pixelCalibrationWS = sortTableWorkspace(presortedPixelCalibrationWS);
263
264 const DataObjects::MaskWorkspace_sptr maskWorkspace = getProperty(PropertyNames::MASK_WKSP);
265
266 // make the output workspace
267 DataObjects::TableWorkspace_sptr outputWorkspace = std::make_shared<DataObjects::TableWorkspace>();
268 outputWorkspace->addColumn("int", ColNames::DETID);
269 outputWorkspace->addColumn("double", ColNames::DIFC);
270 outputWorkspace->addColumn("double", ColNames::DIFA);
271 outputWorkspace->addColumn("double", ColNames::TZERO);
272
273 // keep track of which ids were already added to the output
274 std::set<int> detidsInGrpCalib;
275
276 API::Progress prog(this, 0.0, 1.0, groupedCalibrationWS->rowCount());
277
278 // cache values from calibrationWS since getting DIFC can be expensive
279 std::map<std::size_t, double> difcArbMap;
280
281 // searching for the row in the pixel calibration table can be expensive
282 // this variable is used to keep track of how far has already been processed
283 // in order to reduce the search space
284 std::size_t lastStart = 0;
285
286 // get a map from detector ID to workspace index in the calibration WS
287 // there is no guarantee detectorIDs in GroupedWorkspace will be contiguous,
288 // and therefore a map and not a vector is necessary.
289 auto wkspIndices = calibrationWS->getDetectorIDToWorkspaceIndexMap();
290
291 // access the spectrum info outside of the loop
292 // this prevents constantly re-calling the method,
293 // which can have some O(N) behavior
294 auto spectrumInfo = calibrationWS->spectrumInfo();
295
296 // loop through all rows in the grouped calibration table
297 // this will calculate an updated row or copy the row if it is missing from the pixel calibration
298 Mantid::API::TableRow groupedCalibrationRow = groupedCalibrationWS->getFirstRow();
299 do {
300 const int detid = groupedCalibrationWS->cell_cast<int>(groupedCalibrationRow.row(), 0);
301 detidsInGrpCalib.insert(detid);
302 bool prevDifValsExist = false;
303
304 if (!(maskWorkspace && maskWorkspace->isMasked(detid))) {
305 std::shared_ptr<Mantid::API::TableRow> pixelCalibrationRow =
306 binarySearchForRow(pixelCalibrationWS, detid, lastStart);
307 if (pixelCalibrationRow) {
308 // value from groupedCalibrationRow
309 const double difcPD = groupedCalibrationWS->cell_cast<double>(groupedCalibrationRow.row(), 1);
310
311 // get workspace index from the map
312 const auto wkspIndex = wkspIndices[detid];
313
314 double difcArb;
315 const auto difcArbIter = difcArbMap.find(wkspIndex);
316 if (difcArbIter == difcArbMap.end()) {
317 difcArb = spectrumInfo.diffractometerConstants(wkspIndex)[Kernel::UnitParams::difc];
318 difcArbMap[wkspIndex] = difcArb;
319 } else {
320 difcArb = difcArbIter->second;
321 }
322
323 // difc and difa values from pixelCalibrationWS
324 const double difcPrev = pixelCalibrationWS->cell_cast<double>(pixelCalibrationRow->row(), 1);
325 const double difaPrev = pixelCalibrationWS->cell_cast<double>(pixelCalibrationRow->row(), 2);
326
327 // calculate new difc and difa
328 const double difcNew = (difcPD / difcArb) * difcPrev;
329 const double difaNew = ((difcPD / difcArb) * (difcPD / difcArb)) * difaPrev;
330
331 // copy tzero from pixelCalibrationWS
332 const double tzeroNew = pixelCalibrationWS->cell_cast<double>(pixelCalibrationRow->row(), 3);
333
334 Mantid::API::TableRow newRow = outputWorkspace->appendRow();
335 newRow << detid << difcNew << difaNew << tzeroNew;
336 prevDifValsExist = true;
337
338 // update where to start next search from
339 lastStart = pixelCalibrationRow->row() + 1;
340 }
341 }
342
343 if (!prevDifValsExist) {
344 // copy from group calibration
345 addRowFromPreviousCalibration(outputWorkspace, groupedCalibrationWS, groupedCalibrationRow.row());
346 }
347
348 prog.report();
349 } while (groupedCalibrationRow.next());
350
351 // loop through rows in the pixel calibration table
352 // this will add rows that are not already represented
353 bool shouldSortOutputTable{false};
354 const std::size_t NUM_PIXEL_ROW = pixelCalibrationWS->rowCount();
355 for (std::size_t i = 0; i < NUM_PIXEL_ROW; ++i) {
356 const int detid = pixelCalibrationWS->cell_cast<int>(i, 0);
357 if (!(maskWorkspace && maskWorkspace->isMasked(detid))) {
358 if (detidsInGrpCalib.count(detid) == 0) {
359 // copy from pixel calibration
360 addRowFromPreviousCalibration(outputWorkspace, pixelCalibrationWS, i);
361 shouldSortOutputTable = true;
362 }
363 }
364 }
365
366 // if rows were copied from the pixel calibration, the output should be sorted
367 if (shouldSortOutputTable)
368 outputWorkspace = std::dynamic_pointer_cast<DataObjects::TableWorkspace>(sortTableWorkspace(outputWorkspace));
369
370 setProperty(PropertyNames::OUTPUT_WKSP, outputWorkspace);
371}
372
373} // namespace Mantid::Algorithms
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double error
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Helper class for reporting progress from algorithms.
Definition Progress.h:25
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
bool next()
Steps to the next row in the TableWorkspace if there is one.
Definition TableRow.cpp:40
size_t row() const
Returns the row number of the TableRow.
Definition TableRow.h:43
A property class for workspaces.
DataObjects::TableWorkspace_sptr sortTableWorkspace(DataObjects::TableWorkspace_sptr const &table)
sort the calibration table according increasing values in column "detid"
void exec() override
Execute the algorithm.
const std::string category() const override
Algorithm's category for identification.
int version() const override
Algorithm's version for identification.
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
const std::string name() const override
Algorithms name for identification.
void init() override
Initialize the algorithm's properties.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
bool findColumn(const std::vector< std::string > &columnNames, const std::string &name)
void addRowFromPreviousCalibration(DataObjects::TableWorkspace_sptr &ws, const DataObjects::TableWorkspace_sptr &previousTable, const std::size_t rowNum)
std::string generateErrorString(const DataObjects::TableWorkspace_sptr &ws)
std::shared_ptr< Mantid::API::TableRow > binarySearchForRow(const API::ITableWorkspace_sptr &ws, const int detid, const size_t lastStart)
std::shared_ptr< TableWorkspace > TableWorkspace_sptr
shared pointer to Mantid::DataObjects::TableWorkspace
std::shared_ptr< MaskWorkspace > MaskWorkspace_sptr
shared pointer to the MaskWorkspace class
const std::string OUTPUT_WKSP("OutputWorkspace")
int32_t detid_t
Typedef for a detector ID.
Describes the direction (within an algorithm) of a Property.
Definition Property.h:50
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54