Mantid
Loading...
Searching...
No Matches
SaveDiffCal.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 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 +
12#include "MantidNexus/H5Util.h"
13
14#include <H5Cpp.h>
15#include <filesystem>
16namespace Mantid::DataHandling {
17
29
30using namespace H5;
31using namespace Nexus;
32
33// Register the algorithm into the AlgorithmFactory
34DECLARE_ALGORITHM(SaveDiffCal)
35
36//----------------------------------------------------------------------------------------------
37
38
39const std::string SaveDiffCal::name() const { return "SaveDiffCal"; }
40
42int SaveDiffCal::version() const { return 1; }
43
45const std::string SaveDiffCal::category() const { return "DataHandling\\Instrument;Diffraction\\DataHandling"; }
46
48const std::string SaveDiffCal::summary() const { return "Saves a calibration file for powder diffraction"; }
49
50//----------------------------------------------------------------------------------------------
54 declareProperty(std::make_unique<WorkspaceProperty<ITableWorkspace>>("CalibrationWorkspace", "", Direction::Input,
56 "An output workspace.");
57
58 declareProperty(std::make_unique<WorkspaceProperty<GroupingWorkspace>>("GroupingWorkspace", "", Direction::Input,
60 "Optional: A GroupingWorkspace giving the grouping info.");
61
63 std::make_unique<WorkspaceProperty<MaskWorkspace>>("MaskWorkspace", "", Direction::Input, PropertyMode::Optional),
64 "Optional: A MaskWorkspace giving which detectors are masked.");
65
66 declareProperty(std::make_unique<FileProperty>("Filename", "", FileProperty::Save, ".h5"),
67 "Path to the .h5 file that will be created.");
68}
69
70std::map<std::string, std::string> SaveDiffCal::validateInputs() {
71 std::map<std::string, std::string> result;
72
73 ITableWorkspace_const_sptr calibrationWS = getProperty("CalibrationWorkspace");
74 if (bool(calibrationWS)) {
75 size_t numRows = calibrationWS->rowCount();
76 if (numRows == 0) {
77 result["CalibrationWorkspace"] = "Cannot save empty table";
78 } else {
79 GroupingWorkspace_const_sptr groupingWS = getProperty("GroupingWorkspace");
80 if (bool(groupingWS) && numRows < groupingWS->getNumberHistograms()) {
81 result["GroupingWorkspace"] = "Must have equal or less number of spectra as the table has rows";
82 }
83 MaskWorkspace_const_sptr maskWS = getProperty("MaskWorkspace");
84 if (bool(maskWS) && numRows < maskWS->getNumberHistograms()) {
85 result["MaskWorkspace"] = "Must have equal or less number of spectra as the table has rows";
86 }
87 }
88 } else {
89 GroupingWorkspace_const_sptr groupingWS = getProperty("GroupingWorkspace");
90 MaskWorkspace_const_sptr maskWS = getProperty("MaskWorkspace");
91
92 if ((!groupingWS) && (!maskWS)) {
93 const std::string msg("Failed to supply any input workspace");
94 result["CalibrationWorkspace"] = msg;
95 result["GroupingWorkspace"] = msg;
96 result["MaskWorkspace"] = msg;
97 }
98 }
99
100 return result;
101}
102
103void SaveDiffCal::writeDoubleFieldZeros(H5::Group &group, const std::string &name) {
104 std::vector<double> zeros(m_numValues, 0.);
106}
107
115void SaveDiffCal::writeDoubleFieldFromTable(H5::Group &group, const std::string &name) {
116 auto column = m_calibrationWS->getColumn(name);
117 // Retrieve only the first m_numValues, not necessarily the whole column
118 auto data = column->numeric_fill<>(m_numValues);
119
120 // if the field is optional, check if it is all zeros
121 if (name != "difc") {
122 bool allZeros = std::all_of(data.cbegin(), data.cend(), [](const auto &value) { return value == 0; });
123 if (allZeros)
124 return; // don't write the field
125 }
126
128}
129
137void SaveDiffCal::writeIntFieldFromTable(H5::Group &group, const std::string &name) {
138 auto column = m_calibrationWS->getColumn(name);
139 // Retrieve only the first m_numValues, not necessarily the whole column
140 auto data = column->numeric_fill<int32_t>(m_numValues);
142}
143
144/*
145 * Mantid::DataObjects::WorkspaceSingleValue/SpecialWorkspace2D is a parent to both GroupingWorkspace
146 * and MaskWorkspace
147 */
148void SaveDiffCal::writeDetIdsfromSVWS(H5::Group &group, const std::string &name,
150 if (!bool(ws))
151 throw std::runtime_error("Encountered null pointer in SaveDiffCal::writeDetIdsfromSVWS which should be impossible");
152
153 std::vector<int32_t> values;
154 for (size_t i = 0; i < m_numValues; ++i) {
155 const auto &detids = ws->getSpectrum(i).getDetectorIDs();
156 std::transform(detids.cbegin(), detids.cend(), std::back_inserter(values),
157 [](const auto &detid) { return static_cast<int32_t>(detid); });
158 }
159
161}
162
174void SaveDiffCal::writeIntFieldFromSVWS(H5::Group &group, const std::string &name,
176 const bool isMask = (name == "use");
177
178 // output array defaults to all one (one group, use the pixel)
179 std::vector<int32_t> values(m_numValues, 1);
180
181 if (bool(ws)) {
182 for (size_t i = 0; i < m_numValues; ++i) {
183 auto &ids = ws->getSpectrum(i).getDetectorIDs(); // set of detector ID's
184 // check if the first detector ID in the set is in the calibration table
185 auto found = m_detidToIndex.find(*(ids.begin())); // (detID, row_index)
186 if (found != m_detidToIndex.end()) {
187 auto value = static_cast<int32_t>(ws->getValue(found->first));
188 // in maskworkspace 0=use, 1=dontuse - backwards from the file
189 if (isMask) {
190 if (value == 0)
191 value = 1; // thus "use" means a calibrated detector, good for use
192 else
193 value = 0;
194 }
195 values[found->second] = value;
196 }
197 }
198 }
199
201}
202
204 m_detidToIndex.clear();
205
206 auto detidCol = m_calibrationWS->getColumn("detid");
207 auto detids = detidCol->numeric_fill<detid_t>();
208
209 const size_t numDets = detids.size();
210 for (size_t i = 0; i < numDets; ++i) {
211 m_detidToIndex[static_cast<detid_t>(detids[i])] = i;
212 }
213}
214
216 if (!bool(ws))
217 throw std::runtime_error(
218 "Encountered null pointer in SaveDiffCal::generateDetidToIndex which should be impossible");
219
220 m_detidToIndex.clear();
221
222 std::size_t index = 0;
223 for (size_t i = 0; i < m_numValues; ++i) {
224 const auto &detids = ws->getSpectrum(i).getDetectorIDs();
225 for (const auto &detid : detids) {
226 m_detidToIndex[static_cast<detid_t>(detid)] = index;
227 index++;
228 }
229 }
230}
231
232bool SaveDiffCal::tableHasColumn(const std::string &ColumnName) const {
233 if (m_calibrationWS) {
234 const std::vector<std::string> names = m_calibrationWS->getColumnNames();
235 return std::any_of(names.cbegin(), names.cend(), [&ColumnName](const auto &name) { return name == ColumnName; });
236 } else {
237 return false;
238 }
239}
240
241//----------------------------------------------------------------------------------------------
245 m_calibrationWS = getProperty("CalibrationWorkspace");
246 GroupingWorkspace_sptr groupingWS = getProperty("GroupingWorkspace");
247 MaskWorkspace_const_sptr maskWS = getProperty("MaskWorkspace");
248
249 // Get a starting number of values to work with that will be refined below
250 // THE ORDER OF THE IF/ELSE TREE MATTERS
251 m_numValues = std::numeric_limits<std::size_t>::max();
252 if (m_calibrationWS)
253 m_numValues = std::min(m_numValues, m_calibrationWS->rowCount());
254 if (groupingWS)
255 m_numValues = std::min(m_numValues, groupingWS->getNumberHistograms());
256 if (maskWS)
257 m_numValues = std::min(m_numValues, maskWS->getNumberHistograms());
258
259 // Initialize the mapping of detid to row number to make getting information
260 // from the table faster. ORDER MATTERS
261 if (m_calibrationWS) {
262 this->generateDetidToIndex();
263 } else if (groupingWS) {
264 this->generateDetidToIndex(groupingWS);
265 } else if (maskWS) {
266 this->generateDetidToIndex(maskWS);
267 }
268
269 if (groupingWS && groupingWS->isDetectorIDMappingEmpty())
270 groupingWS->buildDetectorIDMapping();
271
272 // delete the file if it already exists
273 std::string filename = getProperty("Filename");
274 if (std::filesystem::exists(filename)) {
275 std::filesystem::remove(filename);
276 }
277
278 H5File file(filename, H5F_ACC_EXCL, Nexus::H5Util::defaultFileAcc());
279
280 auto calibrationGroup = H5Util::createGroupNXS(file, "calibration", "NXentry");
281
282 // write the d-spacing to TOF conversion parameters for the selected pixels
283 // as datasets under the NXentry group
284 if (m_calibrationWS) {
285 this->writeDoubleFieldFromTable(calibrationGroup, "difc");
286 this->writeDoubleFieldFromTable(calibrationGroup, "difa");
287 this->writeDoubleFieldFromTable(calibrationGroup, "tzero");
288 } else {
289 writeDoubleFieldZeros(calibrationGroup, "difc");
290 // LoadDiffCal will set difa and tzero to zero if they are missing
291 }
292
293 // add the detid from which ever of these exists
294 if (m_calibrationWS) {
295 this->writeIntFieldFromTable(calibrationGroup, "detid");
296 } else if (groupingWS) {
297 this->writeDetIdsfromSVWS(calibrationGroup, "detid", groupingWS);
298 } else if (maskWS) {
299 this->writeDetIdsfromSVWS(calibrationGroup, "detid", maskWS);
300 }
301
302 // the dasid is a legacy column that is not used by mantid but should be written if it exists
303 if (this->tableHasColumn("dasid")) // optional field
304 this->writeIntFieldFromTable(calibrationGroup, "dasid");
305 else
306 g_log.information("Not writing out values for \"dasid\"");
307
308 this->writeIntFieldFromSVWS(calibrationGroup, "group", groupingWS);
309 this->writeIntFieldFromSVWS(calibrationGroup, "use", maskWS);
310
311 // check if the input calibration table has an "offset" field
312 if (this->tableHasColumn("offset")) // optional field
313 this->writeDoubleFieldFromTable(calibrationGroup, "offset");
314 else
315 g_log.information("Not writing out values for \"offset\"");
316
317 // get the instrument information only if a GroupingWorkspace or
318 // MaskWorkspace is supplied by the user
319 std::string instrumentName;
320 std::string instrumentSource;
321 if (bool(groupingWS)) {
322 instrumentName = groupingWS->getInstrument()->getName();
323 instrumentSource = groupingWS->getInstrument()->getFilename();
324 }
325 if (bool(maskWS)) {
326 if (instrumentName.empty()) {
327 instrumentName = maskWS->getInstrument()->getName();
328 }
329 if (instrumentSource.empty()) {
330 instrumentSource = maskWS->getInstrument()->getFilename();
331 }
332 }
333 if (!instrumentSource.empty()) {
334 instrumentSource = std::filesystem::path(instrumentSource).filename().string();
335 }
336
337 // add the instrument information
338 auto instrumentGroup = calibrationGroup.createGroup("instrument");
339 H5Util::writeStrAttribute(instrumentGroup, "NX_class", "NXinstrument");
340 if (!instrumentName.empty()) {
341 H5Util::write(instrumentGroup, "name", instrumentName);
342 }
343 if (!instrumentSource.empty()) {
344 H5Util::write(instrumentGroup, "instrument_source", instrumentSource);
345 }
346
347 file.close();
348}
349
350} // namespace Mantid::DataHandling
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
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.
Kernel::Logger & g_log
Definition Algorithm.h:422
A specialized class for dealing with file properties.
@ Save
to specify a file to write to, the file may or may not exist
ITableWorkspace is an implementation of Workspace in which the data are organised in columns of same ...
A property class for workspaces.
SaveDiffCal : TODO: DESCRIPTION.
Definition SaveDiffCal.h:23
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
void writeIntFieldFromTable(H5::Group &group, const std::string &name)
Create a dataset under a given group with a given name Use CalibrationWorkspace to retrieve the data.
API::ITableWorkspace_sptr m_calibrationWS
Definition SaveDiffCal.h:51
const std::string category() const override
Algorithm's category for identification.
void writeDoubleFieldFromTable(H5::Group &group, const std::string &name)
Create a dataset under a given group with a given name Use CalibrationWorkspace to retrieve the data.
int version() const override
Algorithm's version for identification.
void init() override
Initialize the algorithm's properties.
std::map< detid_t, size_t > m_detidToIndex
Definition SaveDiffCal.h:52
void writeDetIdsfromSVWS(H5::Group &group, const std::string &name, const DataObjects::SpecialWorkspace2D_const_sptr &ws)
void writeDoubleFieldZeros(H5::Group &group, const std::string &name)
const std::string name() const override
Algorithms name for identification.
bool tableHasColumn(const std::string &ColumnName) const
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
void exec() override
Execute the algorithm.
void writeIntFieldFromSVWS(H5::Group &group, const std::string &name, const DataObjects::SpecialWorkspace2D_const_sptr &ws)
Create a dataset under a given group with a given name Use GroupingWorkspace or MaskWorkspace to retr...
A GroupingWorkspace is a subclass of Workspace2D where each spectrum has a single number entry,...
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
std::shared_ptr< const ITableWorkspace > ITableWorkspace_const_sptr
shared pointer to Mantid::API::ITableWorkspace (const version)
std::shared_ptr< const GroupingWorkspace > GroupingWorkspace_const_sptr
shared pointer to a const GroupingWorkspace
std::shared_ptr< const SpecialWorkspace2D > SpecialWorkspace2D_const_sptr
shared pointer to a const SpecialWorkspace2D
std::shared_ptr< GroupingWorkspace > GroupingWorkspace_sptr
shared pointer to the GroupingWorkspace class
std::shared_ptr< const MaskWorkspace > MaskWorkspace_const_sptr
shared pointer to a const MaskWorkspace
void writeArray1D(H5::Group &group, const std::string &name, const std::vector< NumT > &values)
MANTID_NEXUS_DLL void writeStrAttribute(const H5::H5Object &object, const std::string &name, const std::string &value)
Definition H5Util.cpp:208
MANTID_NEXUS_DLL H5::FileAccPropList defaultFileAcc()
Default file access is H5F_CLOSE_STRONG.
Definition H5Util.cpp:119
MANTID_NEXUS_DLL H5::Group createGroupNXS(H5::H5File &file, const std::string &name, const std::string &nxtype)
MANTID_NEXUS_DLL void write(H5::Group &group, const std::string &name, const std::string &value)
int32_t detid_t
Typedef for a detector ID.
STL namespace.
Enumeration for a mandatory/optional property.
Describes the direction (within an algorithm) of a Property.
Definition Property.h:50
@ Input
An input workspace.
Definition Property.h:53