23#include "MantidNexus/NexusFile.h"
34const std::string NX_SPIN_LOG =
"spin_state_NXcanSAS";
35const std::map<std::string, int> SAMPLE_GEOMETRIES = {
36 {
"cylinder", 1}, {
"flat plate", 2}, {
"flatplate", 2}, {
"disc", 3}};
38std::string getNameOfEntry(
const H5::H5File &root) {
39 auto numberOfObjects = root.getNumObjs();
40 if (numberOfObjects != 1) {
41 throw std::invalid_argument(
"LoadNXcanSAS: Trying to load multiperiod "
42 "data. This is currently not supported.");
44 auto objectType = root.getObjTypeByIdx(0);
45 if (objectType != H5G_GROUP) {
46 throw std::invalid_argument(
"LoadNXcanSAS: The object below the root is not a H5::Group.");
49 return root.getObjnameByIdx(0);
53 const auto &[dimSpectrumAxis, dimBin, _] = dimInfo;
56 "Workspace2D", dimSpectrumAxis , asHistogram ? dimBin + 1 : dimBin , dimBin );
59std::vector<double> getNumDataSetIfExists(
const H5::Group &
group,
const std::string &datasetName) {
60 if (
group.nameExists(datasetName)) {
61 return H5Util::readArray1DCoerce<double>(
group, datasetName);
66std::string getStrDataSetIfExists(
const H5::Group &
group,
const std::string &datasetName) {
67 if (
group.nameExists(datasetName)) {
77 const std::string &logUnits =
"") {
79 auto property = std::make_unique<PropertyWithValue<T>>(logName, logValue);
80 if (!logUnits.empty()) {
81 property->setUnits(logUnits);
83 run.addProperty(std::move(property));
87 const auto addLogFromGroup = [
workspace](
const H5::Group &
group,
const std::string &sasTerm,
88 const std::string &propertyName) {
89 const auto propValue = getStrDataSetIfExists(
group, sasTerm);
90 if (!propValue.empty()) {
91 addLogToWs(
workspace, propertyName, propValue);
113bool checkPolarization(
const H5::Group &
group) {
117 throw std::invalid_argument(
"Polarized data requires to have Pin and Pout axes");
127std::pair<std::vector<int>, std::vector<int>> loadSpinVectors(
const H5::Group &
group) {
129 std::vector<int> pIn;
130 std::vector<int> pOut;
131 if (checkPolarization(
group)) {
135 return std::make_pair(pIn, pOut);
148std::vector<SpinState> prepareSpinIndexes(
const std::vector<int> &pIn,
const std::vector<int> &pOut) {
150 auto spinToString = [](
const int spinIndex) {
154 std::vector<SpinState> spinIndexes;
155 for (
size_t i = 0; i < pIn.size(); i++) {
156 for (
size_t j = 0; j < pOut.size(); j++) {
158 state.
strSpinState = spinToString(pIn.at(i)) + spinToString(pOut.at(j));
160 spinIndexes.push_back(std::move(state));
174 for (
const auto &log : logNames) {
175 auto logValue = getNumDataSetIfExists(
group, log);
176 if (!logValue.empty()) {
177 std::string logUnits;
179 addLogToWs(
workspace, log, logValue.front(), logUnits);
185std::optional<H5::Group> getGroupIfExists(
const H5::Group &
group,
const std::string &groupName) {
186 if (
group.nameExists(groupName)) {
187 return group.openGroup(groupName);
197std::optional<Sample> loadSample(
const H5::Group &
group) {
200 auto apertureGroup = std::optional<H5::Group>();
201 if (instrumentGroup.has_value()) {
206 if (!apertureGroup.has_value() && !sampleGroup.has_value()) {
210 if (apertureGroup.has_value()) {
213 sample.setHeight(
height.front());
216 if (!width.empty()) {
217 sample.setWidth(width.front());
220 boost::to_lower(geometry);
221 if (!geometry.empty()) {
222 SAMPLE_GEOMETRIES.contains(geometry) ? sample.setGeometryFlag(SAMPLE_GEOMETRIES.at(geometry))
223 : sample.setGeometryFlag(0);
228 if (sampleGroup.has_value()) {
230 if (!thickness.empty()) {
231 sample.setThickness(thickness.front());
245 const auto instAlg = Mantid::API::AlgorithmManager::Instance().createUnmanaged(
"LoadInstrument");
246 instAlg->initialize();
247 instAlg->setChild(
true);
248 instAlg->setProperty(
"Workspace",
workspace);
249 instAlg->setProperty(
"InstrumentName", instrumentInfo.
instrumentName);
250 if (!instrumentInfo.
idf.empty()) {
251 instAlg->setProperty(
"Filename", instrumentInfo.
idf);
253 instAlg->setProperty(
"RewriteSpectraMap",
"False");
255 }
catch (std::invalid_argument &) {
257 }
catch (std::runtime_error &) {
258 g_log.
information(
"Unable to successfully run LoadInstrument Child Algorithm.");
283std::vector<hsize_t> updateOffset(
const int axesIndex,
const std::pair<size_t, size_t> &spinIndexPair,
284 std::vector<hsize_t> &slabShape) {
285 const bool isXAxis = axesIndex == WorkspaceDataAxes::X || axesIndex == WorkspaceDataAxes::XErr;
286 const auto &[indexPin, indexPout] = spinIndexPair;
287 auto position = std::vector<hsize_t>(slabShape.size(), 0);
288 if (slabShape.size() > 2) {
291 slabShape = std::vector<hsize_t>(slabShape.cbegin() + 2, slabShape.cend());
301std::string getStrAttribute(
const H5::DataSet &dataSet,
const std::string &attrName) {
302 std::string attrValue;
311struct WorkspaceDataInserter {
314 void insertData(
const size_t index,
const std::vector<double> &data)
const {
329 throw std::runtime_error(
"Provided axis is not compatible with workspace.");
333 void setUnits(
const H5::DataSet &dataSet)
const {
336 }
else if (axisType ==
X) {
337 workspace->getAxis(0)->setUnit(
"MomentumTransfer");
341 void setAxisType(
const int type) { axisType = type; }
355void readDataIntoWorkspace(
const H5::DataSet &dataSet,
const WorkspaceDataInserter &inserter,
356 const std::vector<hsize_t> &slabShape,
const hsize_t nPoints,
const hsize_t nHistograms,
357 std::vector<hsize_t> &offset) {
360 const std::array<hsize_t, 1> memSpaceDimension = {nPoints};
361 const H5::DataSpace memSpace(1, memSpaceDimension.data());
362 const bool isPolarizedDataSet = slabShape.size() > 2;
363 const auto histogramIndex = isPolarizedDataSet ? slabShape.size() - 2 : 0;
365 const auto fileSpace = dataSet.getSpace();
366 auto data = std::vector<double>(nPoints, 0);
369 fileSpace.selectHyperslab(H5S_SELECT_SET, slabShape.data(), offset.data());
371 inserter.insertData(
index, data);
372 offset.at(histogramIndex)++;
386 const std::array<hsize_t, 2> slabShape = {nHistograms, 1};
387 const std::array<hsize_t, 2>
position = {0, 0};
390 const std::array<hsize_t, 1> memSpaceDimensions = {nHistograms};
391 const H5::DataSpace memSpace(1, memSpaceDimensions.data());
394 const auto fileSpace = dataSet.getSpace();
395 fileSpace.selectHyperslab(H5S_SELECT_SET, slabShape.data(),
position.data());
398 auto data = std::vector<double>(nHistograms);
401 auto newAxis = std::make_unique<Mantid::API::NumericAxis>(data);
402 workspace->replaceAxis(1, std::move(newAxis));
405 workspace->getAxis(1)->setUnit(
"MomentumTransfer");
409bool fileHasTransmissionEntry(
const H5::Group &entry,
const std::string &
name) {
411 if (!hasTransmission) {
414 return hasTransmission;
434 std::vector<double>
lambda;
441 const std::string objectName{transmission.getObjName()};
442 throw std::runtime_error(
"Unexpected array size for lambda in transmission group '" + objectName +
446 workspace->getAxis(0)->setUnit(
"Wavelength");
447 workspace->setYUnitLabel(
"Transmission");
462 const std::string &extn = descriptor.
extension();
463 if (extn !=
".nxs" && extn !=
".h5") {
468 for (
auto const &[sasEntry, nxClass] : entries) {
471 std::string
const definitionFromFile = descriptor.
getStrData(dataAddress);
485 const std::vector<std::string> exts{
".nxs",
".h5"};
487 "The name of the NXcanSAS file to read, as a full or relative path.");
490 "The name of the workspace to be created as the output of "
491 "the algorithm. A workspace of this name will be created "
492 "and stored in the Analysis Data Service. For multiperiod "
493 "files, one workspace may be generated for each period. "
494 "Currently only one workspace can be saved at a time so "
495 "multiperiod Mantid files are not generated.");
498 "Load the transmission related data from the file if it is present "
499 "(optional, default False).");
504 const bool isLoadTransmissionChecked =
getProperty(
"LoadTransmission");
507 const auto entry = file.openGroup(getNameOfEntry(file));
512 const size_t stepsPerSpinState = dataInfo.spinStates * 5;
513 const auto numberOfSteps = isLoadTransmissionChecked ? stepsPerSpinState + 1 : stepsPerSpinState;
514 m_progress = std::make_unique<API::Progress>(
this, 0.1, 1.0, numberOfSteps);
521 if (isLoadTransmissionChecked) {
529 const auto wsOut = dataInfo.spinStates == 1 ? wsGroup->getItem(0) : std::dynamic_pointer_cast<Workspace>(wsGroup);
544 const bool hasPolarizedData)
const {
558 if (sample.has_value()) {
559 workspace->mutableSample() = sample.value();
563 loadInstrument(
workspace, instrumentInfo);
573 const std::pair<size_t, size_t> &spinIndexPair)
const {
576 WorkspaceDataInserter dataInserter(
workspace);
577 auto dataSets = std::map<std::string, int>{
584 const auto nPoints =
m_dataDims->getNumberOfPoints();
585 const auto nHisto =
m_dataDims->getNumberOfHistograms();
587 for (
const auto &[setName, axesIndex] : dataSets) {
588 auto dataSet = dataGroup.openDataSet(setName);
589 auto offset = updateOffset(axesIndex, spinIndexPair, slabShape);
590 dataInserter.setAxisType(axesIndex);
591 readDataIntoWorkspace(dataSet, dataInserter, slabShape, nPoints, nHisto, offset);
592 dataInserter.setUnits(dataSet);
609 const auto &[pIn, pOut] = loadSpinVectors(
group);
610 const auto spinPairs =
611 !pIn.empty() && !pOut.empty() ? std::optional(std::make_pair(pIn.size(), pOut.size())) : std::nullopt;
613 const auto spinStates = spinPairs.has_value() ? prepareSpinIndexes(pIn, pOut) :
634 auto dataOut = std::make_shared<WorkspaceGroup>();
635 const auto sample = loadSample(
group);
637 for (
const auto &[spinStr, spinIndexPair] : states) {
641 loadData(dataGroup, ws, spinIndexPair);
643 if (!spinStr.empty()) {
644 addLogToWs(ws, NX_SPIN_LOG, spinStr);
645 ws->setTitle(wsName +
"_" + spinStr);
647 dataOut->addWorkspace(ws);
654 if (!fileHasTransmissionEntry(entry,
name)) {
666 loadInstrument(
workspace, instrumentInfo);
668 loadTransmissionData(transmission,
workspace);
670 const std::string propertyName = (
name ==
"sample") ?
"TransmissionWorkspace" :
"TransmissionCanWorkspace";
672 "The transmission workspace");
const std::vector< double > * lambda
IPeaksWorkspace_sptr workspace
std::map< DeltaEMode::Type, std::string > index
#define DECLARE_NEXUS_LAZY_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_LAZY_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM mac...
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
@ Load
allowed here which will be passed to the algorithm
This class stores information about the sample used in particular run.
A property class for workspaces.
LoadNXcanSAS : Tries to load an NXcanSAS file type into a Workspace2D.
void loadMetadata(const H5::Group &group, const Mantid::API::MatrixWorkspace_sptr &workspace, const InstrumentNameInfo &instrumentInfo, const std::optional< API::Sample > &sample, bool hasPolarizedData=false) const
Loads metadata from H5 File into a Workspace: sample logs, instrument and sample information.
void loadData(const H5::Group &dataGroup, const Mantid::API::MatrixWorkspace_sptr &workspace, const std::pair< size_t, size_t > &spinIndexPair) const
General data loader, uses m_dataDims.
std::unique_ptr< NXcanSAS::DataDimensions > m_dataDims
void init() override
Initialisation code.
LoadNXcanSAS()
Constructor.
const std::string name() const override
function to return a name of the algorithm, must be overridden in all algorithms
void loadTransmission(const H5::Group &entry, const std::string &name, const InstrumentNameInfo &instrumentInfo)
Loads the transmission runs.
Mantid::API::WorkspaceGroup_sptr transferFileDataIntoWorkspace(const H5::Group &group, const DataSpaceInformation &dataInfo, const InstrumentNameInfo &instrumentInfo)
Prepares data to be loaded based on type and dimensionality.
std::unique_ptr< API::Progress > m_progress
std::vector< SpinState > prepareDataDimensions(const H5::Group &group, const DataSpaceInformation &dataInfo)
Prepares the DataDimensions struct that contains the shapes of the dataset objects,...
int confidence(Nexus::NexusDescriptorLazy &descriptor) const override
Returns a confidence value that this algorithm can load a file.
void exec() override
Execution code.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
The Logger class is in charge of the publishing messages from the framework through various channels.
void information(const std::string &msg)
Logs at information level.
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
std::string getStrData(std::string const &address)
Get string data from a dataset at address.
std::string const & extension() const noexcept
Access the file extension.
std::map< std::string, std::string > const & getAllEntries() const noexcept
Returns a const reference of the internal map holding all entries in the Nexus HDF5 file.
std::shared_ptr< WorkspaceGroup > WorkspaceGroup_sptr
shared pointer to Mantid::API::WorkspaceGroup
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< T > createWorkspace(InitArgs... args)
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
const std::string sasInstrumentApertureGapWidth
const std::string sasDataIdev
const std::string sasProcessGroupName
const std::string sasDataQx
const std::string sasEntryRunInLogs
const std::string sasEntryRun
const std::string sasTransmissionSpectrumNameCanAttrValue
const std::string nxEntryClassAttr
const std::string sasInstrumentApertureGapHeight
const std::string sasProcessTermUserFile
const std::string sasDataGroupName
const std::string sasTransmissionSpectrumTdev
const std::string sasTransmissionSpectrumT
const std::string sasDataQy
DataSpaceInformation getDataSpaceInfo(const H5::DataSet &dataSet)
const std::string sasEntryClassAttr
const std::string sasDataQdev
const std::string sasSampleMagneticField
const std::string sasDataPout
const std::string sasSampleEMFieldDirectionAzimuthal
const std::string sasTransmissionSpectrumLambda
const std::string sasDataQ
const std::string sasDataI
const std::string sasInstrumentSampleThickness
const std::string sasEntryDefinition
const std::string sasUnitAttr
const std::string sasTransmissionSpectrumNameSampleAttrValue
const std::string sasInstrumentApertureShape
const std::string sasProcessUserFileInLogs
const std::string sasInstrumentApertureGroupName
const std::string sasEntryDefinitionFormat
const std::string sasSampleEMFieldDirectionRotation
const std::string sasProcessBatchFileInLogs
const std::string sasTransmissionSpectrumGroupName
const std::string sasDataPin
const std::string sasProcessTermBatchFile
const std::string sasEntryTitle
const std::string sasInstrumentSampleGroupAttr
const std::string sasSampleEMFieldDirectionPolar
const std::string sasInstrumentGroupName
MANTID_NEXUS_DLL void readStringAttribute(const H5::H5Object &object, const std::string &attributeName, std::string &output)
MANTID_NEXUS_DLL std::string readString(H5::H5File &file, const std::string &address)
MANTID_NEXUS_DLL H5::FileAccPropList defaultFileAcc()
Default file access is H5F_CLOSE_STRONG.
void readArray1DCoerce(const H5::Group &group, const std::string &name, std::vector< NumT > &output)
MANTID_NEXUS_DLL DataType getType< double >()
Header for a base Nexus::Exception.
std::string to_string(const wide_integer< Bits, Signed > &n)
std::string instrumentName
std::pair< size_t, size_t > spinIndexPair
@ Input
An input workspace.
@ Output
An output workspace.