23#include "MantidNexus/NexusFile.h"
33const std::string NX_SPIN_LOG =
"spin_state_NXcanSAS";
34const std::map<std::string, int> SAMPLE_GEOMETRIES = {
35 {
"cylinder", 1}, {
"flat plate", 2}, {
"flatplate", 2}, {
"disc", 3}};
37std::string getNameOfEntry(
const H5::H5File &root) {
38 auto numberOfObjects = root.getNumObjs();
39 if (numberOfObjects != 1) {
40 throw std::invalid_argument(
"LoadNXcanSAS: Trying to load multiperiod "
41 "data. This is currently not supported.");
43 auto objectType = root.getObjTypeByIdx(0);
44 if (objectType != H5G_GROUP) {
45 throw std::invalid_argument(
"LoadNXcanSAS: The object below the root is not a H5::Group.");
48 return root.getObjnameByIdx(0);
55bool findDefinition(Mantid::Nexus::File &file) {
56 bool foundDefinition =
false;
57 const auto entries = file.getEntries();
58 for (
const auto &[sasEntry, nxEntry] : entries) {
60 file.openGroup(sasEntry, nxEntry);
62 const auto definitionFromFile = file.getStrData();
64 foundDefinition =
true;
71 return foundDefinition;
75 const auto &[dimSpectrumAxis, dimBin, _] = dimInfo;
78 "Workspace2D", dimSpectrumAxis , asHistogram ? dimBin + 1 : dimBin , dimBin );
81std::vector<double> getNumDataSetIfExists(
const H5::Group &
group,
const std::string &datasetName) {
82 if (
group.nameExists(datasetName)) {
83 return H5Util::readArray1DCoerce<double>(
group, datasetName);
88std::string getStrDataSetIfExists(
const H5::Group &
group,
const std::string &datasetName) {
89 if (
group.nameExists(datasetName)) {
99 const std::string &logUnits =
"") {
101 auto property = std::make_unique<PropertyWithValue<T>>(logName, logValue);
102 if (!logUnits.empty()) {
103 property->setUnits(logUnits);
105 run.addProperty(std::move(property));
109 const auto addLogFromGroup = [
workspace](
const H5::Group &
group,
const std::string &sasTerm,
110 const std::string &propertyName) {
111 const auto propValue = getStrDataSetIfExists(
group, sasTerm);
112 if (!propValue.empty()) {
113 addLogToWs(
workspace, propertyName, propValue);
135bool checkPolarization(
const H5::Group &
group) {
139 throw std::invalid_argument(
"Polarized data requires to have Pin and Pout axes");
149std::pair<std::vector<int>, std::vector<int>> loadSpinVectors(
const H5::Group &
group) {
151 std::vector<int> pIn;
152 std::vector<int> pOut;
153 if (checkPolarization(
group)) {
157 return std::make_pair(pIn, pOut);
170std::vector<SpinState> prepareSpinIndexes(
const std::vector<int> &pIn,
const std::vector<int> &pOut) {
172 auto spinToString = [](
const int spinIndex) {
176 std::vector<SpinState> spinIndexes;
177 for (
size_t i = 0; i < pIn.size(); i++) {
178 for (
size_t j = 0; j < pOut.size(); j++) {
180 state.
strSpinState = spinToString(pIn.at(i)) + spinToString(pOut.at(j));
182 spinIndexes.push_back(std::move(state));
196 for (
const auto &log : logNames) {
197 auto logValue = getNumDataSetIfExists(
group, log);
198 if (!logValue.empty()) {
199 std::string logUnits;
201 addLogToWs(
workspace, log, logValue.front(), logUnits);
207std::optional<H5::Group> getGroupIfExists(
const H5::Group &
group,
const std::string &groupName) {
208 if (
group.nameExists(groupName)) {
209 return group.openGroup(groupName);
219std::optional<Sample> loadSample(
const H5::Group &
group) {
222 auto apertureGroup = std::optional<H5::Group>();
223 if (instrumentGroup.has_value()) {
228 if (!apertureGroup.has_value() && !sampleGroup.has_value()) {
232 if (apertureGroup.has_value()) {
235 sample.setHeight(
height.front());
238 if (!width.empty()) {
239 sample.setWidth(width.front());
242 boost::to_lower(geometry);
243 if (!geometry.empty()) {
244 SAMPLE_GEOMETRIES.contains(geometry) ? sample.setGeometryFlag(SAMPLE_GEOMETRIES.at(geometry))
245 : sample.setGeometryFlag(0);
250 if (sampleGroup.has_value()) {
252 if (!thickness.empty()) {
253 sample.setThickness(thickness.front());
267 const auto instAlg = Mantid::API::AlgorithmManager::Instance().createUnmanaged(
"LoadInstrument");
268 instAlg->initialize();
269 instAlg->setChild(
true);
270 instAlg->setProperty(
"Workspace",
workspace);
271 instAlg->setProperty(
"InstrumentName", instrumentInfo.
instrumentName);
272 if (!instrumentInfo.
idf.empty()) {
273 instAlg->setProperty(
"Filename", instrumentInfo.
idf);
275 instAlg->setProperty(
"RewriteSpectraMap",
"False");
277 }
catch (std::invalid_argument &) {
279 }
catch (std::runtime_error &) {
280 g_log.
information(
"Unable to successfully run LoadInstrument Child Algorithm.");
305std::vector<hsize_t> updateOffset(
const int axesIndex,
const std::pair<size_t, size_t> &spinIndexPair,
306 std::vector<hsize_t> &slabShape) {
307 const bool isXAxis = axesIndex == WorkspaceDataAxes::X || axesIndex == WorkspaceDataAxes::XErr;
308 const auto &[indexPin, indexPout] = spinIndexPair;
309 auto position = std::vector<hsize_t>(slabShape.size(), 0);
310 if (slabShape.size() > 2) {
313 slabShape = std::vector<hsize_t>(slabShape.cbegin() + 2, slabShape.cend());
323std::string getStrAttribute(
const H5::DataSet &dataSet,
const std::string &attrName) {
324 std::string attrValue;
333struct WorkspaceDataInserter {
336 void insertData(
const size_t index,
const std::vector<double> &data)
const {
351 throw std::runtime_error(
"Provided axis is not compatible with workspace.");
355 void setUnits(
const H5::DataSet &dataSet)
const {
358 }
else if (axisType ==
X) {
359 workspace->getAxis(0)->setUnit(
"MomentumTransfer");
363 void setAxisType(
const int type) { axisType = type; }
377void readDataIntoWorkspace(
const H5::DataSet &dataSet,
const WorkspaceDataInserter &inserter,
378 const std::vector<hsize_t> &slabShape,
const hsize_t nPoints,
const hsize_t nHistograms,
379 std::vector<hsize_t> &offset) {
382 const std::array<hsize_t, 1> memSpaceDimension = {nPoints};
383 const H5::DataSpace memSpace(1, memSpaceDimension.data());
384 const bool isPolarizedDataSet = slabShape.size() > 2;
385 const auto histogramIndex = isPolarizedDataSet ? slabShape.size() - 2 : 0;
387 const auto fileSpace = dataSet.getSpace();
388 auto data = std::vector<double>(nPoints, 0);
391 fileSpace.selectHyperslab(H5S_SELECT_SET, slabShape.data(), offset.data());
393 inserter.insertData(
index, data);
394 offset.at(histogramIndex)++;
408 const std::array<hsize_t, 2> slabShape = {nHistograms, 1};
409 const std::array<hsize_t, 2>
position = {0, 0};
412 const std::array<hsize_t, 1> memSpaceDimensions = {nHistograms};
413 const H5::DataSpace memSpace(1, memSpaceDimensions.data());
416 const auto fileSpace = dataSet.getSpace();
417 fileSpace.selectHyperslab(H5S_SELECT_SET, slabShape.data(),
position.data());
420 auto data = std::vector<double>(nHistograms);
423 auto newAxis = std::make_unique<Mantid::API::NumericAxis>(data);
424 workspace->replaceAxis(1, std::move(newAxis));
427 workspace->getAxis(1)->setUnit(
"MomentumTransfer");
431bool fileHasTransmissionEntry(
const H5::Group &entry,
const std::string &
name) {
433 if (!hasTransmission) {
436 return hasTransmission;
456 std::vector<double>
lambda;
463 const std::string objectName{transmission.getObjName()};
464 throw std::runtime_error(
"Unexpected array size for lambda in transmission group '" + objectName +
468 workspace->getAxis(0)->setUnit(
"Wavelength");
469 workspace->setYUnitLabel(
"Transmission");
484 const std::string &extn = descriptor.
extension();
485 if (extn !=
".nxs" && extn !=
".h5") {
489 Mantid::Nexus::File file(descriptor.
filename());
492 if (findDefinition(file)) {
503 const std::vector<std::string> exts{
".nxs",
".h5"};
505 "The name of the NXcanSAS file to read, as a full or relative path.");
508 "The name of the workspace to be created as the output of "
509 "the algorithm. A workspace of this name will be created "
510 "and stored in the Analysis Data Service. For multiperiod "
511 "files, one workspace may be generated for each period. "
512 "Currently only one workspace can be saved at a time so "
513 "multiperiod Mantid files are not generated.");
516 "Load the transmission related data from the file if it is present "
517 "(optional, default False).");
522 const bool isLoadTransmissionChecked =
getProperty(
"LoadTransmission");
525 const auto entry = file.openGroup(getNameOfEntry(file));
530 const size_t stepsPerSpinState = dataInfo.spinStates * 5;
531 const auto numberOfSteps = isLoadTransmissionChecked ? stepsPerSpinState + 1 : stepsPerSpinState;
532 m_progress = std::make_unique<API::Progress>(
this, 0.1, 1.0, numberOfSteps);
539 if (isLoadTransmissionChecked) {
547 const auto wsOut = dataInfo.spinStates == 1 ? wsGroup->getItem(0) : std::dynamic_pointer_cast<Workspace>(wsGroup);
562 const bool hasPolarizedData)
const {
576 if (sample.has_value()) {
577 workspace->mutableSample() = sample.value();
581 loadInstrument(
workspace, instrumentInfo);
591 const std::pair<size_t, size_t> &spinIndexPair)
const {
594 WorkspaceDataInserter dataInserter(
workspace);
595 auto dataSets = std::map<std::string, int>{
602 const auto nPoints =
m_dataDims->getNumberOfPoints();
603 const auto nHisto =
m_dataDims->getNumberOfHistograms();
605 for (
const auto &[setName, axesIndex] : dataSets) {
606 auto dataSet = dataGroup.openDataSet(setName);
607 auto offset = updateOffset(axesIndex, spinIndexPair, slabShape);
608 dataInserter.setAxisType(axesIndex);
609 readDataIntoWorkspace(dataSet, dataInserter, slabShape, nPoints, nHisto, offset);
610 dataInserter.setUnits(dataSet);
627 const auto &[pIn, pOut] = loadSpinVectors(
group);
628 const auto spinPairs =
629 !pIn.empty() && !pOut.empty() ? std::optional(std::make_pair(pIn.size(), pOut.size())) : std::nullopt;
631 const auto spinStates = spinPairs.has_value() ? prepareSpinIndexes(pIn, pOut) :
652 auto dataOut = std::make_shared<WorkspaceGroup>();
653 const auto sample = loadSample(
group);
655 for (
const auto &[spinStr, spinIndexPair] : states) {
659 loadData(dataGroup, ws, spinIndexPair);
661 if (!spinStr.empty()) {
662 addLogToWs(ws, NX_SPIN_LOG, spinStr);
663 ws->setTitle(wsName +
"_" + spinStr);
665 dataOut->addWorkspace(ws);
672 if (!fileHasTransmissionEntry(entry,
name)) {
684 loadInstrument(
workspace, instrumentInfo);
686 loadTransmissionData(transmission,
workspace);
688 const std::string propertyName = (
name ==
"sample") ?
"TransmissionWorkspace" :
"TransmissionCanWorkspace";
690 "The transmission workspace");
const std::vector< double > * lambda
IPeaksWorkspace_sptr workspace
std::map< DeltaEMode::Type, std::string > index
#define DECLARE_NEXUS_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro wh...
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.
int confidence(Nexus::NexusDescriptor &descriptor) const override
Returns a confidence value that this algorithm can load a file.
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,...
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...
const std::string & extension() const
Access the file extension.
const std::string & filename() const noexcept
Returns a copy of the current file name.
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.