Mantid
Loading...
Searching...
No Matches
SaveNXcanSAS.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 +
8#include "MantidAPI/Axis.h"
13#include "MantidAPI/Progress.h"
14#include "MantidAPI/Run.h"
23#include "MantidKernel/MDUnit.h"
26
27#include <H5Cpp.h>
28#include <boost/algorithm/string/trim.hpp>
29#include <boost/regex.hpp>
30#include <memory>
31
32#include <Poco/File.h>
33#include <Poco/Path.h>
34#include <algorithm>
35#include <cctype>
36#include <functional>
37#include <iterator>
38#include <utility>
39
40using namespace Mantid::Kernel;
41using namespace Mantid::Geometry;
42using namespace Mantid::API;
43using namespace Mantid::DataHandling::NXcanSAS;
44
45namespace {
46
47enum class StoreType { Qx, Qy, I, Idev, Other };
48
49bool isCanSASCompliant(bool isStrict, const std::string &input) {
50 auto baseRegex = isStrict ? boost::regex("[a-z_][a-z0-9_]*") : boost::regex("[A-Za-z_][\\w_]*");
51 return boost::regex_match(input, baseRegex);
52}
53
54void removeSpecialCharacters(std::string &input) {
55 boost::regex toReplace("[-\\.]");
56 std::string replaceWith("_");
57 input = boost::regex_replace(input, toReplace, replaceWith);
58}
59
60std::string makeCompliantName(const std::string &input, bool isStrict,
61 const std::function<void(std::string &)> &captializeStrategy) {
62 auto output = input;
63 // Check if input is compliant
64 if (!isCanSASCompliant(isStrict, output)) {
65 removeSpecialCharacters(output);
66 captializeStrategy(output);
67 // Check if the changes have made it compliant
68 if (!isCanSASCompliant(isStrict, output)) {
69 std::string message = "SaveNXcanSAS: The input " + input + "is not compliant with the NXcanSAS format.";
70 throw std::runtime_error(message);
71 }
72 }
73 return output;
74}
75
76template <typename NumT>
77void writeArray1DWithStrAttributes(H5::Group &group, const std::string &dataSetName, const std::vector<NumT> &values,
78 const std::map<std::string, std::string> &attributes) {
79 Mantid::DataHandling::H5Util::writeArray1D(group, dataSetName, values);
80 auto dataSet = group.openDataSet(dataSetName);
81 for (const auto &attribute : attributes) {
82 Mantid::DataHandling::H5Util::writeStrAttribute(dataSet, attribute.first, attribute.second);
83 }
84}
85
86H5::DSetCreatPropList setCompression2D(const hsize_t *chunkDims, const int deflateLevel = 6) {
87 H5::DSetCreatPropList propList;
88 const int rank = 2;
89 propList.setChunk(rank, chunkDims);
90 propList.setDeflate(deflateLevel);
91 return propList;
92}
93
94template <typename Functor>
95void write2DWorkspace(H5::Group &group, Mantid::API::MatrixWorkspace_sptr workspace, const std::string &dataSetName,
96 Functor func, const std::map<std::string, std::string> &attributes) {
97 using namespace Mantid::DataHandling::H5Util;
98
99 // Set the dimension
100 const size_t dimension0 = workspace->getNumberHistograms();
101 const size_t dimension1 = workspace->y(0).size();
102 const hsize_t rank = 2;
103 hsize_t dimensionArray[rank] = {static_cast<hsize_t>(dimension0), static_cast<hsize_t>(dimension1)};
104
105 // Start position in the 2D data (indexed) data structure
106 hsize_t start[rank] = {0, 0};
107
108 // Size of a slab
109 hsize_t sizeOfSingleSlab[rank] = {1, dimensionArray[1]};
110
111 // Get the Data Space definition for the 2D Data Set in the file
112 auto fileSpace = H5::DataSpace(rank, dimensionArray);
113 H5::DataType dataType(getType<double>());
114
115 // Get the proplist with compression settings
116 H5::DSetCreatPropList propList = setCompression2D(sizeOfSingleSlab);
117
118 // Create the data set
119 auto dataSet = group.createDataSet(dataSetName, dataType, fileSpace, propList);
120
121 // Create Data Spae for 1D entry for each row in memory
122 hsize_t memSpaceDimension[1] = {dimension1};
123 H5::DataSpace memSpace(1, memSpaceDimension);
124
125 // Insert each row of the workspace as a slab
126 for (unsigned int index = 0; index < dimension0; ++index) {
127 // Need the data space
128 fileSpace.selectHyperslab(H5S_SELECT_SET, sizeOfSingleSlab, start);
129
130 // Write the correct data set to file
131 dataSet.write(func(workspace, index), dataType, memSpace, fileSpace);
132 // Step up the write position
133 ++start[0];
134 }
135
136 // Add attributes to data set
137 for (const auto &attribute : attributes) {
138 writeStrAttribute(dataSet, attribute.first, attribute.second);
139 }
140}
141
142std::vector<std::string> splitDetectorNames(std::string detectorNames) {
143 const std::string delimiter = ",";
144 std::vector<std::string> detectors;
145 size_t pos(0);
146 std::string detectorName;
147 while ((pos = detectorNames.find(delimiter)) != std::string::npos) {
148 detectorName = detectorNames.substr(0, pos);
149 boost::algorithm::trim(detectorName);
150 detectors.emplace_back(detectorName);
151 detectorNames.erase(0, pos + delimiter.length());
152 }
153 // Push remaining element
154 boost::algorithm::trim(detectorNames);
155 detectors.emplace_back(detectorNames);
156 return detectors;
157}
158
159//------- SASentry
166H5::Group addSasEntry(H5::H5File &file, const Mantid::API::MatrixWorkspace_sptr &workspace, const std::string &suffix) {
167 using namespace Mantid::DataHandling::NXcanSAS;
168 const std::string sasEntryName = sasEntryGroupName + suffix;
169 auto sasEntry =
171
172 // Add version
174
175 // Add definition
177
178 // Add title
179 auto workspaceTitle = workspace->getTitle();
180 Mantid::DataHandling::H5Util::write(sasEntry, sasEntryTitle, workspaceTitle);
181
182 // Add run
183 const auto runNumber = workspace->getRunNumber();
185
186 return sasEntry;
187}
188
189//------- SASinstrument
190std::string getInstrumentName(const Mantid::API::MatrixWorkspace_sptr &workspace) {
191 auto instrument = workspace->getInstrument();
192 return instrument->getFullName();
193}
194
195std::string getIDF(const Mantid::API::MatrixWorkspace_sptr &workspace) {
196 auto date = workspace->getWorkspaceStartDate();
197 auto instrumentName = getInstrumentName(workspace);
198 return InstrumentFileFinder::getInstrumentFilename(instrumentName, date);
199}
200
201void addDetectors(H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace,
202 const std::vector<std::string> &detectorNames) {
203 // If the group is empty then don't add anything
204 if (!detectorNames.empty()) {
205 for (const auto &detectorName : detectorNames) {
206 if (detectorName.empty()) {
207 continue;
208 }
209
210 std::string sasDetectorName = sasInstrumentDetectorGroupName + detectorName;
211 sasDetectorName = Mantid::DataHandling::makeCanSASRelaxedName(sasDetectorName);
212
213 auto instrument = workspace->getInstrument();
214 auto component = instrument->getComponentByName(detectorName);
215
216 if (component) {
217 const auto sample = instrument->getSample();
218 const auto distance = component->getDistance(*sample);
219 std::map<std::string, std::string> sddAttributes;
220 sddAttributes.insert(std::make_pair(sasUnitAttr, sasInstrumentDetectorSddUnitAttrValue));
225 sddAttributes);
226 }
227 }
228 }
229}
230
239void addInstrument(H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace,
240 const std::string &radiationSource, const std::vector<std::string> &detectorNames) {
241 // Setup instrument
242 const std::string sasInstrumentNameForGroup = sasInstrumentGroupName;
243 auto instrument = Mantid::DataHandling::H5Util::createGroupCanSAS(group, sasInstrumentNameForGroup,
245 auto instrumentName = getInstrumentName(workspace);
246 Mantid::DataHandling::H5Util::write(instrument, sasInstrumentName, instrumentName);
247
248 // Setup the detector
249 addDetectors(instrument, workspace, detectorNames);
250
251 // Setup source
252 const std::string sasSourceName = sasInstrumentSourceGroupName;
256
257 // Add IDF information
258 auto idf = getIDF(workspace);
260}
261
262//------- SASprocess
263
264std::string getDate() {
265 time_t rawtime;
266 time(&rawtime);
267 char temp[25];
268 strftime(temp, 25, "%Y-%m-%dT%H:%M:%S", localtime(&rawtime));
269 std::string sasDate(temp);
270 return sasDate;
271}
272
280void addPropertyFromRunIfExists(Run const &run, std::string const &propertyName, H5::Group &sasGroup,
281 std::string const &sasTerm) {
282 if (run.hasProperty(propertyName)) {
283 auto property = run.getProperty(propertyName);
284 Mantid::DataHandling::H5Util::write(sasGroup, sasTerm, property->value());
285 }
286}
287
294void addProcess(H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace) {
295 // Setup process
296 const std::string sasProcessNameForGroup = sasProcessGroupName;
297 auto process = Mantid::DataHandling::H5Util::createGroupCanSAS(group, sasProcessNameForGroup, nxProcessClassAttr,
299
300 // Add name
302
303 // Add creation date of the file
304 auto date = getDate();
306
307 // Add Mantid version
308 const auto version = std::string(MantidVersion::version());
310
311 // Add log values
312 const auto run = workspace->run();
313 addPropertyFromRunIfExists(run, sasProcessUserFileInLogs, process, sasProcessTermUserFile);
314 addPropertyFromRunIfExists(run, sasProcessBatchFileInLogs, process, sasProcessTermBatchFile);
315}
316
323void addProcess(H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace,
324 const Mantid::API::MatrixWorkspace_sptr &canWorkspace) {
325 // Setup process
326 const std::string sasProcessNameForGroup = sasProcessGroupName;
327 auto process = Mantid::DataHandling::H5Util::createGroupCanSAS(group, sasProcessNameForGroup, nxProcessClassAttr,
329
330 // Add name
332
333 // Add creation date of the file
334 auto date = getDate();
336
337 // Add Mantid version
338 const auto version = std::string(MantidVersion::version());
340
341 const auto run = workspace->run();
342 addPropertyFromRunIfExists(run, sasProcessUserFileInLogs, process, sasProcessTermUserFile);
343 addPropertyFromRunIfExists(run, sasProcessBatchFileInLogs, process, sasProcessTermBatchFile);
344
345 // Add can run number
346 const auto canRun = canWorkspace->getRunNumber();
348}
349
354void createNote(H5::Group &group) {
355 auto process = group.openGroup(sasProcessGroupName);
357}
358
370void addNoteToProcess(H5::Group &group, const std::string &firstEntryName, const std::string &firstEntryValue,
371 const std::string &secondEntryName, const std::string &secondEntryValue) {
372 auto process = group.openGroup(sasProcessGroupName);
373 auto note = process.openGroup(sasNoteGroupName);
374
375 // Populate note
376 Mantid::DataHandling::H5Util::write(note, firstEntryName, firstEntryValue);
377 Mantid::DataHandling::H5Util::write(note, secondEntryName, secondEntryValue);
378}
379
381 auto numberOfHistograms = workspace->getNumberHistograms();
382 WorkspaceDimensionality dimensionality(WorkspaceDimensionality::other);
383 if (numberOfHistograms == 1) {
384 dimensionality = WorkspaceDimensionality::oneD;
385 } else if (numberOfHistograms > 1) {
386 dimensionality = WorkspaceDimensionality::twoD;
387 }
388 return dimensionality;
389}
390
391//------- SASdata
392
393std::string getIntensityUnitLabel(std::string intensityUnitLabel) {
394 if (intensityUnitLabel == "I(q) (cm-1)") {
395 return sasIntensity;
396 } else {
397 return intensityUnitLabel;
398 }
399}
400
401std::string getIntensityUnit(const Mantid::API::MatrixWorkspace_sptr &workspace) {
402 auto iUnit = workspace->YUnit();
403 if (iUnit.empty()) {
404 iUnit = workspace->YUnitLabel();
405 }
406 return iUnit;
407}
408
409std::string getMomentumTransferLabel(std::string momentumTransferLabel) {
410 if (momentumTransferLabel == "Angstrom^-1") {
411 return sasMomentumTransfer;
412 } else {
413 return momentumTransferLabel;
414 }
415}
416
417std::string getUnitFromMDDimension(const Mantid::Geometry::IMDDimension_const_sptr &dimension) {
418 const auto unitLabel = dimension->getMDUnits().getUnitLabel();
419 return unitLabel.ascii();
420}
421
422void addData1D(H5::Group &data, const Mantid::API::MatrixWorkspace_sptr &workspace) {
423 // Add attributes for @signal, @I_axes, @Q_indices,
429
430 if (workspace->hasDx(0)) {
433 }
434
435 //-----------------------------------------
436 // Add Q with units + uncertainty definition
437 const auto &qValue = workspace->points(0);
438 std::map<std::string, std::string> qAttributes;
439 auto qUnit = getUnitFromMDDimension(workspace->getDimension(0));
440 qUnit = getMomentumTransferLabel(qUnit);
441 qAttributes.emplace(sasUnitAttr, qUnit);
442 if (workspace->hasDx(0)) {
443 qAttributes.emplace(sasUncertaintyAttr, sasDataQdev);
444 qAttributes.emplace(sasUncertaintiesAttr, sasDataQdev);
445 }
446
447 writeArray1DWithStrAttributes(data, sasDataQ, qValue.rawData(), qAttributes);
448
449 //-----------------------------------------
450 // Add I with units + uncertainty definition
451 const auto &intensity = workspace->y(0);
452 std::map<std::string, std::string> iAttributes;
453 auto iUnit = getIntensityUnit(workspace);
454 iUnit = getIntensityUnitLabel(iUnit);
455 iAttributes.emplace(sasUnitAttr, iUnit);
456 iAttributes.emplace(sasUncertaintyAttr, sasDataIdev);
457 iAttributes.emplace(sasUncertaintiesAttr, sasDataIdev);
458
459 writeArray1DWithStrAttributes(data, sasDataI, intensity.rawData(), iAttributes);
460
461 //-----------------------------------------
462 // Add Idev with units
463 const auto &intensityUncertainty = workspace->e(0);
464 std::map<std::string, std::string> eAttributes;
465 eAttributes.insert(std::make_pair(sasUnitAttr, iUnit)); // same units as intensity
466
467 writeArray1DWithStrAttributes(data, sasDataIdev, intensityUncertainty.rawData(), eAttributes);
468
469 //-----------------------------------------
470 // Add Qdev with units if available
471 if (workspace->hasDx(0)) {
472 const auto qResolution = workspace->pointStandardDeviations(0);
473 std::map<std::string, std::string> xUncertaintyAttributes;
474 xUncertaintyAttributes.emplace(sasUnitAttr, qUnit);
475
476 writeArray1DWithStrAttributes(data, sasDataQdev, qResolution.rawData(), xUncertaintyAttributes);
477 }
478}
479
480bool areAxesNumeric(const Mantid::API::MatrixWorkspace_sptr &workspace) {
481 const unsigned indices[] = {0, 1};
482 for (const auto index : indices) {
483 auto axis = workspace->getAxis(index);
484 if (!axis->isNumeric()) {
485 return false;
486 }
487 }
488 return true;
489}
490
491class SpectrumAxisValueProvider {
492public:
493 explicit SpectrumAxisValueProvider(Mantid::API::MatrixWorkspace_sptr workspace) : m_workspace(std::move(workspace)) {
494 setSpectrumAxisValues();
495 }
496
497 Mantid::MantidVec::value_type *operator()(const Mantid::API::MatrixWorkspace_sptr & /*unused*/, int index) {
498 auto isPointData = m_workspace->getNumberHistograms() == m_spectrumAxisValues.size();
499 double value = 0;
500 if (isPointData) {
501 value = m_spectrumAxisValues[index];
502 } else {
503 value = (m_spectrumAxisValues[index + 1] + m_spectrumAxisValues[index]) / 2.0;
504 }
505
506 Mantid::MantidVec tempVec(m_workspace->dataY(index).size(), value);
507 m_currentAxisValues.swap(tempVec);
508 return m_currentAxisValues.data();
509 }
510
511private:
512 void setSpectrumAxisValues() {
513 auto sAxis = m_workspace->getAxis(1);
514 for (size_t index = 0; index < sAxis->length(); ++index) {
515 m_spectrumAxisValues.emplace_back((*sAxis)(index));
516 }
517 }
518
520 Mantid::MantidVec m_spectrumAxisValues;
521 Mantid::MantidVec m_currentAxisValues;
522};
523
527template <typename T> class QxExtractor {
528public:
529 T *operator()(const Mantid::API::MatrixWorkspace_sptr &ws, int index) {
530 if (ws->isHistogramData()) {
531 qxPointData.clear();
533 return qxPointData.data();
534 } else {
535 return ws->dataX(index).data();
536 }
537 }
538
539 std::vector<T> qxPointData;
540};
541
582void addData2D(H5::Group &data, const Mantid::API::MatrixWorkspace_sptr &workspace) {
583 if (!areAxesNumeric(workspace)) {
584 std::invalid_argument("SaveNXcanSAS: The provided 2D workspace needs "
585 "to have 2 numeric axes.");
586 }
587 // Add attributes for @signal, @I_axes, @Q_indices,
589 const std::string sasDataIAxesAttr2D = sasDataQ + sasSeparator + sasDataQ;
593 // Write the Q Indices as Int Array
595
596 // Store the 2D Qx data + units
597 std::map<std::string, std::string> qxAttributes;
598 auto qxUnit = getUnitFromMDDimension(workspace->getXDimension());
599 qxUnit = getMomentumTransferLabel(qxUnit);
600 qxAttributes.emplace(sasUnitAttr, qxUnit);
601 QxExtractor<double> qxExtractor;
602 write2DWorkspace(data, workspace, sasDataQx, qxExtractor, qxAttributes);
603
604 // Get 2D Qy data and store it
605 std::map<std::string, std::string> qyAttributes;
606 auto qyUnit = getUnitFromMDDimension(workspace->getDimension(1));
607 qyUnit = getMomentumTransferLabel(qyUnit);
608 qyAttributes.emplace(sasUnitAttr, qyUnit);
609
610 SpectrumAxisValueProvider spectrumAxisValueProvider(workspace);
611 write2DWorkspace(data, workspace, sasDataQy, spectrumAxisValueProvider, qyAttributes);
612
613 // Get 2D I data and store it
614 std::map<std::string, std::string> iAttributes;
615 auto iUnit = getIntensityUnit(workspace);
616 iUnit = getIntensityUnitLabel(iUnit);
617 iAttributes.emplace(sasUnitAttr, iUnit);
618 iAttributes.emplace(sasUncertaintyAttr, sasDataIdev);
619 iAttributes.emplace(sasUncertaintiesAttr, sasDataIdev);
620
621 auto iExtractor = [](const Mantid::API::MatrixWorkspace_sptr &ws, int index) { return ws->dataY(index).data(); };
622 write2DWorkspace(data, workspace, sasDataI, iExtractor, iAttributes);
623
624 // Get 2D Idev data and store it
625 std::map<std::string, std::string> eAttributes;
626 eAttributes.insert(std::make_pair(sasUnitAttr, iUnit)); // same units as intensity
627
628 auto iDevExtractor = [](const Mantid::API::MatrixWorkspace_sptr &ws, int index) { return ws->dataE(index).data(); };
629 write2DWorkspace(data, workspace, sasDataIdev, iDevExtractor, eAttributes);
630}
631
632void addData(H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace) {
633 const std::string sasDataName = sasDataGroupName;
635
636 auto workspaceDimensionality = getWorkspaceDimensionality(workspace);
637 switch (workspaceDimensionality) {
638 case (WorkspaceDimensionality::oneD):
639 addData1D(data, workspace);
640 break;
641 case (WorkspaceDimensionality::twoD):
642 addData2D(data, workspace);
643 break;
644 default:
645 throw std::runtime_error("SaveNXcanSAS: The provided workspace "
646 "dimensionality is not 1D or 2D.");
647 }
648}
649
650//------- SAStransmission_spectrum
651void addTransmission(H5::Group &group, const Mantid::API::MatrixWorkspace_const_sptr &workspace,
652 const std::string &transmissionName) {
653 // Setup process
654 const std::string sasTransmissionName = sasTransmissionSpectrumGroupName + "_" + transmissionName;
657
658 // Add attributes for @signal, @T_axes, @T_indices, @T_uncertainty,
659 // @T_uncertainties, @name, @timestamp
668
669 auto date = getDate();
671
672 //-----------------------------------------
673 // Add T with units + uncertainty definition
674 const auto transmissionData = workspace->y(0);
675 std::map<std::string, std::string> transmissionAttributes;
676 std::string unit = sasNone;
677
678 transmissionAttributes.emplace(sasUnitAttr, unit);
679 transmissionAttributes.emplace(sasUncertaintyAttr, sasTransmissionSpectrumTdev);
680 transmissionAttributes.emplace(sasUncertaintiesAttr, sasTransmissionSpectrumTdev);
681
682 writeArray1DWithStrAttributes(transmission, sasTransmissionSpectrumT, transmissionData.rawData(),
683 transmissionAttributes);
684
685 //-----------------------------------------
686 // Add Tdev with units
687 const auto &transmissionErrors = workspace->e(0);
688 std::map<std::string, std::string> transmissionErrorAttributes;
689 transmissionErrorAttributes.emplace(sasUnitAttr, unit);
690
691 writeArray1DWithStrAttributes(transmission, sasTransmissionSpectrumTdev, transmissionErrors.rawData(),
692 transmissionErrorAttributes);
693
694 //-----------------------------------------
695 // Add lambda with units
696 const auto lambda = workspace->points(0);
697 std::map<std::string, std::string> lambdaAttributes;
698 auto lambdaUnit = getUnitFromMDDimension(workspace->getDimension(0));
699 if (lambdaUnit.empty() || lambdaUnit == "Angstrom") {
700 lambdaUnit = sasAngstrom;
701 }
702 lambdaAttributes.emplace(sasUnitAttr, lambdaUnit);
703
704 writeArray1DWithStrAttributes(transmission, sasTransmissionSpectrumLambda, lambda.rawData(), lambdaAttributes);
705}
706} // namespace
707
708namespace Mantid::DataHandling {
709// Register the algorithm into the AlgorithmFactory
711
712
714
716 auto inputWSValidator = std::make_shared<Kernel::CompositeValidator>();
717 inputWSValidator->add<API::WorkspaceUnitValidator>("MomentumTransfer");
718 inputWSValidator->add<API::CommonBinsValidator>();
719 declareProperty(std::make_unique<Mantid::API::WorkspaceProperty<>>("InputWorkspace", "", Kernel::Direction::Input,
720 inputWSValidator),
721 "The input workspace, which must be in units of Q");
722 declareProperty(std::make_unique<Mantid::API::FileProperty>("Filename", "", API::FileProperty::Save, ".h5"),
723 "The name of the .h5 file to save");
724
725 std::vector<std::string> radiation_source{"Spallation Neutron Source",
726 "Pulsed Reactor Neutron Source",
727 "Reactor Neutron Source",
728 "Synchrotron X-ray Source",
729 "Pulsed Muon Source",
730 "Rotating Anode X-ray",
731 "Fixed Tube X-ray",
732 "neutron",
733 "x-ray",
734 "muon",
735 "electron"};
736 declareProperty("RadiationSource", "Spallation Neutron Source",
737 std::make_shared<Kernel::StringListValidator>(radiation_source), "The type of radiation used.");
738 declareProperty("DetectorNames", "",
739 "Specify in a comma separated list, which detectors to store "
740 "information about; \nwhere each name must match a name "
741 "given for a detector in the [[IDF|instrument definition "
742 "file (IDF)]]. \nIDFs are located in the instrument "
743 "sub-directory of the MantidPlot install directory.");
744
746 std::make_unique<API::WorkspaceProperty<>>("Transmission", "", Kernel::Direction::Input, PropertyMode::Optional,
747 std::make_shared<API::WorkspaceUnitValidator>("Wavelength")),
748 "The transmission workspace. Optional. If given, will be saved at "
749 "TransmissionSpectrum");
750
752 "TransmissionCan", "", Kernel::Direction::Input, PropertyMode::Optional,
753 std::make_shared<API::WorkspaceUnitValidator>("Wavelength")),
754 "The transmission workspace of the Can. Optional. If given, will be "
755 "saved at TransmissionSpectrum");
756
757 declareProperty("SampleTransmissionRunNumber", "", "The run number for the sample transmission workspace. Optional.");
758 declareProperty("SampleDirectRunNumber", "", "The run number for the sample direct workspace. Optional.");
759 declareProperty("CanScatterRunNumber", "", "The run number for the can scatter workspace. Optional.");
760 declareProperty("CanDirectRunNumber", "", "The run number for the can direct workspace. Optional.");
761}
762
763std::map<std::string, std::string> SaveNXcanSAS::validateInputs() {
764 // The input should be a Workspace2D
766 std::map<std::string, std::string> result;
767 if (!workspace || !std::dynamic_pointer_cast<const Mantid::DataObjects::Workspace2D>(workspace)) {
768 result.emplace("InputWorkspace", "The InputWorkspace must be a Workspace2D.");
769 }
770
771 // Transmission data should be 1D
772 Mantid::API::MatrixWorkspace_sptr transmission = getProperty("Transmission");
773 Mantid::API::MatrixWorkspace_sptr transmissionCan = getProperty("TransmissionCan");
774
775 auto checkTransmission = [&result](const Mantid::API::MatrixWorkspace_sptr &trans, const std::string &propertyName) {
776 if (trans->getNumberHistograms() != 1) {
777 result.emplace(propertyName, "The input workspaces for transmissions have to be 1D.");
778 }
779 };
780
781 if (transmission) {
782 checkTransmission(transmission, "Trasmission");
783 }
784
785 if (transmissionCan) {
786 checkTransmission(transmissionCan, "TransmissionCan");
787 }
788
789 return result;
790}
791
794 std::string filename = getPropertyValue("Filename");
795
796 std::string radiationSource = getPropertyValue("RadiationSource");
797 std::string detectorNames = getPropertyValue("DetectorNames");
798
799 Mantid::API::MatrixWorkspace_sptr transmissionSample = getProperty("Transmission");
800 Mantid::API::MatrixWorkspace_sptr transmissionCan = getProperty("TransmissionCan");
801
802 // Remove the file if it already exists
803 if (Poco::File(filename).exists()) {
804 Poco::File(filename).remove();
805 }
806
807 H5::H5File file(filename, H5F_ACC_EXCL);
808
809 const std::string suffix("01");
810
811 // Setup progress bar
812 int numberOfSteps = 4;
813 if (transmissionSample) {
814 ++numberOfSteps;
815 }
816
817 if (transmissionCan) {
818 ++numberOfSteps;
819 }
820
821 Progress progress(this, 0.1, 1.0, numberOfSteps);
822
823 // Add a new entry
824 progress.report("Adding a new entry.");
825 auto sasEntry = addSasEntry(file, workspace, suffix);
826
827 // Add the instrument information
828 progress.report("Adding instrument information.");
829 const auto detectors = splitDetectorNames(detectorNames);
830 addInstrument(sasEntry, workspace, radiationSource, detectors);
831
832 // Get additional run numbers
833 const auto sampleTransmissionRun = getPropertyValue("SampleTransmissionRunNumber");
834 const auto sampleDirectRun = getPropertyValue("SampleDirectRunNumber");
835 const auto canScatterRun = getPropertyValue("CanScatterRunNumber");
836 const auto canDirectRun = getPropertyValue("CanDirectRunNumber");
837
838 // Add the process information
839 progress.report("Adding process information.");
840 if (transmissionCan) {
841 addProcess(sasEntry, workspace, transmissionCan);
842 } else {
843 addProcess(sasEntry, workspace);
844 }
845
846 if (transmissionCan || transmissionSample) {
847 createNote(sasEntry);
848 if (transmissionCan)
849 addNoteToProcess(sasEntry, sasProcessTermCanScatter, canScatterRun, sasProcessTermCanDirect, canDirectRun);
850 if (transmissionSample)
851 addNoteToProcess(sasEntry, sasProcessTermSampleTrans, sampleTransmissionRun, sasProcessTermSampleDirect,
852 sampleDirectRun);
853 }
854
855 // Add the transmissions for sample
856 if (transmissionSample) {
857 progress.report("Adding sample transmission information.");
858 addTransmission(sasEntry, transmissionSample, sasTransmissionSpectrumNameSampleAttrValue);
859 }
860
861 // Add the transmissions for can
862 if (transmissionCan) {
863 progress.report("Adding can transmission information.");
864 addTransmission(sasEntry, transmissionCan, sasTransmissionSpectrumNameCanAttrValue);
865 }
866
867 // Add the data
868 progress.report("Adding data.");
869 addData(sasEntry, workspace);
870
871 file.close();
872}
873
879std::string makeCanSASRelaxedName(const std::string &input) {
880 bool isStrict = false;
881 auto emptyCapitalizationStrategy = [](std::string &) {};
882 return makeCompliantName(input, isStrict, emptyCapitalizationStrategy);
883}
884
885} // namespace Mantid::DataHandling
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
const std::vector< double > * lambda
double value
The value of the point.
Definition: FitMW.cpp:51
IPeaksWorkspace_sptr workspace
Definition: IndexPeaks.cpp:114
std::map< DeltaEMode::Type, std::string > index
Definition: DeltaEMode.cpp:19
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
Definition: Algorithm.cpp:2026
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Definition: Algorithm.cpp:231
A validator which provides a TENTATIVE check that a workspace contains common bins in each spectrum.
@ Save
to specify a file to write to, the file may or may not exist
Definition: FileProperty.h:49
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.
Definition: LogManager.cpp:265
Kernel::Property * getProperty(const std::string &name) const
Returns the named property as a pointer.
Definition: LogManager.cpp:404
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
This class stores information regarding an experimental run as a series of log entries.
Definition: Run.h:38
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
SaveNXcanSAS : Saves a reduced workspace in the NXcanSAS format.
Definition: SaveNXcanSAS.h:18
void init() override
Initialisation code.
void exec() override
Execution code.
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
static const char * version()
The full version number.
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
MANTID_DATAHANDLING_DLL H5::Group createGroupCanSAS(H5::Group &group, const std::string &name, const std::string &nxtype, const std::string &cstype)
void writeNumAttribute(LocationType &location, const std::string &name, const NumT &value)
Definition: H5Util.cpp:124
void writeArray1D(H5::Group &group, const std::string &name, const std::vector< NumT > &values)
MANTID_DATAHANDLING_DLL void write(H5::Group &group, const std::string &name, const std::string &value)
void writeStrAttribute(LocationType &location, const std::string &name, const std::string &value)
Definition: H5Util.cpp:116
void writeScalarDataSetWithStrAttributes(H5::Group &group, const std::string &name, const T &value, const std::map< std::string, std::string > &attributes)
Definition: H5Util.cpp:152
const std::string sasTransmissionSpectrumNameAttr
const std::string sasTransmissionSpectrumNameCanAttrValue
const std::string sasInstrumentClassAttr
const std::string sasInstrumentSourceRadiation
const std::string sasDataQUncertaintiesAttr
const std::string sasTransmissionSpectrumTdev
const std::string sasTransmissionSpectrumT
const std::string nxTransmissionSpectrumClassAttr
const std::string sasTransmissionSpectrumTIndices
const std::string sasUncertaintyAttr
Standards state that "uncertainties" should be used, however different facilities interpret the stand...
const std::string sasInstrumentDetectorSdd
const std::string sasTransmissionSpectrumTUncertainties
const std::string sasProcessTermSampleDirect
const std::string sasTransmissionSpectrumLambda
const std::string sasInstrumentDetectorClassAttr
const std::string sasInstrumentSourceGroupName
const std::string nxInstrumentSourceClassAttr
const std::string sasProcessTermCanScatter
const std::string sasTransmissionSpectrumNameSampleAttrValue
const std::string sasInstrumentDetectorSddUnitAttrValue
const std::string sasDataQUncertaintyAttr
const std::string sasProcessUserFileInLogs
const std::string sasTransmissionSpectrumTUncertainty
const std::string sasInstrumentSourceClassAttr
const std::string nxInstrumentDetectorClassAttr
const std::string sasEntryVersionAttrValue
const std::string sasEntryDefinitionFormat
const std::string sasDataIUncertaintiesAttr
const std::string sasProcessBatchFileInLogs
const std::string sasTransmissionSpectrumTimeStampAttr
const std::string sasInstrumentDetectorName
const std::string sasTransmissionSpectrumGroupName
const std::string sasInstrumentDetectorGroupName
const std::string sasTransmissionSpectrumClassAttr
const std::string sasDataIUncertaintyAttr
const std::string sasProcessTermSampleTrans
const std::string sasInstrumentGroupName
bool exists(::NeXus::File &file, const std::string &name)
Based on the current group in the file, does the named sub-entry exist?
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...
std::shared_ptr< const IMDDimension > IMDDimension_const_sptr
Shared Pointer to const IMDDimension.
Definition: IMDDimension.h:101
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.
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
Definition: cow_ptr.h:172
Definition: NDArray.h:49
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition: Property.h:53