Mantid
Loading...
Searching...
No Matches
SaveNXcanSASHelper.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2025 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 +
8
9#include "MantidAPI/Axis.h"
12#include "MantidAPI/Run.h"
19#include "MantidKernel/MDUnit.h"
23#include "MantidNexus/H5Util.h"
24
25#include <functional>
26#include <regex>
27
28using namespace Mantid::Kernel;
30using namespace Mantid::Geometry;
31using namespace Mantid::API;
32using namespace Mantid::DataHandling::NXcanSAS;
33using namespace Mantid::Nexus;
34
35namespace {
36//=== UTILITY ===//
37
38// For h5 -> hsize_t = unsigned long long
39using AttrMap = std::map<std::string, std::string>;
40
41std::string getIntensityUnit(const MatrixWorkspace_sptr &workspace) {
42 auto iUnit = workspace->YUnit();
43 if (iUnit.empty()) {
44 iUnit = workspace->YUnitLabel();
45 }
46 return iUnit == "I(q) (cm-1)" ? sasIntensity : std::string(iUnit);
47}
48
49std::string getMDUnit(const Mantid::Geometry::IMDDimension_const_sptr &dimension,
50 const std::string &expectedUnit = "Angstrom^-1",
51 const std::string &sasFormatUnit = sasMomentumTransfer) {
52
53 const auto unitLabel = dimension->getMDUnits().getUnitLabel().ascii();
54 return (unitLabel.empty() || unitLabel == expectedUnit) ? sasFormatUnit : std::string(unitLabel);
55}
56
57template <typename NumT>
58void writeArray1DWithStrAttributes(H5::Group &group, const std::string &dataSetName, const std::vector<NumT> &values,
59 const AttrMap &attributes) {
60 H5Util::writeArray1D(group, dataSetName, values);
61 const auto dataSet = group.openDataSet(dataSetName);
62 for (const auto &[attributeName, attributeValue] : attributes) {
63 H5Util::writeStrAttribute(dataSet, attributeName, attributeValue);
64 }
65}
66
67void writeDataSetAttributes(const H5::H5Object &dataSet, const AttrMap &attributes) {
68 // Add attributes to data set
69 for (const auto &[nameAttr, valueAttr] : attributes) {
70 H5Util::writeStrAttribute(dataSet, nameAttr, valueAttr);
71 }
72}
73
74H5::DSetCreatPropList setCompression(const size_t rank, const hsize_t *chunkDims, const int deflateLevel = 6) {
75 H5::DSetCreatPropList propList;
76 propList.setChunk(static_cast<int>(rank), chunkDims);
77 propList.setDeflate(deflateLevel);
78 return propList;
79}
80
81//=== Functors to Extract Data From Workspaces ===//
82
86template <typename T> class QxExtractor {
87public:
88 T *operator()(const MatrixWorkspace_sptr &ws, size_t index) {
89 if (ws->isHistogramData()) {
90 qxPointData.clear();
91 VectorHelper::convertToBinCentre(ws->dataX(index), qxPointData);
92 return qxPointData.data();
93 }
94 return ws->dataX(index).data();
95 }
96
97 std::vector<T> qxPointData;
98};
99
103class SpectrumAxisValueProvider {
104public:
105 explicit SpectrumAxisValueProvider(const MatrixWorkspace_sptr &workspace) : m_workspace(workspace) {
106 setSpectrumAxisValues();
107 }
108
109 Mantid::MantidVec::value_type *operator()(const MatrixWorkspace_sptr & /*unused*/, size_t index) {
110 const auto isPointData = m_workspace->getNumberHistograms() == m_spectrumAxisValues.size();
111 const double value = isPointData ? m_spectrumAxisValues[index]
112 : (m_spectrumAxisValues[index + 1] + m_spectrumAxisValues[index]) / 2.0;
113
114 Mantid::MantidVec tempVec(m_workspace->dataY(index).size(), value);
115 m_currentAxisValues.swap(tempVec);
116 return m_currentAxisValues.data();
117 }
118
119private:
120 void setSpectrumAxisValues() {
121 const auto sAxis = m_workspace->getAxis(1);
122 for (size_t index = 0; index < sAxis->length(); ++index) {
123 m_spectrumAxisValues.emplace_back((*sAxis)(index));
124 }
125 }
126
127 MatrixWorkspace_sptr m_workspace;
128 Mantid::MantidVec m_spectrumAxisValues;
129 Mantid::MantidVec m_currentAxisValues;
130};
131
136template <typename T> class WorkspaceGroupDataExtractor {
137public:
138 explicit WorkspaceGroupDataExtractor(const WorkspaceGroup_sptr &workspace, bool extractError = false)
139 : m_workspace(workspace), m_extractError(extractError) {}
140
141 T *operator()(size_t groupIndex, size_t spectraIndex = 0) {
142 const auto ws = std::dynamic_pointer_cast<MatrixWorkspace>(m_workspace->getItem(groupIndex));
143 return m_extractError ? ws->dataE(spectraIndex).data() : ws->dataY(spectraIndex).data();
144 }
145
146 void setExtractErrors(bool extractError) { m_extractError = extractError; }
147
148private:
149 WorkspaceGroup_sptr m_workspace{};
150 bool m_extractError;
151};
152
153//=== SASFilename ===//
154
155bool isCanSASCompliant(bool isStrict, const std::string &input) {
156 const auto baseRegex = isStrict ? std::regex("[a-z_][a-z0-9_]*") : std::regex("[A-Za-z_][\\w_]*");
157 return std::regex_match(input, baseRegex);
158}
159
160std::string makeCompliantName(const std::string &input, bool isStrict,
161 const std::function<void(std::string &)> &capitalizeStrategy) {
162 auto output = input;
163 // Check if input is compliant
164 if (!isCanSASCompliant(isStrict, output)) {
165 output = std::regex_replace(output, std::regex("[-\\.]"), std::string("_"));
166 capitalizeStrategy(output);
167 // Check if the changes have made it compliant
168 if (!isCanSASCompliant(isStrict, output)) {
169 const auto message = "SaveNXcanSAS: The input " + input + "is not compliant with the NXcanSAS format.";
170 throw std::runtime_error(message);
171 }
172 }
173 return output;
174}
175
176//=== SASinstrument ===//
177
178std::string getInstrumentName(const MatrixWorkspace_sptr &workspace) {
179 return workspace->getInstrument()->getFullName();
180}
181
182std::string getIDF(const MatrixWorkspace_sptr &workspace) {
183 const auto date = workspace->getWorkspaceStartDate();
184 const auto instrumentName = getInstrumentName(workspace);
185 return InstrumentFileFinder::getInstrumentFilename(instrumentName, date);
186}
187
188//=== SASprocess ===//
189
190std::string getDate() {
191 time_t rawtime;
192 time(&rawtime);
193 char temp[25];
194 strftime(temp, 25, "%Y-%m-%dT%H:%M:%S", localtime(&rawtime));
195 std::string sasDate(temp);
196 return sasDate;
197}
198
206void addPropertyFromRunIfExists(const Run &run, const std::string &propertyName, H5::Group &sasGroup,
207 const std::string &sasTerm) {
208 if (run.hasProperty(propertyName)) {
209 const auto *property = run.getProperty(propertyName);
210 H5Util::write(sasGroup, sasTerm, property->value());
211 }
212}
213
214//=== SASpolarization ===//
215
216template <typename WorkspaceExtractorFunctor>
217void writePolarizedDataToFile(H5::DataSet &dataSet, WorkspaceExtractorFunctor func, const DataDimensions &dimensions,
218 const SpinVectorBuilder &spin) {
219 auto stateConverter = [](int spin) -> std::string { return (spin == 1) ? "+1" : std::to_string(spin); };
220
221 const auto rank = dimensions.getDataShape().size();
222 const H5::DataSpace memSpace(static_cast<int>(rank), dimensions.getSlabShape().data());
223 // create index of position in hypermatrix
224 auto pos = std::vector<hsize_t>(rank, 0);
225 const auto &fileSpace = dimensions.getDataSpace();
226 for (size_t i = 0; i < spin.pIn.size(); i++) {
227 for (size_t j = 0; j < spin.pOut.size(); j++) {
228 auto state = stateConverter(spin.pIn.at(i)) + stateConverter(spin.pOut.at(j));
229 auto index = indexOfWorkspaceForSpinState(spin.spinVec, state);
230 if (!index.has_value()) {
231 throw std::runtime_error("Couldn't find workspace for spin state " + state);
232 }
233
234 pos.at(0) = i;
235 pos.at(1) = j;
236
237 if (dimensions.getNumberOfHistograms() == 1) {
238 fileSpace.selectHyperslab(H5S_SELECT_SET, dimensions.getSlabShape().data(), pos.data());
239 dataSet.write(func(*index), dimensions.getDataType(), memSpace, fileSpace);
240 } else {
241 for (size_t n = 0; n < dimensions.getNumberOfHistograms(); n++) {
242 pos.at(2) = n;
243 fileSpace.selectHyperslab(H5S_SELECT_SET, dimensions.getSlabShape().data(), pos.data());
244 dataSet.write(func(*index, n), dimensions.getDataType(), memSpace, fileSpace);
245 }
246 }
247 }
248 }
249}
250
251template <typename WorkspaceExtractorFunctor>
252void savePolarizedDataSet(H5::Group &group, const WorkspaceGroup_sptr &workspaces, WorkspaceExtractorFunctor func,
253 const std::string &dataSetName, const SpinVectorBuilder &spin,
254 const AttrMap &attributes = {}) {
255 const auto ws0 = std::dynamic_pointer_cast<MatrixWorkspace>(workspaces->getItem(0));
256 auto dataDimensions = DataDimensions(ws0, std::make_pair(spin.pIn.size(), spin.pOut.size()));
257 const auto propList =
258 setCompression(static_cast<int>(dataDimensions.getDataShape().size()), dataDimensions.getSlabShape().data());
259 auto dataSet =
260 group.createDataSet(dataSetName, dataDimensions.getDataType(), dataDimensions.getDataSpace(), propList);
261 writePolarizedDataToFile(dataSet, std::move(func), dataDimensions, spin);
262 writeDataSetAttributes(dataSet, attributes);
263}
264
265void writeSpinDataAttributes(H5::Group &data, const SpinVectorBuilder &spinPairs) {
266 // store Pin/ Pout
269 AttrMap polAttributes;
270 polAttributes.emplace(sasUnitAttr, sasDataPolarizationUnitAttr);
271 writeArray1DWithStrAttributes(data, sasDataPin, spinPairs.pIn, polAttributes);
272 writeArray1DWithStrAttributes(data, sasDataPout, spinPairs.pOut, polAttributes);
273}
274
275//=== SASdata ===//
276
277void writeStandardDataAttributes(const H5::Group &data, const std::string &IaxesAttr = "Q",
278 const std::vector<int> &qIndices = {0}) {
284}
285
286AttrMap prepareUnitAttributes(const MatrixWorkspace_sptr &workspace, std::string iUnit = "") {
287 AttrMap iAttributes;
288 if (iUnit.empty()) {
289 iUnit = getIntensityUnit(workspace);
290 }
291 iAttributes.emplace(sasUnitAttr, iUnit);
292 iAttributes.emplace(sasUncertaintyAttr, sasDataIdev);
293 iAttributes.emplace(sasUncertaintiesAttr, sasDataIdev);
294 return iAttributes;
295}
296
297template <typename Functor>
298void write2DWorkspace(H5::Group &group, const MatrixWorkspace_sptr &workspace, const std::string &dataSetName,
299 Functor func, const AttrMap &attributes) {
300 // Set the dimension
301 const auto dimensions = DataDimensions(workspace);
302 // Get the proplist with compression settings
303 const auto propList =
304 setCompression(static_cast<int>(dimensions.getDataShape().size()), dimensions.getSlabShape().data());
305 const auto &fileSpace = dimensions.getDataSpace();
306 // Create the data set
307 auto dataSet = group.createDataSet(dataSetName, dimensions.getDataType(), fileSpace, propList);
308
309 // Create Data Space for 1D entry for each row in memory
310 hsize_t memSpaceDimension[1] = {dimensions.getNumberOfPoints()};
311 const H5::DataSpace memSpace(1, memSpaceDimension);
312 // Start position in the 2D data (indexed) data structure
313 auto pos = std::vector<hsize_t>{0, 0};
314 // Insert each row of the workspace as a slab
315 for (size_t index = 0; index < dimensions.getNumberOfHistograms(); ++index) {
316 // Need the data space
317 fileSpace.selectHyperslab(H5S_SELECT_SET, dimensions.getSlabShape().data(), pos.data());
318 // Write the correct data set to file
319 dataSet.write(func(workspace, index), dimensions.getDataType(), memSpace, fileSpace);
320 // Step up the write position
321 ++pos[0];
322 }
323 writeDataSetAttributes(dataSet, attributes);
324}
325
326void addQ1D(H5::Group &data, const MatrixWorkspace_sptr &workspace) {
327 AttrMap qAttributes;
328 // Prepare units
329 auto qUnit = getMDUnit(workspace->getDimension(0));
330 qAttributes.emplace(sasUnitAttr, qUnit);
331
332 // Add Qdev with units if available
333 if (workspace->hasDx(0)) {
336
337 qAttributes.emplace(sasUncertaintyAttr, sasDataQdev);
338 qAttributes.emplace(sasUncertaintiesAttr, sasDataQdev);
339
340 const auto qResolution = workspace->pointStandardDeviations(0);
341 AttrMap xUncertaintyAttributes;
342 xUncertaintyAttributes.emplace(sasUnitAttr, qUnit);
343 writeArray1DWithStrAttributes(data, sasDataQdev, qResolution.rawData(), xUncertaintyAttributes);
344 }
345
346 // We finally add the Q data with necessary attributes
347 const auto &qValue = workspace->points(0);
348 writeArray1DWithStrAttributes(data, sasDataQ, qValue.rawData(), qAttributes);
349}
350
351void addQ2D(H5::Group &data, const MatrixWorkspace_sptr &workspace) {
352 // Store the 2D Qx data + units
353 AttrMap qxAttributes;
354 auto qxUnit = getMDUnit(workspace->getDimension(0));
355 qxAttributes.emplace(sasUnitAttr, qxUnit);
356 const QxExtractor<double> qxExtractor;
357 write2DWorkspace(data, workspace, sasDataQx, qxExtractor, qxAttributes);
358
359 // Get 2D Qy data and store it
360 AttrMap qyAttributes;
361 auto qyUnit = getMDUnit(workspace->getDimension(1));
362 qyAttributes.emplace(sasUnitAttr, qyUnit);
363
364 const SpectrumAxisValueProvider spectrumAxisValueProvider(workspace);
365 write2DWorkspace(data, workspace, sasDataQy, spectrumAxisValueProvider, qyAttributes);
366}
367
368} // namespace
369
371
372std::string addDigit(size_t index) { return index < 10 ? "0" + std::to_string(index) : std::to_string(index); }
373
374std::filesystem::path prepareFilename(const std::string &baseFilename, bool addDigitSuffix, size_t index) {
375 auto path = std::filesystem::path(baseFilename);
376 if (!addDigitSuffix) {
377 // return early if it doesn't need to add digits
378 return path.replace_extension(NX_CANSAS_EXTENSION);
379 }
380 // remove extension if it has any
381 path.replace_extension("");
382 // add digit for groups
383 path += addDigit(index);
384 // add correct extension and return path
385 return path.replace_extension(NX_CANSAS_EXTENSION);
386}
387
394std::string makeCanSASRelaxedName(const std::string &input) {
395 bool isStrict = false;
396 auto emptyCapitalizationStrategy = [](std::string &) {};
397 return makeCompliantName(input, isStrict, emptyCapitalizationStrategy);
398}
399
407 const std::vector<std::string> &detectorNames) {
408 // If the group is empty then don't add anything
409 for (const auto &detectorName : detectorNames) {
410 if (!detectorName.empty()) {
411 std::string sasDetectorName = sasInstrumentDetectorGroupName + detectorName;
412 sasDetectorName = makeCanSASRelaxedName(sasDetectorName);
413
414 auto instrument = workspace->getInstrument();
415 auto component = instrument->getComponentByName(detectorName);
416
417 if (component) {
418 const auto sample = instrument->getSample();
419 const auto distance = component->getDistance(*sample);
420 AttrMap sddAttributes;
421 sddAttributes.insert(std::make_pair(sasUnitAttr, sasInstrumentDetectorSddUnitAttrValue));
422 auto detector = H5Util::createGroupCanSAS(group, sasDetectorName, nxInstrumentDetectorClassAttr,
424 H5Util::write(detector, sasInstrumentDetectorName, detectorName);
426 }
427 }
428 }
429}
430
442void addInstrument(H5::Group &group, const MatrixWorkspace_sptr &workspace, const std::string &radiationSource,
443 const std::string &geometry, double beamHeight, double beamWidth,
444 const std::vector<std::string> &detectorNames) {
445 // Setup instrument
446 const std::string sasInstrumentNameForGroup = sasInstrumentGroupName;
447 auto instrument =
449 auto instrumentName = getInstrumentName(workspace);
450 H5Util::write(instrument, sasInstrumentName, instrumentName);
451
452 // Setup the detector
453 addDetectors(instrument, workspace, detectorNames);
454
455 // Setup source
456 const std::string sasSourceName = sasInstrumentSourceGroupName;
457 auto source =
459 H5Util::write(source, sasInstrumentSourceRadiation, radiationSource);
460
461 // Setup Aperture
462 const std::string sasApertureName = sasInstrumentApertureGroupName;
463 auto aperture = H5Util::createGroupCanSAS(instrument, sasApertureName, nxInstrumentApertureClassAttr,
465
466 H5Util::write(aperture, sasInstrumentApertureShape, geometry);
467
468 AttrMap beamSizeAttrs;
469 beamSizeAttrs.insert(std::make_pair(sasUnitAttr, sasBeamAndSampleSizeUnitAttrValue));
470 if (beamHeight != 0) {
472 }
473 if (beamWidth != 0) {
475 }
476
477 // Add IDF information
478 std::string idf;
479 // try-catch added to use test instrument in testing
480 try {
481 idf = getIDF(workspace);
482 } catch (const std::runtime_error &) {
483 idf = "unknown";
484 }
485
486 H5Util::write(instrument, sasInstrumentIDF, idf);
487}
488
497void addPolarizer(H5::Group &group, const MatrixWorkspace_sptr &workspace, const std::string &componentName,
498 const std::string &componentType, const std::string &groupSuffix) {
499
500 const auto instrumentAttr = InstrumentPolarizer(componentType, componentName);
501 auto instrumentGroup = group.openGroup(sasInstrumentGroupName);
502
503 const auto instrument = workspace->getInstrument();
504 const auto component = instrument->getComponentByName(instrumentAttr.getComponentName());
505
506 if (component) {
507 auto polarizerGroup =
508 H5Util::createGroupCanSAS(instrumentGroup, instrumentAttr.sasPolarizerGroupAttr() + groupSuffix,
509 instrumentAttr.nxPolarizerClassAttr(), instrumentAttr.sasPolarizerClassAttr());
510
511 const auto type = component->getStringParameter(instrumentAttr.sasPolarizerIDFDeviceType());
512 H5Util::write(polarizerGroup, instrumentAttr.sasPolarizerName(), componentName);
513 H5Util::write(polarizerGroup, instrumentAttr.sasPolarizerDeviceType(), type.front());
514
515 // Calculate Z distance from component to sample
516 const auto samplePos = instrument->getSample()->getPos();
517 const auto compPos = component->getPos();
518 auto distance = samplePos.Z() - compPos.Z();
519
520 AttrMap distanceAttrs;
521 distanceAttrs.insert(std::make_pair(sasUnitAttr, instrumentAttr.sasPolarizerDistanceUnitAttr()));
522 H5Util::writeScalarDataSetWithStrAttributes(polarizerGroup, instrumentAttr.sasPolarizerDistance(), distance,
523 distanceAttrs);
524 }
525}
526
532void addEMFieldDirection(H5::Group &group, const std::string &emFieldDir) {
533 // Expect to receive a comma separated string with directions polar,azimuthal and rotation.
534 const auto directions = Mantid::Kernel::VectorHelper::splitStringIntoVector<double>(emFieldDir);
535 const auto angles = std::vector<std::string>{sasSampleEMFieldDirectionPolar, sasSampleEMFieldDirectionAzimuthal,
537
538 if (!directions.empty()) {
539 AttrMap magFieldAttrs;
540 magFieldAttrs.insert(std::make_pair(sasUnitAttr, sasSampleEMFieldDirectionUnitsAttr));
541 for (size_t i = 0; i < directions.size(); i++)
542 H5Util::writeScalarDataSetWithStrAttributes(group, angles.at(i), directions.at(i), magFieldAttrs);
543 }
544}
545
554void addSampleEMFields(H5::Group &group, const MatrixWorkspace_sptr &workspace, const std::string &emFieldStrengthLog,
555 const std::string &emFieldDir) {
556 if (emFieldStrengthLog.empty() && emFieldDir.empty()) {
557 return;
558 }
559
560 auto sampleGroup = group.exists(sasInstrumentSampleGroupAttr)
564
565 // Field Strength
566 if (workspace->run().hasProperty(emFieldStrengthLog)) {
567 const auto magFStrength = workspace->run().getLogAsSingleValue(emFieldStrengthLog);
568 const auto magFStrengthUnits = workspace->run().getProperty(emFieldStrengthLog)->units();
569
570 AttrMap magFieldAttrs;
571 if (!magFStrengthUnits.empty()) {
572 magFieldAttrs.insert(std::make_pair(sasUnitAttr, magFStrengthUnits));
573 }
574 H5Util::writeScalarDataSetWithStrAttributes(sampleGroup, sasSampleMagneticField, magFStrength, magFieldAttrs);
575 }
576
577 // Field Direction
578 addEMFieldDirection(sampleGroup, emFieldDir);
579}
580
587void addSample(H5::Group &group, const double &sampleThickness) {
588 if (sampleThickness == 0) {
589 return;
590 }
591 const std::string sasSampleNameForGroup = sasInstrumentSampleGroupAttr;
592
593 auto sample = H5Util::createGroupCanSAS(group, sasSampleNameForGroup, nxInstrumentSampleClassAttr,
595
596 AttrMap sampleThicknessAttrs;
597 sampleThicknessAttrs.insert(std::make_pair(sasUnitAttr, sasBeamAndSampleSizeUnitAttrValue));
599 sampleThicknessAttrs);
600}
601
609void addProcess(H5::Group &group, const MatrixWorkspace_sptr &workspace, const MatrixWorkspace_sptr &canWorkspace) {
610 // Setup process
611 const std::string sasProcessNameForGroup = sasProcessGroupName;
612 auto process = H5Util::createGroupCanSAS(group, sasProcessNameForGroup, nxProcessClassAttr, sasProcessClassAttr);
613
614 // Add name
616
617 // Add creation date of the file
618 auto date = getDate();
619 H5Util::write(process, sasProcessDate, date);
620
621 // Add Mantid version
622 const auto version = std::string(MantidVersion::version());
623 H5Util::write(process, sasProcessTermSvn, version);
624
625 const auto run = workspace->run();
626 addPropertyFromRunIfExists(run, sasProcessUserFileInLogs, process, sasProcessTermUserFile);
627 addPropertyFromRunIfExists(run, sasProcessBatchFileInLogs, process, sasProcessTermBatchFile);
628
629 if (canWorkspace) {
630 // Add can run number
631 const auto canRun = canWorkspace->getRunNumber();
633 }
634}
635
643 const std::string &transmissionName) {
644 // Setup process
645 const std::string sasTransmissionName = sasTransmissionSpectrumGroupName + "_" + transmissionName;
646 auto transmission = H5Util::createGroupCanSAS(group, sasTransmissionName, nxTransmissionSpectrumClassAttr,
648
649 // Add attributes for @signal, @T_axes, @T_indices, @T_uncertainty,
650 // @T_uncertainties, @name, @timestamp
655 H5Util::writeStrAttribute(transmission, sasTransmissionSpectrumNameAttr, transmissionName);
656
657 auto date = getDate();
659
660 //-----------------------------------------
661 // Add T with units + uncertainty definition
662 const auto transmissionData = workspace->y(0);
663 AttrMap transmissionAttributes;
664 std::string unit = sasNone;
665
666 transmissionAttributes.emplace(sasUnitAttr, unit);
667 transmissionAttributes.emplace(sasUncertaintyAttr, sasTransmissionSpectrumTdev);
668 transmissionAttributes.emplace(sasUncertaintiesAttr, sasTransmissionSpectrumTdev);
669
670 writeArray1DWithStrAttributes(transmission, sasTransmissionSpectrumT, transmissionData.rawData(),
671 transmissionAttributes);
672
673 //-----------------------------------------
674 // Add Tdev with units
675 const auto &transmissionErrors = workspace->e(0);
676 AttrMap transmissionErrorAttributes;
677 transmissionErrorAttributes.emplace(sasUnitAttr, unit);
678
679 writeArray1DWithStrAttributes(transmission, sasTransmissionSpectrumTdev, transmissionErrors.rawData(),
680 transmissionErrorAttributes);
681
682 //-----------------------------------------
683 // Add lambda with units
684 const auto lambda = workspace->points(0);
685 AttrMap lambdaAttributes;
686 auto lambdaUnit = getMDUnit(workspace->getDimension(0), "Angstrom", sasAngstrom);
687 lambdaAttributes.emplace(sasUnitAttr, lambdaUnit);
688 writeArray1DWithStrAttributes(transmission, sasTransmissionSpectrumLambda, lambda.rawData(), lambdaAttributes);
689}
690
696void addData1D(H5::Group &data, const MatrixWorkspace_sptr &workspace) {
697 // Add attributes for @signal, @I_axes, @Q_indices,
698 writeStandardDataAttributes(data);
699 addQ1D(data, workspace);
700 //-----------------------------------------
701 // Add I with units + uncertainty definition
702 const auto &intensity = workspace->y(0);
703 auto iAttributes = prepareUnitAttributes(workspace);
704 writeArray1DWithStrAttributes(data, sasDataI, intensity.rawData(), iAttributes);
705
706 //-----------------------------------------
707 // Add Idev with units
708 const auto &intensityUncertainty = workspace->e(0);
709 AttrMap eAttributes;
710 eAttributes.insert(std::make_pair(sasUnitAttr, iAttributes[sasUnitAttr])); // same units as intensity
711
712 writeArray1DWithStrAttributes(data, sasDataIdev, intensityUncertainty.rawData(), eAttributes);
713}
714
753void addData2D(H5::Group &data, const MatrixWorkspace_sptr &workspace) {
754
755 const std::string sasDataIAxesAttr2D = sasDataQ + sasSeparator + sasDataQ;
756 // Add attributes for @signal, @I_axes, @Q_indices,
757 writeStandardDataAttributes(data, sasDataIAxesAttr2D, std::vector<int>{0, 1});
758
759 addQ2D(data, workspace);
760 // Get 2D I data and store it
761 auto iAttributes = prepareUnitAttributes(workspace);
762
763 auto iExtractor = [](const MatrixWorkspace_sptr &ws, size_t index) { return ws->dataY(index).data(); };
764 write2DWorkspace(data, workspace, sasDataI, iExtractor, iAttributes);
765
766 // Get 2D Idev data and store it
767 AttrMap eAttributes;
768 eAttributes.insert(std::make_pair(sasUnitAttr, iAttributes[sasUnitAttr])); // same units as intensity
769
770 auto iDevExtractor = [](const MatrixWorkspace_sptr &ws, size_t index) { return ws->dataE(index).data(); };
771 write2DWorkspace(data, workspace, sasDataIdev, iDevExtractor, eAttributes);
772}
773
780void addPolarizedData(H5::Group &data, const WorkspaceGroup_sptr &wsGroup, const std::string &inputSpinStates) {
781
782 // Workspace form which to extract metadata
783 const auto ws0 = std::dynamic_pointer_cast<MatrixWorkspace>(wsGroup->getItem(0));
784 const auto dim = getWorkspaceDimensionality(ws0);
785
786 // Add attributes for @signal, @I_axes, @Q_indices,
787 std::string sasDataIAxesAttrSpin = sasDataPin + sasSeparator + sasDataPout + sasSeparator + sasDataQ;
788 std::vector<int> qIndices = {0, 1, 2};
790 sasDataIAxesAttrSpin += sasSeparator + sasDataQ;
791 qIndices.push_back(3);
792 }
793
794 writeStandardDataAttributes(data, sasDataIAxesAttrSpin, qIndices);
795 const auto inputSpinOrder = splitSpinStateString(inputSpinStates);
796 const auto &spinPairs = SpinVectorBuilder(inputSpinOrder);
797 writeSpinDataAttributes(data, spinPairs);
798
799 // add Q
800 switch (dim) {
802 addQ1D(data, ws0);
803 break;
805 addQ2D(data, ws0);
806 break;
807 default:
808 throw(std::invalid_argument("Incorrect dimension for workspace"));
809 }
810
811 // Add I with units + uncertainty definition
812 auto iAttributes = prepareUnitAttributes(ws0);
813
814 // add signal
815 WorkspaceGroupDataExtractor<double> wsGroupExtractor(wsGroup);
816 savePolarizedDataSet(data, wsGroup, wsGroupExtractor, sasDataI, spinPairs, iAttributes);
817
818 // add signal error
819 wsGroupExtractor.setExtractErrors(true);
820 AttrMap eAttributes;
821 eAttributes.insert(std::make_pair(sasUnitAttr, iAttributes[sasUnitAttr]));
822 savePolarizedDataSet(data, wsGroup, wsGroupExtractor, sasDataIdev, spinPairs, eAttributes);
823}
824
829H5::H5File prepareFile(const std::filesystem::path &path) {
830 // Prepare file
831 if (!path.empty()) {
832 std::filesystem::remove(path);
833 }
834 return H5::H5File(path.string(), H5F_ACC_EXCL, Nexus::H5Util::defaultFileAcc());
835}
836
837} // namespace Mantid::DataHandling::NXcanSAS
const std::vector< double > * lambda
double value
The value of the point.
Definition FitMW.cpp:51
IPeaksWorkspace_sptr workspace
std::map< DeltaEMode::Type, std::string > index
uint64_t hsize_t
static std::string getInstrumentFilename(const std::string &instrumentName, const std::string &date="")
Get the IDF using the instrument name and date.
bool hasProperty(const std::string &name) const
Does the property exist on the object.
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
This class stores information regarding an experimental run as a series of log entries.
Definition Run.h:35
const H5::DataSpace & getDataSpace() const
const std::vector< hsize_t > & getSlabShape() const
const std::vector< hsize_t > & getDataShape() const
static const char * version()
The full version number.
std::shared_ptr< WorkspaceGroup > WorkspaceGroup_sptr
shared pointer to Mantid::API::WorkspaceGroup
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
const std::string sasInstrumentApertureGapWidth
std::filesystem::path MANTID_DATAHANDLING_DLL prepareFilename(const std::string &baseFilename, bool addDigitSuffix=false, size_t index=0)
const std::string sasTransmissionSpectrumNameAttr
void MANTID_DATAHANDLING_DLL addInstrument(H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace, const std::string &radiationSource, const std::string &geometry, double beamHeight, double beamWidth, const std::vector< std::string > &detectorNames)
Add the instrument group to the NXcanSAS file.
void MANTID_DATAHANDLING_DLL addData2D(H5::Group &data, const Mantid::API::MatrixWorkspace_sptr &workspace)
Stores the 2D signal and Q data in the HDF5 file.
void MANTID_DATAHANDLING_DLL addSampleEMFields(H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace, const std::string &emFieldStrengthLog, const std::string &emFieldDir)
Adds the direction and strength of either magnetic or electric field on the sample.
const std::string sasInstrumentApertureGapHeight
const std::string sasInstrumentSampleClassAttr
const std::string sasInstrumentSourceRadiation
const std::string sasDataQUncertaintiesAttr
const std::string sasTransmissionSpectrumTdev
void MANTID_DATAHANDLING_DLL addData1D(H5::Group &data, const Mantid::API::MatrixWorkspace_sptr &workspace)
Adds signal and Q data to the data group from 1D reduced SANS data.
const std::string sasInstrumentApertureClassAttr
const std::string nxTransmissionSpectrumClassAttr
void MANTID_DATAHANDLING_DLL addEMFieldDirection(H5::Group &group, const std::string &emFieldDir)
Adds the Field direction of either the magnetic or the electric field on the sample.
const std::string sasSampleEMFieldDirectionUnitsAttr
void MANTID_DATAHANDLING_DLL addProcess(H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace)
const std::string sasTransmissionSpectrumTIndices
const std::string sasUncertaintyAttr
Standards state that "uncertainties" should be used, however different facilities interpret the stand...
const std::string sasTransmissionSpectrumTUncertainties
void MANTID_DATAHANDLING_DLL addDetectors(H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace, const std::vector< std::string > &detectorNames)
Adds detector info to the sas group.
const std::string sasSampleEMFieldDirectionAzimuthal
const std::string sasTransmissionSpectrumLambda
const std::string sasInstrumentDetectorClassAttr
const std::string sasInstrumentSampleThickness
std::string MANTID_DATAHANDLING_DLL makeCanSASRelaxedName(const std::string &input)
This makes out of an input a relaxed name, something conforming to "[A-Za-z_][\w_]*" For now "-" is c...
const std::string sasInstrumentSourceGroupName
std::string MANTID_DATAHANDLING_DLL addDigit(size_t index)
const std::string nxInstrumentSourceClassAttr
const std::string sasBeamAndSampleSizeUnitAttrValue
void MANTID_DATAHANDLING_DLL addPolarizer(H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace, const std::string &componentName, const std::string &componentType, const std::string &groupSuffix)
Add the polarizer component information to the instrument cansas group.
const std::string nxInstrumentApertureClassAttr
const std::string nxInstrumentSampleClassAttr
const std::string sasInstrumentDetectorSddUnitAttrValue
const std::string sasDataPolarizationUnitAttr
const std::string sasTransmissionSpectrumTUncertainty
const std::string sasInstrumentSourceClassAttr
const std::string nxInstrumentDetectorClassAttr
WorkspaceDimensionality getWorkspaceDimensionality(const Mantid::API::MatrixWorkspace_sptr &workspace)
Retrieves workspace dimensionality enum value: oneD , twoD, other (error)
void MANTID_DATAHANDLING_DLL addPolarizedData(H5::Group &data, const Mantid::API::WorkspaceGroup_sptr &wsGroup, const std::string &inputSpinStates)
Adds signal, Q and spin data to the data group from 1D or 2D reduced polarized SANS data.
const std::string sasInstrumentApertureGroupName
const std::string sasDataIUncertaintiesAttr
const std::string sasSampleEMFieldDirectionRotation
const std::string sasTransmissionSpectrumTimeStampAttr
void MANTID_DATAHANDLING_DLL addSample(H5::Group &group, const double &sampleThickness)
Adds sample thickness information to the sas sample group.
H5::H5File MANTID_DATAHANDLING_DLL prepareFile(const std::filesystem::path &path)
Creates and opens a H5 File in the given path.
const std::string sasTransmissionSpectrumGroupName
const std::string sasInstrumentDetectorGroupName
const std::string sasTransmissionSpectrumClassAttr
const std::string sasInstrumentSampleGroupAttr
void MANTID_DATAHANDLING_DLL addTransmission(H5::Group &group, const Mantid::API::MatrixWorkspace_const_sptr &workspace, const std::string &transmissionName)
Add a transmission group to the cansas file, including metadata extracted from the transmission works...
const std::string sasSampleEMFieldDirectionPolar
std::shared_ptr< const IMDDimension > IMDDimension_const_sptr
Shared Pointer to const IMDDimension.
MANTID_KERNEL_DLL std::vector< std::string > splitSpinStateString(const std::string &spinStates)
MANTID_KERNEL_DLL std::optional< size_t > indexOfWorkspaceForSpinState(const std::vector< std::string > &spinStateOrder, std::string targetSpinState)
void MANTID_KERNEL_DLL convertToBinCentre(const std::vector< double > &bin_edges, std::vector< double > &bin_centres)
Convert an array of bin boundaries to bin center values.
template DLLExport std::vector< double > splitStringIntoVector< double >(std::string listString, const std::string &separator)
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
void writeNumAttribute(const H5::H5Object &object, const std::string &name, const NumT &value)
Definition H5Util.cpp:216
MANTID_NEXUS_DLL H5::Group createGroupCanSAS(H5::Group &group, const std::string &name, const std::string &nxtype, const std::string &cstype)
MANTID_NEXUS_DLL void write(H5::Group &group, const std::string &name, const std::string &value)
void writeScalarDataSetWithStrAttributes(H5::Group &group, const std::string &name, const T &value, const std::map< std::string, std::string > &attributes)
Definition H5Util.cpp:244
Header for a base Nexus::Exception.
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
Definition cow_ptr.h:172
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)