39using AttrMap = std::map<std::string, std::string>;
46 return iUnit ==
"I(q) (cm-1)" ?
sasIntensity : std::string(iUnit);
50 const std::string &expectedUnit =
"Angstrom^-1",
53 const auto unitLabel = dimension->getMDUnits().getUnitLabel().ascii();
54 return (unitLabel.empty() || unitLabel == expectedUnit) ? sasFormatUnit : std::string(unitLabel);
57template <
typename NumT>
58void writeArray1DWithStrAttributes(H5::Group &
group,
const std::string &dataSetName,
const std::vector<NumT> &values,
59 const AttrMap &attributes) {
61 const auto dataSet =
group.openDataSet(dataSetName);
62 for (
const auto &[attributeName, attributeValue] : attributes) {
67void writeDataSetAttributes(
const H5::H5Object &dataSet,
const AttrMap &attributes) {
69 for (
const auto &[nameAttr, valueAttr] : attributes) {
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);
86template <
typename T>
class QxExtractor {
89 if (ws->isHistogramData()) {
92 return qxPointData.data();
94 return ws->dataX(
index).data();
97 std::vector<T> qxPointData;
103class SpectrumAxisValueProvider {
106 setSpectrumAxisValues();
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;
115 m_currentAxisValues.swap(tempVec);
116 return m_currentAxisValues.data();
120 void setSpectrumAxisValues() {
121 const auto sAxis = m_workspace->getAxis(1);
123 m_spectrumAxisValues.emplace_back((*sAxis)(
index));
136template <
typename T>
class WorkspaceGroupDataExtractor {
139 : m_workspace(
workspace), m_extractError(extractError) {}
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();
146 void setExtractErrors(
bool extractError) { m_extractError = extractError; }
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);
160std::string makeCompliantName(
const std::string &input,
bool isStrict,
161 const std::function<
void(std::string &)> &capitalizeStrategy) {
164 if (!isCanSASCompliant(isStrict, output)) {
165 output = std::regex_replace(output, std::regex(
"[-\\.]"), std::string(
"_"));
166 capitalizeStrategy(output);
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);
179 return workspace->getInstrument()->getFullName();
183 const auto date =
workspace->getWorkspaceStartDate();
184 const auto instrumentName = getInstrumentName(
workspace);
190std::string getDate() {
194 strftime(temp, 25,
"%Y-%m-%dT%H:%M:%S", localtime(&rawtime));
195 std::string sasDate(temp);
206void addPropertyFromRunIfExists(
const Run &run,
const std::string &propertyName, H5::Group &sasGroup,
207 const std::string &sasTerm) {
209 const auto *
property = run.
getProperty(propertyName);
216template <
typename WorkspaceExtractorFunctor>
217void writePolarizedDataToFile(H5::DataSet &dataSet, WorkspaceExtractorFunctor func,
const DataDimensions &dimensions,
219 auto stateConverter = [](
int spin) -> std::string {
return (spin == 1) ?
"+1" :
std::to_string(spin); };
222 const H5::DataSpace memSpace(
static_cast<int>(rank), dimensions.
getSlabShape().data());
224 auto pos = std::vector<hsize_t>(rank, 0);
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));
230 if (!
index.has_value()) {
231 throw std::runtime_error(
"Couldn't find workspace for spin state " + state);
238 fileSpace.selectHyperslab(H5S_SELECT_SET, dimensions.
getSlabShape().data(), pos.data());
243 fileSpace.selectHyperslab(H5S_SELECT_SET, dimensions.
getSlabShape().data(), pos.data());
251template <
typename WorkspaceExtractorFunctor>
254 const AttrMap &attributes = {}) {
255 const auto ws0 = std::dynamic_pointer_cast<MatrixWorkspace>(workspaces->getItem(0));
257 const auto propList =
258 setCompression(
static_cast<int>(dataDimensions.getDataShape().size()), dataDimensions.getSlabShape().data());
260 group.createDataSet(dataSetName, dataDimensions.getDataType(), dataDimensions.getDataSpace(), propList);
261 writePolarizedDataToFile(dataSet, std::move(func), dataDimensions, spin);
262 writeDataSetAttributes(dataSet, attributes);
265void writeSpinDataAttributes(H5::Group &data,
const SpinVectorBuilder &spinPairs) {
269 AttrMap polAttributes;
271 writeArray1DWithStrAttributes(data,
sasDataPin, spinPairs.
pIn, polAttributes);
272 writeArray1DWithStrAttributes(data,
sasDataPout, spinPairs.
pOut, polAttributes);
277void writeStandardDataAttributes(
const H5::Group &data,
const std::string &IaxesAttr =
"Q",
278 const std::vector<int> &qIndices = {0}) {
297template <
typename Functor>
299 Functor func,
const AttrMap &attributes) {
303 const auto propList =
307 auto dataSet =
group.createDataSet(dataSetName, dimensions.
getDataType(), fileSpace, propList);
311 const H5::DataSpace memSpace(1, memSpaceDimension);
313 auto pos = std::vector<hsize_t>{0, 0};
317 fileSpace.selectHyperslab(H5S_SELECT_SET, dimensions.
getSlabShape().data(), pos.data());
323 writeDataSetAttributes(dataSet, attributes);
329 auto qUnit = getMDUnit(
workspace->getDimension(0));
340 const auto qResolution =
workspace->pointStandardDeviations(0);
341 AttrMap xUncertaintyAttributes;
342 xUncertaintyAttributes.emplace(
sasUnitAttr, qUnit);
343 writeArray1DWithStrAttributes(data,
sasDataQdev, qResolution.rawData(), xUncertaintyAttributes);
347 const auto &qValue =
workspace->points(0);
348 writeArray1DWithStrAttributes(data,
sasDataQ, qValue.rawData(), qAttributes);
353 AttrMap qxAttributes;
354 auto qxUnit = getMDUnit(
workspace->getDimension(0));
356 const QxExtractor<double> qxExtractor;
360 AttrMap qyAttributes;
361 auto qyUnit = getMDUnit(
workspace->getDimension(1));
364 const SpectrumAxisValueProvider spectrumAxisValueProvider(
workspace);
365 write2DWorkspace(data,
workspace,
sasDataQy, spectrumAxisValueProvider, qyAttributes);
375 auto path = std::filesystem::path(baseFilename);
376 if (!addDigitSuffix) {
381 path.replace_extension(
"");
395 bool isStrict =
false;
396 auto emptyCapitalizationStrategy = [](std::string &) {};
397 return makeCompliantName(input, isStrict, emptyCapitalizationStrategy);
407 const std::vector<std::string> &detectorNames) {
409 for (
const auto &detectorName : detectorNames) {
410 if (!detectorName.empty()) {
414 auto instrument =
workspace->getInstrument();
415 auto component = instrument->getComponentByName(detectorName);
418 const auto sample = instrument->getSample();
419 const auto distance = component->getDistance(*sample);
420 AttrMap sddAttributes;
443 const std::string &geometry,
double beamHeight,
double beamWidth,
444 const std::vector<std::string> &detectorNames) {
449 auto instrumentName = getInstrumentName(
workspace);
468 AttrMap beamSizeAttrs;
470 if (beamHeight != 0) {
473 if (beamWidth != 0) {
482 }
catch (
const std::runtime_error &) {
498 const std::string &componentType,
const std::string &groupSuffix) {
503 const auto instrument =
workspace->getInstrument();
504 const auto component = instrument->getComponentByName(instrumentAttr.getComponentName());
507 auto polarizerGroup =
509 instrumentAttr.nxPolarizerClassAttr(), instrumentAttr.sasPolarizerClassAttr());
511 const auto type = component->getStringParameter(instrumentAttr.sasPolarizerIDFDeviceType());
512 H5Util::write(polarizerGroup, instrumentAttr.sasPolarizerName(), componentName);
513 H5Util::write(polarizerGroup, instrumentAttr.sasPolarizerDeviceType(), type.front());
516 const auto samplePos = instrument->getSample()->getPos();
517 const auto compPos = component->getPos();
518 auto distance = samplePos.Z() - compPos.Z();
520 AttrMap distanceAttrs;
521 distanceAttrs.insert(std::make_pair(
sasUnitAttr, instrumentAttr.sasPolarizerDistanceUnitAttr()));
538 if (!directions.empty()) {
539 AttrMap magFieldAttrs;
541 for (
size_t i = 0; i < directions.size(); i++)
555 const std::string &emFieldDir) {
556 if (emFieldStrengthLog.empty() && emFieldDir.empty()) {
566 if (
workspace->run().hasProperty(emFieldStrengthLog)) {
567 const auto magFStrength =
workspace->run().getLogAsSingleValue(emFieldStrengthLog);
568 const auto magFStrengthUnits =
workspace->run().getProperty(emFieldStrengthLog)->units();
570 AttrMap magFieldAttrs;
571 if (!magFStrengthUnits.empty()) {
572 magFieldAttrs.insert(std::make_pair(
sasUnitAttr, magFStrengthUnits));
588 if (sampleThickness == 0) {
596 AttrMap sampleThicknessAttrs;
599 sampleThicknessAttrs);
618 auto date = getDate();
631 const auto canRun = canWorkspace->getRunNumber();
643 const std::string &transmissionName) {
657 auto date = getDate();
662 const auto transmissionData =
workspace->y(0);
663 AttrMap transmissionAttributes;
671 transmissionAttributes);
675 const auto &transmissionErrors =
workspace->e(0);
676 AttrMap transmissionErrorAttributes;
677 transmissionErrorAttributes.emplace(
sasUnitAttr, unit);
680 transmissionErrorAttributes);
685 AttrMap lambdaAttributes;
698 writeStandardDataAttributes(data);
703 auto iAttributes = prepareUnitAttributes(
workspace);
704 writeArray1DWithStrAttributes(data,
sasDataI, intensity.rawData(), iAttributes);
708 const auto &intensityUncertainty =
workspace->e(0);
712 writeArray1DWithStrAttributes(data,
sasDataIdev, intensityUncertainty.rawData(), eAttributes);
757 writeStandardDataAttributes(data, sasDataIAxesAttr2D, std::vector<int>{0, 1});
761 auto iAttributes = prepareUnitAttributes(
workspace);
783 const auto ws0 = std::dynamic_pointer_cast<MatrixWorkspace>(wsGroup->getItem(0));
788 std::vector<int> qIndices = {0, 1, 2};
791 qIndices.push_back(3);
794 writeStandardDataAttributes(data, sasDataIAxesAttrSpin, qIndices);
797 writeSpinDataAttributes(data, spinPairs);
808 throw(std::invalid_argument(
"Incorrect dimension for workspace"));
812 auto iAttributes = prepareUnitAttributes(ws0);
815 WorkspaceGroupDataExtractor<double> wsGroupExtractor(wsGroup);
816 savePolarizedDataSet(data, wsGroup, wsGroupExtractor,
sasDataI, spinPairs, iAttributes);
819 wsGroupExtractor.setExtractErrors(
true);
822 savePolarizedDataSet(data, wsGroup, wsGroupExtractor,
sasDataIdev, spinPairs, eAttributes);
832 std::filesystem::remove(path);
const std::vector< double > * lambda
double value
The value of the point.
IPeaksWorkspace_sptr workspace
std::map< DeltaEMode::Type, std::string > index
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.
const H5::DataSpace & getDataSpace() const
const std::vector< hsize_t > & getSlabShape() const
const hsize_t & getNumberOfPoints() const
const H5::DataType & getDataType() const
const std::vector< hsize_t > & getDataShape() const
const hsize_t & getNumberOfHistograms() 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
constexpr int sasDataPoutIndicesValue
std::filesystem::path MANTID_DATAHANDLING_DLL prepareFilename(const std::string &baseFilename, bool addDigitSuffix=false, size_t index=0)
const std::string sasInstrumentIDF
const std::string sasTransmissionSpectrumNameAttr
const std::string sasProcessDate
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.
const std::string sasDataIdev
const std::string sasProcessGroupName
const std::string sasInstrumentName
const std::string sasDataQx
const std::string NX_CANSAS_EXTENSION
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 sasInstrumentClassAttr
const std::string sasSignal
const std::string nxProcessClassAttr
const std::string sasProcessTermUserFile
const std::string sasInstrumentSampleClassAttr
const std::string sasInstrumentSourceRadiation
const std::string sasDataQUncertaintiesAttr
const std::string sasTransmissionSpectrumTdev
const std::string sasDataIAxesAttr
const std::string sasTransmissionSpectrumT
const std::string sasDataQy
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
const std::string sasDataQIndicesAttr
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 sasDataQdev
const std::string sasSampleMagneticField
const std::string sasUncertaintyAttr
Standards state that "uncertainties" should be used, however different facilities interpret the stand...
const std::string sasMomentumTransfer
const std::string sasInstrumentDetectorSdd
const std::string sasTransmissionSpectrumTUncertainties
const std::string sasNone
const std::string sasDataPout
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 sasProcessNameValue
const std::string sasSampleEMFieldDirectionAzimuthal
const std::string sasTransmissionSpectrumLambda
const std::string sasDataQ
const std::string sasDataPinIndicesAttr
constexpr int sasDataPinIndicesValue
const std::string sasDataI
const std::string sasProcessTermCan
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
const std::string sasUncertaintiesAttr
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 sasDataPoutIndicesAttr
const std::string sasUnitAttr
const std::string nxInstrumentApertureClassAttr
const std::string sasInstrumentApertureShape
const std::string nxInstrumentSampleClassAttr
const std::string sasInstrumentDetectorSddUnitAttrValue
const std::string sasProcessName
const std::string sasDataQUncertaintyAttr
const std::string sasProcessUserFileInLogs
const std::string sasDataPolarizationUnitAttr
const std::string sasTransmissionSpectrumTUncertainty
const std::string sasIntensity
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 sasProcessTermSvn
const std::string sasProcessBatchFileInLogs
const std::string sasSeparator
const std::string sasTransmissionSpectrumTimeStampAttr
const std::string sasAngstrom
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 nxInstrumentClassAttr
const std::string sasInstrumentDetectorName
const std::string sasTransmissionSpectrumGroupName
const std::string sasProcessClassAttr
const std::string sasDataPin
const std::string sasProcessTermBatchFile
const std::string sasInstrumentDetectorGroupName
const std::string sasTransmissionSpectrumClassAttr
const std::string sasDataIUncertaintyAttr
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
const std::string sasInstrumentGroupName
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)
MANTID_NEXUS_DLL H5::FileAccPropList defaultFileAcc()
Default file access is H5F_CLOSE_STRONG.
void writeNumAttribute(const H5::H5Object &object, const std::string &name, const NumT &value)
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)
Header for a base Nexus::Exception.
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
std::string to_string(const wide_integer< Bits, Signed > &n)
std::vector< std::string > spinVec