From 606fe6692b5bdc23f3ef2b1ff1db314a2298e755 Mon Sep 17 00:00:00 2001 From: Ross Whitfield Date: Wed, 12 Feb 2025 11:10:17 +1100 Subject: [PATCH 1/7] Create LegacyNexusClasses --- Framework/Nexus/CMakeLists.txt | 6 +- .../inc/MantidNexus/LegacyNexusClasses.h | 939 ++++++++++++++++++ Framework/Nexus/src/LegacyNexusClasses.cpp | 634 ++++++++++++ 3 files changed, 1576 insertions(+), 3 deletions(-) create mode 100644 Framework/Nexus/inc/MantidNexus/LegacyNexusClasses.h create mode 100644 Framework/Nexus/src/LegacyNexusClasses.cpp diff --git a/Framework/Nexus/CMakeLists.txt b/Framework/Nexus/CMakeLists.txt index 5ff72fdbeb39..16cc36594032 100644 --- a/Framework/Nexus/CMakeLists.txt +++ b/Framework/Nexus/CMakeLists.txt @@ -1,7 +1,7 @@ -set(SRC_FILES src/H5Util.cpp src/MuonNexusReader.cpp src/NexusClasses.cpp) +set(SRC_FILES src/H5Util.cpp src/MuonNexusReader.cpp src/NexusClasses.cpp src/LegacyNexusClasses.cpp) set(INC_FILES inc/MantidNexus/H5Util.h inc/MantidNexus/MuonNexusReader.h inc/MantidNexus/NexusClasses.h - inc/MantidNexus/NexusIOHelper.h + inc/MantidNexus/LegacyNexusClasses.h inc/MantidNexus/NexusIOHelper.h ) set(TEST_FILES H5UtilTest.h NexusIOHelperTest.h) @@ -31,7 +31,7 @@ endif() # Add to the 'Framework' group in VS set_property(TARGET Nexus PROPERTY FOLDER "MantidFramework") -target_link_libraries(Nexus PUBLIC Mantid::API Mantid::DataObjects Mantid::Kernel Mantid::NexusCpp) +target_link_libraries(Nexus PUBLIC Mantid::API Mantid::DataObjects Mantid::Kernel Mantid::NexusCpp Mantid::LegacyNexus) # Add the unit tests directory add_subdirectory(test) diff --git a/Framework/Nexus/inc/MantidNexus/LegacyNexusClasses.h b/Framework/Nexus/inc/MantidNexus/LegacyNexusClasses.h new file mode 100644 index 000000000000..68fd19b12700 --- /dev/null +++ b/Framework/Nexus/inc/MantidNexus/LegacyNexusClasses.h @@ -0,0 +1,939 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2025 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#pragma once + +#include "MantidKernel/DateAndTimeHelpers.h" +#include "MantidKernel/TimeSeriesProperty.h" +#include "MantidLegacyNexus/NeXusFile_fwd.h" +#include "MantidLegacyNexus/napi.h" +#include "MantidNexus/DllConfig.h" + +#include +#include +#include +#include +#include + +namespace Mantid { +namespace LegacyNexus { + +/** C++ implementation of NeXus classes. + +@author Roman Tolchenov, Tessella plc +@date 28/05/2009 +*/ + +/** Structure for keeping information about a Nexus data set, + * such as the dimensions and the type + */ +struct NXInfo { + NXInfo() : nxname(), rank(0), dims(), type(NXnumtype::BAD), stat(NXstatus::NX_ERROR) {} + std::string nxname; ///< name of the object + int rank; ///< number of dimensions of the data + int dims[4]; ///< sizes along each dimension + NXnumtype type; ///< type of the data, e.g. NX_CHAR, NXnumtype::FLOAT32, see napi.h + NXstatus stat; ///< return status + operator bool() { return stat == NXstatus::NX_OK; } ///< returns success of an operation +}; + +/** Information about a Nexus class + */ +struct NXClassInfo { + NXClassInfo() : nxname(), nxclass(), datatype(NXnumtype::BAD), stat(NXstatus::NX_ERROR) {} + std::string nxname; ///< name of the object + std::string nxclass; ///< NX class of the object or "SDS" if a dataset + NXnumtype datatype; ///< NX data type if a dataset, e.g. NX_CHAR, NXnumtype::FLOAT32, see + /// napi.h + NXstatus stat; ///< return status + operator bool() { return stat == NXstatus::NX_OK; } ///< returns success of an operation +}; + +/** + * LoadNexusProcessed and SaveNexusProcessed need to share some attributes, put + * them at + * namespace level here + */ +/// Default block size for reading and writing processed files +const int g_processed_blocksize = 8; + +/** Nexus attributes. The type of each attribute is NX_CHAR + */ +class MANTID_NEXUS_DLL NXAttributes { +public: + int n() const { return int(m_values.size()); } ///< number of attributes + std::vector names() const; ///< Returns the list of attribute names + std::vector values() const; ///< Returns the list of attribute values + std::string operator()(const std::string &name) const; ///< returns the value of attribute with name name + void set(const std::string &name, + const std::string &value); ///< set the attribute's value + void set(const std::string &name, + double value); ///< set the attribute's value as a double +private: + std::map m_values; ///< the list of attributes +}; + +/// Forward declaration +class NXClass; + +/** The base abstract class for NeXus classes and data sets. + * NX classes and data sets are defined at www.nexusformat.org + */ +class MANTID_NEXUS_DLL NXObject { + friend class NXDataSet; ///< a friend class declaration + friend class NXClass; ///< a friend class declaration + friend class NXRoot; ///< a friend class declaration +public: + // Constructor + NXObject(const NXhandle fileID, const NXClass *parent, const std::string &name); + virtual ~NXObject() = default; + /// Return the NX class name for a class (HDF group) or "SDS" for a data set; + virtual std::string NX_class() const = 0; + // True if complies with our understanding of the www.nexusformat.org + // definition. + // virtual bool isStandard()const = 0; + /// Returns the absolute path to the object + std::string const &path() const { return m_path; } + /// Returns the name of the object + std::string name() const; + /// Attributes + NXAttributes attributes; + /// Nexus file id + NXhandle m_fileID; + +protected: + std::string m_path; ///< Keeps the absolute path to the object + bool m_open; ///< Set to true if the object has been open +private: + NXObject() : m_fileID(), m_open(false) {} ///< Private default constructor + void getAttributes(); +}; + +/** Abstract base class for a Nexus data set. A typical use include: + *
    + *
  • Creating a dataset object using either the concrete type + * constructor or specialized methods of NXClass'es
  • Opening the dataset + * with open() method. Specialized NXClass creation methods call open() + * internally (so no need to call it again).
  • Loading the data using + * load(...) method. The data can be loaded either in full or by chunks of + * smaller rank (dimension)
  • + *
+ * There is no need to free the memory allocated by the NXDataSet as it is done + * at the destruction. + */ +class MANTID_NEXUS_DLL NXDataSet : public NXObject { +public: + // Constructor + NXDataSet(const NXClass &parent, const std::string &name); + /// NX class name. Returns "SDS" + std::string NX_class() const override { return "SDS"; } + /// Opens the data set. Does not read in any data. Call load(...) to load the + /// data + void open(); + /// Opens datasets faster but the parent group must be already open + void openLocal(); + /// Returns the rank (number of dimensions) of the data. The maximum is 4 + int rank() const { return m_info.rank; } + /// Returns the number of elements along i-th dimension + int dims(int i) const { return i < 4 ? m_info.dims[i] : 0; } + /// Returns the number of elements along the first dimension + int dim0() const; + /// Returns the number of elements along the second dimension + int dim1() const; + /// Returns the number of elements along the third dimension + int dim2() const; + /// Returns the number of elements along the fourth dimension + int dim3() const; + /// Returns the name of the data set + std::string name() const { return m_info.nxname; } // cppcheck-suppress returnByReference + /// Returns the Nexus type of the data. The types are defied in napi.h + NXnumtype type() const { return m_info.type; } + /** Load the data from the file. Calling this method with all default + * arguments + * makes it to read in all the data. + * @param blocksize :: The size of the block of data that should be read. + * Note that this is only used for rank 2 and 3 datasets currently + * @param i :: Calling load with non-negative i reads in a chunk of + * dimension rank()-1 and i is the index of the chunk. The rank of the data + * must be >= 1 + * @param j :: Non-negative value makes it read a chunk of dimension + * rank()-2. i and j are its indices. + * The rank of the data must be >= 2 + * @param k :: Non-negative value makes it read a chunk of dimension + * rank()-3. i,j and k are its indices. + * The rank of the data must be >= 3 + * @param l :: Non-negative value makes it read a chunk of dimension + * rank()-4. i,j,k and l are its indices. + * The rank of the data must be 4 + */ + virtual void load(const int blocksize = 1, int i = -1, int j = -1, int k = -1, int l = -1) { + // Avoid compiler warnings + (void)blocksize; + (void)i; + (void)j; + (void)k; + (void)l; + }; + +protected: + void getData(void *data); + void getSlab(void *data, int start[], int size[]); + +private: + NXInfo m_info; ///< Holds the data info +}; + +template +using container_T = std::conditional_t{}, boost::container::vector, std::vector>; + +/** Templated class implementation of NXDataSet. After loading the data it can + * be accessed via operators () and []. + */ +template class NXDataSetTyped : public NXDataSet { + +public: + /** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the dataset. + * @param name :: The name of the dataset relative to its parent + */ + NXDataSetTyped(const NXClass &parent, const std::string &name) : NXDataSet(parent, name), m_n(0) {} + /** Returns a pointer to the internal data buffer. + * @throw runtime_error exception if the data have not been loaded / + * initialized. + * @return a pointer to the array of items + */ + const T *operator()() const { + if (m_data.empty()) + throw std::runtime_error("Attempt to read uninitialized data from " + path()); + return m_data.data(); + } + + T *operator()() { + if (m_data.empty()) + throw std::runtime_error("Attempt to read uninitialized data from " + path()); + return m_data.data(); + } + + /** Returns the i-th value in the internal buffer + * @param i :: The linear index of the data element + * @throw runtime_error if the data have not been loaded / initialized. + * @throw range_error if the index is greater than the buffer size. + * @return A reference to the value + */ + const T &operator[](int i) const { + if (m_data.empty()) + throw std::runtime_error("Attempt to read uninitialized data from " + path()); + if (i < 0 || i >= m_n) + rangeError(); + return m_data[i]; + } + + T &operator[](int i) { return const_cast(static_cast(*this)[i]); } + /** Returns a value assuming the data is a two-dimensional array + * @param i :: The index along dim0() + * @param j :: The index along dim1() + * @throw runtime_error if the data have not been loaded / initialized. + * @throw range_error if the indeces point outside the buffer. + * @return A reference to the value + */ + const T &operator()(int i, int j) const { return this->operator[](i * dim1() + j); } + T &operator()(int i, int j) { return const_cast(static_cast(*this)(i, j)); } + /** Returns a value assuming the data is a tree-dimensional array + * @param i :: The index along dim0() + * @param j :: The index along dim1() + * @param k :: The index along dim2() + * @throw runtime_error if the data have not been loaded / initialized. + * @throw range_error if the indeces point outside the buffer. + * @return A reference to the value + */ + const T &operator()(int i, int j, int k) const { return this->operator[]((i * dim1() + j) * dim2() + k); } + T &operator()(int i, int j, int k) { return const_cast(static_cast(*this)(i, j, k)); } + + /// Returns a the internal buffer + container_T &vecBuffer() { return m_data; } + /// Returns the size of the data buffer + int size() const { return m_n; } + /** Implementation of the virtual NXDataSet::load(...) method. Internally the + * data are stored as a 1d array. + * If the data are loaded in chunks the newly read in data replace the old + * ones. The actual rank of the loaded + * data is equal or less than the rank of the dataset (returned by rank() + * method). + * @param blocksize :: The size of the block of data that should be read. + * Note that this is only used for rank 2 and 3 datasets currently + * @param i :: Calling load with non-negative i reads in a chunk of + * dimension rank()-1 and i is the index of the chunk. The rank of the data + * must be >= 1 + * @param j :: Non-negative value makes it read a chunk of dimension + * rank()-2. i and j are its indeces. + * The rank of the data must be >= 2 + * @param k :: Non-negative value makes it read a chunk of dimension + * rank()-3. i,j and k are its indeces. + * The rank of the data must be >= 3 + * @param l :: Non-negative value makes it read a chunk of dimension + * rank()-4. i,j,k and l are its indeces. + * The rank of the data must be 4 + */ + void load(const int blocksize = 1, int i = -1, int j = -1, int k = -1, int l = -1) override { + if (rank() > 4) { + throw std::runtime_error("Cannot load dataset of rank greater than 4"); + } + int n = 0; + int start[4]; + if (rank() == 4) { + if (i < 0) // load all data + { + n = dim0() * dim1() * dim2() * dim3(); + alloc(n); + getData(m_data.data()); + return; + } else if (j < 0) { + if (i >= dim0()) + rangeError(); + n = dim1() * dim2() * dim3(); + start[0] = i; + m_size[0] = 1; + start[1] = 0; + m_size[1] = dim1(); + start[2] = 0; + m_size[2] = dim2(); + start[3] = 0; + m_size[3] = dim2(); + } else if (k < 0) { + if (i >= dim0() || j >= dim1()) + rangeError(); + n = dim2() * dim3(); + start[0] = i; + m_size[0] = 1; + start[1] = j; + m_size[1] = 1; + start[2] = 0; + m_size[2] = dim2(); + start[3] = 0; + m_size[3] = dim2(); + } else if (l < 0) { + if (i >= dim0() || j >= dim1() || k >= dim2()) + rangeError(); + n = dim3(); + start[0] = i; + m_size[0] = 1; + start[1] = j; + m_size[1] = 1; + start[2] = k; + m_size[2] = 1; + start[3] = 0; + m_size[3] = dim2(); + } else { + if (i >= dim0() || j >= dim1() || k >= dim2() || l >= dim3()) + rangeError(); + n = dim3(); + start[0] = i; + m_size[0] = 1; + start[1] = j; + m_size[1] = 1; + start[2] = k; + m_size[2] = 1; + start[3] = l; + m_size[3] = 1; + } + } else if (rank() == 3) { + if (i < 0) { + n = dim0() * dim1() * dim2(); + alloc(n); + getData(m_data.data()); + return; + } else if (j < 0) { + if (i >= dim0()) + rangeError(); + n = dim1() * dim2(); + start[0] = i; + m_size[0] = 1; + start[1] = 0; + m_size[1] = dim1(); + start[2] = 0; + m_size[2] = dim2(); + } else if (k < 0) { + if (i >= dim0() || j >= dim1()) + rangeError(); + int m = blocksize; + if (j + m > dim1()) + m = dim1() - j; + n = dim2() * m; + start[0] = i; + m_size[0] = 1; + start[1] = j; + m_size[1] = m; + start[2] = 0; + m_size[2] = dim2(); + } else { + if (i >= dim0() || j >= dim1() || k >= dim2()) + rangeError(); + n = 1; + start[0] = i; + m_size[0] = 1; + start[1] = j; + m_size[1] = 1; + start[2] = k; + m_size[2] = 1; + } + } else if (rank() == 2) { + if (i < 0) { + n = dim0() * dim1(); + alloc(n); + getData(m_data.data()); + return; + } else if (j < 0) { + if (i >= dim0()) + rangeError(); + int m = blocksize; + if (i + m > dim0()) + m = dim0() - i; + n = dim1() * m; + start[0] = i; + m_size[0] = m; + start[1] = 0; + m_size[1] = dim1(); + } else { + if (i >= dim0() || j >= dim1()) + rangeError(); + n = 1; + start[0] = i; + m_size[0] = 1; + start[1] = j; + m_size[1] = 1; + } + } else if (rank() == 1) { + if (i < 0) { + n = dim0(); + alloc(n); + getData(m_data.data()); + return; + } else { + if (i >= dim0()) + rangeError(); + n = 1 * blocksize; + start[0] = i; + m_size[0] = blocksize; + } + } + alloc(n); + getSlab(m_data.data(), start, m_size); + } + +private: + /** Allocates memory for the data buffer + * @param n :: The number of elements to allocate. + */ + void alloc(int n) { + if (n <= 0) { + throw std::runtime_error("Attempt to load from an empty dataset " + path()); + } + try { + if (m_n != n) { + m_data.resize(n); + m_n = n; + } + } catch (...) { + std::ostringstream ostr; + ostr << "Cannot allocate " << n * sizeof(T) << " bytes of memory to load the data"; + throw std::runtime_error(ostr.str()); + } + } + /// A shortcut to "throw std::range_error("Nexus dataset range error");" + void rangeError() const { throw std::range_error("Nexus dataset range error"); } + // We cannot use an STL vector due to the dreaded std::vector + container_T m_data; ///< The data buffer + int m_size[4]; ///< The sizes of the loaded data + int m_n; ///< The buffer size +}; + +/// The integer dataset type +using NXInt = NXDataSetTyped; +/// The float dataset type +using NXFloat = NXDataSetTyped; +/// The double dataset type +using NXDouble = NXDataSetTyped; +/// The char dataset type +using NXChar = NXDataSetTyped; +/// The size_t dataset type +using NXSize = NXDataSetTyped; +/// The size_t dataset type +using NXUInt = NXDataSetTyped; + +//-------------------- classes --------------------------// + +/** The base class for a Nexus class (group). A Nexus class can contain + * datasets and other Nexus classes. + * The NeXus file format (www.nexusformat.org) specifies the content of the + * Nexus classes. + * Derived classes have specialized methods for creating classes and datasets + * specific for the particular Nexus class. + * NXClass is a conctrete C++ class so arbitrary, non-standard Nexus classes + * (groups) can be created and loaded from + * NeXus files. + */ +class MANTID_NEXUS_DLL NXClass : public NXObject { + friend class NXRoot; + +public: + /** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the NXClass. + * @param name :: The name of the NXClass relative to its parent + */ + NXClass(const NXClass &parent, const std::string &name); + /// The NX class identifier + std::string NX_class() const override { return "NXClass"; } + /** Returns the class information about the next entry (class or dataset) in + * this class. + */ + NXClassInfo getNextEntry(); + /// Creates a new object in the NeXus file at path path. + // virtual void make(const std::string& path) = 0; + /// Resets the current position for getNextEntry() to the beginning + void reset(); + /** + * Check if a path exists relative to the current class path + * @param path :: A string representing the path to test + * @return True if it is valid + */ + bool isValid(const std::string &path) const; + /** Templated method for creating derived NX classes. It also opens the + * created class. + * @param name :: The name of the class + * @tparam NX Concrete Nexus class + * @return The new object + */ + template NX openNXClass(const std::string &name) const { + NX nxc(*this, name); + nxc.open(); + return nxc; + } + + /** Creates and opens an arbitrary (non-standard) class (group). + * @param name :: The name of the class. + * @return The opened NXClass + */ + NXClass openNXGroup(const std::string &name) const { return openNXClass(name); } + + /** Templated method for creating datasets. It also opens the created set. + * @param name :: The name of the dataset + * @tparam T The type of the data (int, double, ...). + * @return The new object + */ + template NXDataSetTyped openNXDataSet(const std::string &name) const { + NXDataSetTyped data(*this, name); + data.open(); + return data; + } + + /** Creates and opens an integer dataset + * @param name :: The name of the dataset + * @return The int + */ + NXInt openNXInt(const std::string &name) const { return openNXDataSet(name); } + /** Creates and opens a float dataset + * @param name :: The name of the dataset + * @return The float + */ + NXFloat openNXFloat(const std::string &name) const { return openNXDataSet(name); } + /** Creates and opens a double dataset + * @param name :: The name of the dataset + * @return The double + */ + NXDouble openNXDouble(const std::string &name) const { return openNXDataSet(name); } + /** Creates and opens a char dataset + * @param name :: The name of the dataset + * @return The char + */ + NXChar openNXChar(const std::string &name) const { return openNXDataSet(name); } + /** Creates and opens a size_t dataset + * @param name :: The name of the dataset + * @return The size_t + */ + NXSize openNXSize(const std::string &name) const { return openNXDataSet(name); } + /** Returns a string + * @param name :: The name of the NXChar dataset + * @return The string + */ + std::string getString(const std::string &name) const; + /** Returns a double + * @param name :: The name of the NXDouble dataset + * @return The double + */ + double getDouble(const std::string &name) const; + /** Returns a float + * @param name :: The name of the NXFloat dataset + * @return The float + */ + float getFloat(const std::string &name) const; + /** Returns a int + * @param name :: The name of the NXInt dataset + * @return The int + */ + int getInt(const std::string &name) const; + + /// Returns a list of all classes (or groups) in this NXClass + std::vector &groups() const { return *m_groups; } + /// Returns whether an individual group (or group) is present + bool containsGroup(const std::string &query) const; + /// Returns a list of all datasets in this NXClass + std::vector &datasets() const { return *m_datasets; } + /** Returns NXInfo for a dataset + * @param name :: The name of the dataset + * @return NXInfo::stat is set to NXstatus::NX_ERROR if the dataset does not exist + */ + NXInfo getDataSetInfo(const std::string &name) const; + /// Returns whether an individual dataset is present + bool containsDataSet(const std::string &query) const; + /// Close this class + void close(); + /// Opens this NXClass using NXopengrouppath. Can be slow (or is slow) + void open(); + /// Opens this NXClass using NXopengroup. It is fast, but the parent of this + /// class must be open at + /// the time of calling. openNXClass uses open() (the slow one). To open calss + /// using openLocal() do: + /// NXTheClass class(parent,name); + /// class.openLocal(); + /// // work with class + /// class.close(); + bool openLocal(const std::string &nxclass = ""); + +protected: + std::shared_ptr> m_groups; ///< Holds info about the child NXClasses + std::shared_ptr> m_datasets; ///< Holds info about the datasets in this NXClass + void readAllInfo(); ///< Fills in m_groups and m_datasets. + void clear(); ///< Deletes content of m_groups and m_datasets +private: + /// Pricate constructor. + NXClass() : NXObject() { clear(); } +}; + +//------------------- auxiliary classes ----------------------------// + +/** Implements NXlog Nexus class. + */ +class MANTID_NEXUS_DLL NXLog : public NXClass { +public: + /** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the NXClass. + * @param name :: The name of the NXClass relative to its parent + */ + NXLog(const NXClass &parent, const std::string &name) : NXClass(parent, name) {} + /// Nexus class id + std::string NX_class() const override { return "NXlog"; } + /// Creates a property wrapper around the log + Kernel::Property *createProperty(); + /// Creates a TimeSeriesProperty and returns a pointer to it + Kernel::Property *createTimeSeries(const std::string &start_time = "", const std::string &new_name = ""); + +private: + /// Creates a single value property of the log + Kernel::Property *createSingleValueProperty(); + /// Parse a time series + template + Kernel::Property *parseTimeSeries(const std::string &logName, const TYPE ×, const std::string &time0 = "") { + std::string start_time = (!time0.empty()) ? time0 : times.attributes("start"); + if (start_time.empty()) { + start_time = "2000-01-01T00:00:00"; + } + auto start_t = Kernel::DateAndTimeHelpers::createFromSanitizedISO8601(start_time); + NXInfo vinfo = getDataSetInfo("value"); + if (!vinfo) + return nullptr; + + if (vinfo.dims[0] != times.dim0()) + return nullptr; + + if (vinfo.type == NXnumtype::CHAR) { + auto logv = new Kernel::TimeSeriesProperty(logName); + NXChar value(*this, "value"); + value.openLocal(); + value.load(); + for (int i = 0; i < value.dim0(); i++) { + auto t = start_t + boost::posix_time::seconds(int(times[i])); + for (int j = 0; j < value.dim1(); j++) { + char *c = &value(i, j); + if (!isprint(*c)) + *c = ' '; + } + logv->addValue(t, std::string(value() + i * value.dim1(), value.dim1())); + } + return logv; + } else if (vinfo.type == NXnumtype::FLOAT64) { + if (logName.find("running") != std::string::npos || logName.find("period ") != std::string::npos) { + auto logv = new Kernel::TimeSeriesProperty(logName); + NXDouble value(*this, "value"); + value.openLocal(); + value.load(); + for (int i = 0; i < value.dim0(); i++) { + auto t = start_t + boost::posix_time::seconds(int(times[i])); + logv->addValue(t, (value[i] == 0 ? false : true)); + } + return logv; + } + NXDouble value(*this, "value"); + return loadValues(logName, value, start_t, times); + } else if (vinfo.type == NXnumtype::FLOAT32) { + NXFloat value(*this, "value"); + return loadValues(logName, value, start_t, times); + } else if (vinfo.type == NXnumtype::INT32) { + NXInt value(*this, "value"); + return loadValues(logName, value, start_t, times); + } + return nullptr; + } + + /// Loads the values in the log into the workspace + ///@param logName :: the name of the log + ///@param value :: the value + ///@param start_t :: the start time + ///@param times :: the array of time offsets + ///@returns a property pointer + template + Kernel::Property *loadValues(const std::string &logName, NX_TYPE &value, Types::Core::DateAndTime start_t, + const TIME_TYPE ×) { + value.openLocal(); + auto logv = new Kernel::TimeSeriesProperty(logName); + value.load(); + for (int i = 0; i < value.dim0(); i++) { + if (i == 0 || value[i] != value[i - 1] || times[i] != times[i - 1]) { + auto t = start_t + boost::posix_time::seconds(int(times[i])); + logv->addValue(t, value[i]); + } + } + return logv; + } +}; + +/** Implements NXnote Nexus class. + */ +class MANTID_NEXUS_DLL NXNote : public NXClass { +public: + /** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the NXClass. + * @param name :: The name of the NXClass relative to its parent + */ + NXNote(const NXClass &parent, const std::string &name) + : NXClass(parent, name), m_author_ok(), m_data_ok(), m_description_ok() {} + /// Nexus class id + std::string NX_class() const override { return "NXnote"; } + /// Returns the note's author + std::string author(); + /// Returns the note's content + std::vector &data(); + /// Returns the description string + std::string description(); + +protected: + std::string m_author; ///< author + std::vector m_data; ///< content + std::string m_description; ///< description + bool m_author_ok; ///< author loaded indicator + bool m_data_ok; ///< data loaded indicator + bool m_description_ok; ///< description loaded indicator +}; + +/** Implements NXnote Nexus class with binary data. + */ +class MANTID_NEXUS_DLL NXBinary : public NXNote { +public: + /** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the NXClass. + * @param name :: The name of the NXClass relative to its parent + */ + NXBinary(const NXClass &parent, const std::string &name) : NXNote(parent, name) {} + /// Return the binary data associated with the note + std::vector &binary(); + +private: + std::vector m_binary; ///< content +}; + +//-------------------- main classes -------------------------------// + +/** Main class is the one that can contain auxiliary classes. + */ +class MANTID_NEXUS_DLL NXMainClass : public NXClass { +public: + /** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the NXClass. + * @param name :: The name of the NXClass relative to its parent + */ + NXMainClass(const NXClass &parent, const std::string &name) : NXClass(parent, name) {} + /** Opens a NXLog class + * @param name :: The name of the NXLog + * @return The log + */ + NXLog openNXLog(const std::string &name) { return openNXClass(name); } + /** Opens a NXNote class + * @param name :: The name of the NXNote + * @return The note + */ + NXNote openNXNote(const std::string &name) { return openNXClass(name); } +}; + +/** Implements NXdata Nexus class. + */ +class MANTID_NEXUS_DLL NXData : public NXMainClass { +public: + /** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the NXClass. + * @param name :: The name of the NXClass relative to its parent + */ + NXData(const NXClass &parent, const std::string &name); + /// Nexus class id + std::string NX_class() const override { return "NXdata"; } + /** Opens the dataset within this NXData with signal=1 attribute. + */ + template NXDataSetTyped openData() { + for (std::vector::const_iterator it = datasets().begin(); it != datasets().end(); ++it) { + NXDataSet dset(*this, it->nxname); + dset.open(); + // std::cerr << "NXData signal of " << it->nxname << " = " << + // dset.attributes("signal") << "\n"; + if (dset.attributes("signal") == "1") { + return openNXDataSet(it->nxname); + } + } + // You failed to find the signal. + // So try to just open the "data" entry directly + return openNXDataSet("data"); + // throw std::runtime_error("NXData does not seem to contain the data"); + // return NXDataSetTyped(*this,""); + } + /// Opens data of double type + NXDouble openDoubleData() { return openData(); } + /// Opens data of float type + NXFloat openFloatData() { return openData(); } + /// Opens data of int type + NXInt openIntData() { return openData(); } + /// Opens data of size type + NXSize openSizeData() { return openData(); } + /// Opens data of unsigned int type + NXUInt openUIntData() { return openData(); } +}; + +/** Implements NXdetector Nexus class. + */ +class MANTID_NEXUS_DLL NXDetector : public NXMainClass { +public: + /** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the NXClass. + * @param name :: The name of the NXClass relative to its parent + */ + NXDetector(const NXClass &parent, const std::string &name) : NXMainClass(parent, name) {} + /// Nexus class id + std::string NX_class() const override { return "NXdetector"; } + /// Opens the dataset containing pixel distances + NXFloat openDistance() { return openNXFloat("distance"); } + /// Opens the dataset containing pixel azimuthal angles + NXFloat openAzimuthalAngle() { return openNXFloat("azimuthal_angle"); } + /// Opens the dataset containing pixel polar angles + NXFloat openPolarAngle() { return openNXFloat("polar_angle"); } +}; + +/** Implements NXdisk_chopper Nexus class. + */ +class MANTID_NEXUS_DLL NXDiskChopper : public NXMainClass { +public: + /** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the NXClass. + * @param name :: The name of the NXClass relative to its parent + */ + NXDiskChopper(const NXClass &parent, const std::string &name) : NXMainClass(parent, name) {} + /// Nexus class id + std::string NX_class() const override { return "NXdisk_chopper"; } + /// Opens the dataset containing pixel distances + NXFloat openRotationSpeed() { return openNXFloat("rotation_speed"); } +}; + +/** Implements NXinstrument Nexus class. + */ +class MANTID_NEXUS_DLL NXInstrument : public NXMainClass { +public: + /** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the NXClass. + * @param name :: The name of the NXClass relative to its parent + */ + NXInstrument(const NXClass &parent, const std::string &name) : NXMainClass(parent, name) {} + /// Nexus class id + std::string NX_class() const override { return "NXinstrument"; } + /** Opens a NXDetector + * @param name :: The name of the class + * @return The detector + */ + NXDetector openNXDetector(const std::string &name) { return openNXClass(name); } + + /** Opens a NXDetector + * @param name :: The name of the class + * @return The detector + */ + NXDiskChopper openNXDiskChopper(const std::string &name) { return openNXClass(name); } +}; + +/** Implements NXentry Nexus class. + */ +class MANTID_NEXUS_DLL NXEntry : public NXMainClass { +public: + /** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the NXClass. + * @param name :: The name of the NXClass relative to its parent + */ + NXEntry(const NXClass &parent, const std::string &name) : NXMainClass(parent, name) {} + /// Nexus class id + std::string NX_class() const override { return "NXentry"; } + /** Opens a NXData + * @param name :: The name of the class + * @return the nxdata entry + */ + NXData openNXData(const std::string &name) const { return openNXClass(name); } + /** Opens a NXInstrument + * @param name :: The name of the class + * @return the instrument + */ + NXInstrument openNXInstrument(const std::string &name) const { return openNXClass(name); } +}; + +/** Implements NXroot Nexus class. + */ +class MANTID_NEXUS_DLL NXRoot : public NXClass { +public: + // Constructor + NXRoot(std::string fname); + // Constructor + NXRoot(std::string fname, const std::string &entry); + /// Destructor + ~NXRoot() override; + /// Return the NX class for a class (HDF group) or "SDS" for a data set; + std::string NX_class() const override { return "NXroot"; } + /// True if complies with our understanding of the www.nexusformat.org + /// definition. + bool isStandard() const; + /** Opens an entry -- a topmost Nexus class + * @param name :: The name of the entry + * @return the entry + */ + NXEntry openEntry(const std::string &name) { return openNXClass(name); } + NXEntry openFirstEntry(); + +private: + const std::string m_filename; ///< The file name +}; + +} // namespace LegacyNexus +} // namespace Mantid diff --git a/Framework/Nexus/src/LegacyNexusClasses.cpp b/Framework/Nexus/src/LegacyNexusClasses.cpp new file mode 100644 index 000000000000..d1d3df35ceb8 --- /dev/null +++ b/Framework/Nexus/src/LegacyNexusClasses.cpp @@ -0,0 +1,634 @@ +// Mantid Repository : https://github.com/mantidproject/mantid +// +// Copyright © 2025 ISIS Rutherford Appleton Laboratory UKRI, +// NScD Oak Ridge National Laboratory, European Spallation Source, +// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS +// SPDX - License - Identifier: GPL - 3.0 + +#include "MantidNexus/LegacyNexusClasses.h" + +#include "MantidKernel/Exception.h" +#include "MantidKernel/PropertyWithValue.h" + +#include +#include + +namespace Mantid::LegacyNexus { + +std::vector NXAttributes::names() const { + std::vector out; + out.reserve(m_values.size()); + std::transform(m_values.cbegin(), m_values.cend(), std::back_inserter(out), + [](const auto &value) { return value.first; }); + return out; +} + +std::vector NXAttributes::values() const { + std::vector out; + out.reserve(m_values.size()); + std::transform(m_values.cbegin(), m_values.cend(), std::back_inserter(out), + [](const auto &value) { return value.second; }); + return out; +} + +/** Returns the value of an attribute + * @param name :: The name of the attribute + * @return The value of the attribute if it exists or an empty string + * otherwise + */ +std::string NXAttributes::operator()(const std::string &name) const { + auto it = m_values.find(name); + if (it == m_values.end()) + return ""; + return it->second; +} + +/** Sets the value of the attribute. + * @param name :: The name of the attribute + * @param value :: The new value of the attribute + */ +void NXAttributes::set(const std::string &name, const std::string &value) { m_values[name] = value; } + +/** Sets the value of the attribute as a double. + * @param name :: The name of the attribute + * @param value :: The new value of the attribute + */ +void NXAttributes::set(const std::string &name, double value) { + std::ostringstream ostr; + ostr << value; + m_values[name] = ostr.str(); +} + +//--------------------------------------------------------- +// NXObject methods +//--------------------------------------------------------- + +/** NXObject constructor. + * @param fileID :: The Nexus file id + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the object. + * @param name :: The name of the object relative to its parent + */ +NXObject::NXObject(const NXhandle fileID, const NXClass *parent, const std::string &name) + : m_fileID(fileID), m_open(false) { + if (parent && !name.empty()) { + m_path = parent->path() + "/" + name; + } +} + +std::string NXObject::name() const { + size_t i = m_path.find_last_of('/'); + if (i == std::string::npos) + return m_path; + else + return m_path.substr(i + 1, m_path.size() - i - 1); +} + +/** Reads in attributes + */ +void NXObject::getAttributes() { + NXname pName; + NXnumtype iType; + int iLength; + int rank; + int dims[4]; + std::vector buff(128); + + while (NXgetnextattra(m_fileID, pName, &rank, dims, &iType) != NXstatus::NX_EOD) { + if (rank > 1) { // mantid only supports single value attributes + throw std::runtime_error("Encountered attribute with multi-dimensional array value"); + } + iLength = dims[0]; // to clarify things + if (iType != NXnumtype::CHAR && iLength != 1) { + throw std::runtime_error("Encountered attribute with array value"); + } + + switch (iType) { + case NXnumtype::CHAR: { + if (iLength >= 0 && (unsigned)iLength > buff.size()) { + buff.resize(iLength); + } + int nz = iLength + 1; + NXgetattr(m_fileID, pName, buff.data(), &nz, &iType); + attributes.set(pName, buff.data()); + break; + } + case NXnumtype::INT16: { + short int value; + NXgetattr(m_fileID, pName, &value, &iLength, &iType); + sprintf(buff.data(), "%i", value); + attributes.set(pName, buff.data()); + break; + } + case NXnumtype::INT32: { + int value; + NXgetattr(m_fileID, pName, &value, &iLength, &iType); + sprintf(buff.data(), "%i", value); + attributes.set(pName, buff.data()); + break; + } + case NXnumtype::UINT16: { + short unsigned int value; + NXgetattr(m_fileID, pName, &value, &iLength, &iType); + sprintf(buff.data(), "%u", value); + attributes.set(pName, buff.data()); + break; + } + default: + break; + } + }; +} +//--------------------------------------------------------- +// NXClass methods +//--------------------------------------------------------- + +NXClass::NXClass(const NXClass &parent, const std::string &name) : NXObject(parent.m_fileID, &parent, name) { clear(); } + +NXClassInfo NXClass::getNextEntry() { + NXClassInfo res; + char nxname[NX_MAXNAMELEN], nxclass[NX_MAXNAMELEN]; + res.stat = NXgetnextentry(m_fileID, nxname, nxclass, &res.datatype); + if (res) // Check if previous call was successful + { + res.nxname = nxname; + res.nxclass = nxclass; + } + return res; +} + +void NXClass::readAllInfo() { + clear(); + NXClassInfo info; + while ((info = getNextEntry())) { + if (info.nxclass == "SDS") { + NXInfo data_info; + NXopendata(m_fileID, info.nxname.c_str()); + data_info.stat = NXgetinfo(m_fileID, &data_info.rank, data_info.dims, &data_info.type); + NXclosedata(m_fileID); + data_info.nxname = info.nxname; + m_datasets->emplace_back(data_info); + } else if (info.nxclass.substr(0, 2) == "NX" || info.nxclass.substr(0, 2) == "IX") { + m_groups->emplace_back(info); + } + } + reset(); +} + +bool NXClass::isValid(const std::string &path) const { + if (NXopengrouppath(m_fileID, path.c_str()) == NXstatus::NX_OK) { + NXclosegroup(m_fileID); + return true; + } else + return false; +} + +void NXClass::open() { + if (NXopengrouppath(m_fileID, m_path.c_str()) == NXstatus::NX_ERROR) { + + throw std::runtime_error("Cannot open group " + name() + " of class " + NX_class() + " (trying to open path " + + m_path + ")"); + } + //} + m_open = true; + readAllInfo(); +} + +/** It is fast, but the parent of this class must be open at + * the time of calling. openNXClass uses open() (the slow one). To open class + * using openLocal() do: + * NXTheClass class(parent,name); + * class.openLocal(); + * // work with class + * class.close(); + * @param nxclass :: The NX class name. If empty NX_class() will be used + * @return true if OK + */ +bool NXClass::openLocal(const std::string &nxclass) { + std::string className = nxclass.empty() ? NX_class() : nxclass; + if (NXopengroup(m_fileID, name().c_str(), className.c_str()) == NXstatus::NX_ERROR) { + // It would be nice if this worked + // if (NXstatus::NX_ERROR == NXopengrouppath(m_fileID,m_path.c_str())) + //{ + // throw std::runtime_error("Cannot open group "+m_path+" of class + // "+NX_class()); + //} + return false; + } + m_open = true; + readAllInfo(); + return true; +} + +void NXClass::close() { + if (NXclosegroup(m_fileID) == NXstatus::NX_ERROR) { + throw std::runtime_error("Cannot close group " + name() + " of class " + NX_class() + " (trying to close path " + + m_path + ")"); + } + m_open = false; +} + +void NXClass::reset() { NXinitgroupdir(m_fileID); } + +void NXClass::clear() { + m_groups.reset(new std::vector); + m_datasets.reset(new std::vector); +} + +std::string NXClass::getString(const std::string &name) const { + NXChar buff = openNXChar(name); + try { + buff.load(); + return std::string(buff(), buff.dim0()); + } catch (std::runtime_error &) { + // deals with reading uninitialized/empty data + return std::string(); + } +} + +double NXClass::getDouble(const std::string &name) const { + NXDouble number = openNXDouble(name); + number.load(); + return *number(); +} + +float NXClass::getFloat(const std::string &name) const { + NXFloat number = openNXFloat(name); + number.load(); + return *number(); +} + +int NXClass::getInt(const std::string &name) const { + NXInt number = openNXInt(name); + number.load(); + return *number(); +} +/** Returns whether an individual group (or group) is present + * @param query :: the class name to search for + * @return true if the name is found and false otherwise + */ +bool NXClass::containsGroup(const std::string &query) const { + return std::any_of(m_groups->cbegin(), m_groups->cend(), + [&query](const auto &group) { return group.nxname == query; }); +} + +/** + * Returns NXInfo for a dataset + * @param name :: The name of the dataset + * @return NXInfo::stat is set to NXstatus::NX_ERROR if the dataset does not exist + */ +NXInfo NXClass::getDataSetInfo(const std::string &name) const { + const auto it = std::find_if(datasets().cbegin(), datasets().cend(), + [&name](const auto &dataset) { return dataset.nxname == name; }); + if (it != datasets().cend()) { + return *it; + } + NXInfo info; + info.stat = NXstatus::NX_ERROR; + return info; +} + +/** + * Returns whether an individual dataset is present. + */ +bool NXClass::containsDataSet(const std::string &query) const { + return getDataSetInfo(query).stat != NXstatus::NX_ERROR; +} + +//--------------------------------------------------------- +// NXNote methods +//--------------------------------------------------------- + +std::string NXNote::author() { + if (!m_author_ok) { + NXChar aut = openNXChar("author"); + aut.load(); + m_author = std::string(aut(), aut.dim0()); + m_author_ok = true; + } + return m_author; +} + +std::vector &NXNote::data() { + if (!m_data_ok) { + int rank; + int dims[4]; + NXnumtype type; + NXopendata(m_fileID, "data"); + NXgetinfo(m_fileID, &rank, dims, &type); + int n = dims[0]; + auto buffer = new char[n]; + NXstatus stat = NXgetdata(m_fileID, buffer); + NXclosedata(m_fileID); + m_data.clear(); + if (stat == NXstatus::NX_ERROR) { + delete[] buffer; + return m_data; + } + std::istringstream istr(std::string(buffer, n)); + delete[] buffer; + + std::string line; + while (getline(istr, line)) { + m_data.emplace_back(line); + } + + m_data_ok = true; + } + return m_data; +} + +std::string NXNote::description() { + if (!m_description_ok) { + NXChar str = openNXChar("description"); + str.load(); + m_description = std::string(str(), str.dim0()); + m_description_ok = true; + } + return m_description; +} + +std::vector &NXBinary::binary() { + if (!m_data_ok) { + int rank; + int dims[4]; + NXnumtype type; + NXopendata(m_fileID, "data"); + NXgetinfo(m_fileID, &rank, dims, &type); + int n = dims[0]; + m_binary.resize(n); + NXstatus stat = NXgetdata(m_fileID, &m_binary[0]); + (void)stat; // Avoid unused variable compiler warning + NXclosedata(m_fileID); + } + return m_binary; +} + +//--------------------------------------------------------- +// NXRoot methods +//--------------------------------------------------------- + +/** Constructor. On creation opens the Nexus file for reading only. + * @param fname :: The file name to open + */ +NXRoot::NXRoot(std::string fname) : m_filename(std::move(fname)) { + // Open NeXus file + NXstatus stat = NXopen(m_filename.c_str(), NXACC_READ, &m_fileID); + if (stat == NXstatus::NX_ERROR) { + std::cout << "NXRoot: Error loading " << m_filename; + throw Kernel::Exception::FileError("Unable to open File:", m_filename); + } + readAllInfo(); +} + +/** Constructor. + * Creates a new Nexus file. The first root entry will be also created. + * @param fname :: The file name to create + * @param entry :: The name of the first entry in the new file + */ +NXRoot::NXRoot(std::string fname, const std::string &entry) : m_filename(std::move(fname)) { + (void)entry; + // Open NeXus file + NXstatus stat = NXopen(m_filename.c_str(), NXACC_CREATE5, &m_fileID); + if (stat == NXstatus::NX_ERROR) { + throw Kernel::Exception::FileError("Unable to open File:", m_filename); + } +} + +NXRoot::~NXRoot() { NXclose(&m_fileID); } + +bool NXRoot::isStandard() const { return true; } + +/** + * Open the first NXentry in the file. + */ +NXEntry NXRoot::openFirstEntry() { + if (groups().empty()) { + throw std::runtime_error("NeXus file has no entries"); + } + const auto it = + std::find_if(groups().cbegin(), groups().cend(), [](const auto &group) { return group.nxclass == "NXentry"; }); + if (it != groups().cend()) { + return openEntry(it->nxname); + } + throw std::runtime_error("NeXus file has no entries"); +} + +//--------------------------------------------------------- +// NXDataSet methods +//--------------------------------------------------------- + +/** Constructor. + * @param parent :: The parent Nexus class. In terms of HDF it is the group + * containing the dataset. + * @param name :: The name of the dataset relative to its parent + */ +NXDataSet::NXDataSet(const NXClass &parent, const std::string &name) : NXObject(parent.m_fileID, &parent, name) { + size_t i = name.find_last_of('/'); + if (i == std::string::npos) + m_info.nxname = name; + else if (name.empty() || i == name.size() - 1) + throw std::runtime_error("Improper dataset name " + name); + else + m_info.nxname = name.substr(i + 1); +} + +// Opens the data set. Does not read in any data. Call load(...) to load the +// data +void NXDataSet::open() { + size_t i = m_path.find_last_of('/'); + if (i == std::string::npos || i == 0) + return; // we are in the root group, assume it is open + std::string group_path = m_path.substr(0, i); + if (NXopenpath(m_fileID, group_path.c_str()) == NXstatus::NX_ERROR) { + throw std::runtime_error("Cannot open dataset " + m_path); + } + if (NXopendata(m_fileID, name().c_str()) != NXstatus::NX_OK) { + throw std::runtime_error("Error opening data in group \"" + name() + "\""); + } + + if (NXgetinfo(m_fileID, &m_info.rank, m_info.dims, &m_info.type) != NXstatus::NX_OK) { + throw std::runtime_error("Error retrieving information for " + name() + " group"); + } + + getAttributes(); + NXclosedata(m_fileID); +} + +void NXDataSet::openLocal() { + if (NXopendata(m_fileID, name().c_str()) != NXstatus::NX_OK) { + throw std::runtime_error("Error opening data in group \"" + name() + "\""); + } + if (NXgetinfo(m_fileID, &m_info.rank, m_info.dims, &m_info.type) != NXstatus::NX_OK) { + throw std::runtime_error("Error retrieving information for " + name() + " group"); + } + getAttributes(); + NXclosedata(m_fileID); +} + +/** + * The size of the first dimension of data + * @returns An integer indicating the size of the dimension. + * @throws out_of_range error if requested on an object of rank 0 + */ +int NXDataSet::dim0() const { + if (m_info.rank == 0) { + throw std::out_of_range("NXDataSet::dim0() - Requested dimension greater than rank."); + } + return m_info.dims[0]; +} + +/** + * The size of the second dimension of data + * @returns An integer indicating the size of the dimension + * @throws out_of_range error if requested on an object of rank < 2 + */ +int NXDataSet::dim1() const { + if (m_info.rank < 2) { + throw std::out_of_range("NXDataSet::dim1() - Requested dimension greater than rank."); + } + return m_info.dims[1]; +} + +/** + * The size of the third dimension of data + * @returns An integer indicating the size of the dimension + * @throws out_of_range error if requested on an object of rank < 3 + */ +int NXDataSet::dim2() const { + if (m_info.rank < 3) { + throw std::out_of_range("NXDataSet::dim2() - Requested dimension greater than rank."); + } + return m_info.dims[2]; +} + +/** + * The size of the fourth dimension of data + * @returns An integer indicating the size of the dimension + * @throws out_of_range error if requested on an object of rank < 4 + */ +int NXDataSet::dim3() const { + if (m_info.rank < 4) { + throw std::out_of_range("NXDataSet::dim3() - Requested dimension greater than rank."); + } + return m_info.dims[3]; +} + +/** Wrapper to the NXgetdata. + * @param data :: The pointer to the buffer accepting the data from the file. + * @throw runtime_error if the operation fails. + */ +void NXDataSet::getData(void *data) { + NXopendata(m_fileID, name().c_str()); + if (NXgetdata(m_fileID, data) != NXstatus::NX_OK) + throw std::runtime_error("Cannot read data from NeXus file"); + NXclosedata(m_fileID); +} + +/** Wrapper to the NXgetslab. + * @param data :: The pointer to the buffer accepting the data from the file. + * @param start :: The array of starting indeces to read in from the file. The + * size of the array must be equal to + * the rank of the data. + * @param size :: The array of numbers of data elements to read along each + * dimenstion. + * The number of dimensions (the size of the array) must be equal to + * the rank of the data. + * @throw runtime_error if the operation fails. + */ +void NXDataSet::getSlab(void *data, int start[], int size[]) { + NXopendata(m_fileID, name().c_str()); + if (NXgetslab(m_fileID, data, start, size) != NXstatus::NX_OK) + throw std::runtime_error("Cannot read data slab from NeXus file"); + NXclosedata(m_fileID); +} + +//--------------------------------------------------------- +// NXData methods +//--------------------------------------------------------- + +NXData::NXData(const NXClass &parent, const std::string &name) : NXMainClass(parent, name) {} + +//--------------------------------------------------------- +// NXLog methods +//--------------------------------------------------------- + +/** Creates a property wrapper around the log entry + * @returns A valid property pointer or NULL + */ +Kernel::Property *NXLog::createProperty() { + NXInfo vinfo = getDataSetInfo("time"); + if (vinfo.stat == NXstatus::NX_ERROR) { + return createSingleValueProperty(); + } else { + return createTimeSeries(); + } +} + +/** Creates a single value property of the log + * @returns A pointer to a newly created property wrapped around the log entry + */ +Kernel::Property *NXLog::createSingleValueProperty() { + const std::string valAttr("value"); + NXInfo vinfo = getDataSetInfo(valAttr); + Kernel::Property *prop; + NXnumtype nxType = vinfo.type; + if (nxType == NXnumtype::FLOAT64) { + prop = new Kernel::PropertyWithValue(name(), getDouble(valAttr)); + } else if (nxType == NXnumtype::INT32) { + prop = new Kernel::PropertyWithValue(name(), getInt(valAttr)); + } else if (nxType == NXnumtype::CHAR) { + prop = new Kernel::PropertyWithValue(name(), getString(valAttr)); + } else if (nxType == NXnumtype::UINT8) { + NXDataSetTyped value(*this, valAttr); + value.load(); + bool state = value[0] != 0; + prop = new Kernel::PropertyWithValue(name(), state); + } else { + prop = nullptr; + } + + return prop; +} + +/** createTimeSeries + * Create a TimeSeries property form the records of the NXLog group. Times are + * in dataset "time" + * and the values are in dataset "value" + * @param start_time :: If the "time" dataset does not have the "start" + * attribute sets the + * start time for the series. + * @param new_name :: If not empty it is used as the TimeSeries property name + * @return The property or NULL + */ +Kernel::Property *NXLog::createTimeSeries(const std::string &start_time, const std::string &new_name) { + const std::string &logName = new_name.empty() ? name() : new_name; + NXInfo vinfo = getDataSetInfo("time"); + if (vinfo.type == NXnumtype::FLOAT64) { + NXDouble times(*this, "time"); + times.openLocal(); + times.load(); + std::string units = times.attributes("units"); + if (units == "minutes") { + using std::placeholders::_1; + std::transform(times(), times() + times.dim0(), times(), std::bind(std::multiplies(), _1, 60)); + } else if (!units.empty() && units.substr(0, 6) != "second") { + return nullptr; + } + return parseTimeSeries(logName, times, start_time); + } else if (vinfo.type == NXnumtype::FLOAT32) { + NXFloat times(*this, "time"); + times.openLocal(); + times.load(); + std::string units = times.attributes("units"); + if (units == "minutes") { + std::for_each(times(), times() + times.dim0(), [](float &val) { val *= 60.0f; }); + } else if (!units.empty() && units.substr(0, 6) != "second") { + return nullptr; + } + return parseTimeSeries(logName, times, start_time); + } + + return nullptr; +} + +} // namespace Mantid::LegacyNexus From ae2bcd1ddb8f53a205dfeb17179542549a39d9a5 Mon Sep 17 00:00:00 2001 From: Ross Whitfield Date: Wed, 12 Feb 2025 13:23:38 +1100 Subject: [PATCH 2/7] Move LoadMuonNexus2 to LegacyNexus --- .../Muon/inc/MantidMuon/LoadMuonNexus2.h | 10 ++++----- Framework/Muon/src/LoadMuonNexus2.cpp | 22 +++++++++---------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Framework/Muon/inc/MantidMuon/LoadMuonNexus2.h b/Framework/Muon/inc/MantidMuon/LoadMuonNexus2.h index 99b33a391127..aa1848ff297e 100644 --- a/Framework/Muon/inc/MantidMuon/LoadMuonNexus2.h +++ b/Framework/Muon/inc/MantidMuon/LoadMuonNexus2.h @@ -12,7 +12,7 @@ #include "MantidDataObjects/Histogram1D.h" #include "MantidMuon/DllConfig.h" #include "MantidMuon/LoadMuonNexus.h" -#include "MantidNexus/NexusClasses.h" +#include "MantidNexus/LegacyNexusClasses.h" namespace Mantid { @@ -65,11 +65,11 @@ class MANTID_MUON_DLL LoadMuonNexus2 : public LoadMuonNexus { private: void exec() override; - HistogramData::Histogram loadData(const Mantid::HistogramData::BinEdges &edges, const Mantid::NeXus::NXInt &counts, - int period, int spec); - void loadLogs(const API::MatrixWorkspace_sptr &ws, Mantid::NeXus::NXEntry &entry, int period); + HistogramData::Histogram loadData(const Mantid::HistogramData::BinEdges &edges, + const Mantid::LegacyNexus::NXInt &counts, int period, int spec); + void loadLogs(const API::MatrixWorkspace_sptr &ws, Mantid::LegacyNexus::NXEntry &entry, int period); void loadRunDetails(const DataObjects::Workspace2D_sptr &localWorkspace); - std::map> loadDetectorMapping(const Mantid::NeXus::NXInt &spectrumIndex); + std::map> loadDetectorMapping(const Mantid::LegacyNexus::NXInt &spectrumIndex); }; } // namespace Algorithms diff --git a/Framework/Muon/src/LoadMuonNexus2.cpp b/Framework/Muon/src/LoadMuonNexus2.cpp index 440ff353227b..d517ff598cea 100644 --- a/Framework/Muon/src/LoadMuonNexus2.cpp +++ b/Framework/Muon/src/LoadMuonNexus2.cpp @@ -23,12 +23,9 @@ #include "MantidKernel/Unit.h" #include "MantidKernel/UnitFactory.h" #include "MantidKernel/UnitLabelTypes.h" -#include "MantidNexus/NexusClasses.h" -#include "MantidNexusCpp/NeXusException.hpp" -#include "MantidNexusCpp/NeXusFile.hpp" - -// must be after MantidNexusCpp/NeXusFile.hpp +#include "MantidLegacyNexus/NeXusException.hpp" #include "MantidLegacyNexus/NeXusFile.hpp" +#include "MantidNexus/LegacyNexusClasses.h" #include #include @@ -48,7 +45,7 @@ using Geometry::Instrument; using Mantid::HistogramData::BinEdges; using Mantid::HistogramData::Counts; using Mantid::HistogramData::Histogram; -using namespace Mantid::NeXus; +using namespace Mantid::LegacyNexus; using Mantid::Types::Core::DateAndTime; LoadMuonNexus2::LoadMuonNexus2() : LoadMuonNexus() {} @@ -78,7 +75,7 @@ void LoadMuonNexus2::exec() { if (entry.containsGroup("run")) { try { m_numberOfPeriods = entry.getInt("run/number_periods"); - } catch (::NeXus::Exception &) { + } catch (LegacyNexus::Exception &) { // assume 1 m_numberOfPeriods = 1; } @@ -104,7 +101,7 @@ void LoadMuonNexus2::exec() { } NXData dataGroup = entry.openNXData(detectorName); - Mantid::NeXus::NXInt spectrum_index = dataGroup.openNXInt("spectrum_index"); + LegacyNexus::NXInt spectrum_index = dataGroup.openNXInt("spectrum_index"); spectrum_index.load(); m_numberOfSpectra = spectrum_index.dim0(); @@ -168,7 +165,7 @@ void LoadMuonNexus2::exec() { // Mantid::NeXus::NXInt period_index = dataGroup.openNXInt("period_index"); // period_index.load(); - Mantid::NeXus::NXInt counts = dataGroup.openIntData(); + Mantid::LegacyNexus::NXInt counts = dataGroup.openIntData(); counts.load(); NXInstrument instr = entry.openNXInstrument("instrument"); @@ -268,7 +265,8 @@ void LoadMuonNexus2::exec() { /** loadData * Load the counts data from an NXInt into a workspace */ -Histogram LoadMuonNexus2::loadData(const BinEdges &edges, const Mantid::NeXus::NXInt &counts, int period, int spec) { +Histogram LoadMuonNexus2::loadData(const BinEdges &edges, const Mantid::LegacyNexus::NXInt &counts, int period, + int spec) { int nBins = 0; const int *data = nullptr; @@ -424,7 +422,7 @@ int LoadMuonNexus2::confidence(Kernel::LegacyNexusDescriptor &descriptor) const * @returns :: map of index -> detector IDs * @throws std::runtime_error if fails to read data from file */ -std::map> LoadMuonNexus2::loadDetectorMapping(const Mantid::NeXus::NXInt &spectrumIndex) { +std::map> LoadMuonNexus2::loadDetectorMapping(const Mantid::LegacyNexus::NXInt &spectrumIndex) { std::map> mapping; const int nSpectra = spectrumIndex.dim0(); @@ -465,7 +463,7 @@ std::map> LoadMuonNexus2::loadDetectorMapping(const Mantid::N } mapping[i] = detIDs; } - } catch (const ::NeXus::Exception &err) { + } catch (const LegacyNexus::Exception &err) { // Throw a more user-friendly message std::ostringstream message; message << "Failed to read detector mapping: " << err.what(); From 77f6e1f38b3e947e3b0579ed443eb5058ff01208 Mon Sep 17 00:00:00 2001 From: Ross Whitfield Date: Wed, 12 Feb 2025 13:44:10 +1100 Subject: [PATCH 3/7] Move LoadMuonNexus1 to LegacyNexus --- .../Muon/inc/MantidMuon/LoadMuonNexus1.h | 6 +-- Framework/Muon/src/LoadMuonNexus1.cpp | 52 +++++++++---------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Framework/Muon/inc/MantidMuon/LoadMuonNexus1.h b/Framework/Muon/inc/MantidMuon/LoadMuonNexus1.h index 9f5331f131d7..7b134b50e4c4 100644 --- a/Framework/Muon/inc/MantidMuon/LoadMuonNexus1.h +++ b/Framework/Muon/inc/MantidMuon/LoadMuonNexus1.h @@ -20,7 +20,7 @@ namespace Mantid { //---------------------------------------------------------------------- // Forward declaration //---------------------------------------------------------------------- -namespace NeXus { +namespace LegacyNexus { class NXRoot; } @@ -73,14 +73,14 @@ class MANTID_MUON_DLL LoadMuonNexus1 : public LoadMuonNexus { void addGoodFrames(const DataObjects::Workspace2D_sptr &localWorkspace, int64_t period, int nperiods); /// Loads dead time table for the detector - void loadDeadTimes(Mantid::NeXus::NXRoot &root); + void loadDeadTimes(Mantid::LegacyNexus::NXRoot &root); /// Creates Dead Time Table using all the data between begin and end DataObjects::TableWorkspace_sptr createDeadTimeTable(std::vector const &specToLoad, std::vector const &deadTimes); /// Loads detector grouping information - API::Workspace_sptr loadDetectorGrouping(Mantid::NeXus::NXRoot &root, + API::Workspace_sptr loadDetectorGrouping(Mantid::LegacyNexus::NXRoot &root, const Mantid::Geometry::Instrument_const_sptr &inst); /// Creates Detector Grouping Table using all the data from the range diff --git a/Framework/Muon/src/LoadMuonNexus1.cpp b/Framework/Muon/src/LoadMuonNexus1.cpp index 0becbedf456d..fcf230554b1e 100644 --- a/Framework/Muon/src/LoadMuonNexus1.cpp +++ b/Framework/Muon/src/LoadMuonNexus1.cpp @@ -29,12 +29,11 @@ #include "MantidKernel/UnitFactory.h" #include "MantidKernel/UnitLabelTypes.h" #include "MantidNexus/MuonNexusReader.h" -#include "MantidNexus/NexusClasses.h" -#include "MantidNexusCpp/NeXusException.hpp" -#include "MantidNexusCpp/NeXusFile.hpp" -// must be after MantidNexusCpp/NeXusFile.hpp +// must be after MantidNexus/MuonNexusReader.h which includes MantidNexusCpp/NeXusFile.hpp +#include "MantidLegacyNexus/NeXusException.hpp" #include "MantidLegacyNexus/NeXusFile.hpp" +#include "MantidNexus/LegacyNexusClasses.h" #include #include @@ -72,7 +71,7 @@ DECLARE_NEXUS_FILELOADER_ALGORITHM(LoadMuonNexus1) using namespace Kernel; using namespace API; -using namespace Mantid::NeXus; +using namespace Mantid::LegacyNexus; using HistogramData::BinEdges; using HistogramData::Counts; @@ -96,7 +95,7 @@ void LoadMuonNexus1::exec() { NXEntry entry = root.openEntry("run/histogram_data_1"); try { NXInfo info = entry.getDataSetInfo("time_zero"); - if (info.stat != ::NXstatus::NX_ERROR) { + if (info.stat != NXstatus::NX_ERROR) { double dum = root.getFloat("run/histogram_data_1/time_zero"); setProperty("TimeZero", dum); } @@ -107,14 +106,14 @@ void LoadMuonNexus1::exec() { NXInfo infoResolution = entry.getDataSetInfo("resolution"); NXInt counts = root.openNXInt("run/histogram_data_1/counts"); std::string firstGoodBin = counts.attributes("first_good_bin"); - if (!firstGoodBin.empty() && infoResolution.stat != ::NXstatus::NX_ERROR) { + if (!firstGoodBin.empty() && infoResolution.stat != NXstatus::NX_ERROR) { double resolution; switch (infoResolution.type) { - case ::NXnumtype::FLOAT32: + case NXnumtype::FLOAT32: resolution = static_cast(entry.getFloat("resolution")); break; - case ::NXnumtype::INT32: + case NXnumtype::INT32: resolution = static_cast(entry.getInt("resolution")); break; default: @@ -134,14 +133,14 @@ void LoadMuonNexus1::exec() { NXInfo infoResolution = entry.getDataSetInfo("resolution"); NXInt counts = root.openNXInt("run/histogram_data_1/counts"); std::string lastGoodBin = counts.attributes("last_good_bin"); - if (!lastGoodBin.empty() && infoResolution.stat != ::NXstatus::NX_ERROR) { + if (!lastGoodBin.empty() && infoResolution.stat != NXstatus::NX_ERROR) { double resolution; switch (infoResolution.type) { - case ::NXnumtype::FLOAT32: + case NXnumtype::FLOAT32: resolution = static_cast(entry.getFloat("resolution")); break; - case ::NXnumtype::INT32: + case NXnumtype::INT32: resolution = static_cast(entry.getInt("resolution")); break; default: @@ -391,7 +390,7 @@ void LoadMuonNexus1::loadDeadTimes(NXRoot &root) { NXEntry detector = root.openEntry("run/instrument/detector"); NXInfo infoDeadTimes = detector.getDataSetInfo("deadtimes"); - if (infoDeadTimes.stat != ::NXstatus::NX_ERROR) { + if (infoDeadTimes.stat != NXstatus::NX_ERROR) { NXFloat deadTimesData = detector.openNXFloat("deadtimes"); deadTimesData.load(); @@ -417,12 +416,13 @@ void LoadMuonNexus1::loadDeadTimes(NXRoot &root) { if (numDeadTimes < m_numberOfSpectra) { // Check number of dead time entries match the number of // spectra in the nexus file - throw Exception::FileError("Number of dead times specified is less than number of spectra", m_filename); + throw Kernel::Exception::FileError("Number of dead times specified is less than number of spectra", m_filename); } else if (numDeadTimes % m_numberOfSpectra) { // At least, number of dead times should cover the number of spectra - throw Exception::FileError("Number of dead times doesn't cover every spectrum in every period", m_filename); + throw Kernel::Exception::FileError("Number of dead times doesn't cover every spectrum in every period", + m_filename); } else { if (m_numberOfPeriods == 1) { @@ -488,7 +488,7 @@ Workspace_sptr LoadMuonNexus1::loadDetectorGrouping(NXRoot &root, const Geometry NXEntry dataEntry = root.openEntry("run/histogram_data_1"); NXInfo infoGrouping = dataEntry.getDataSetInfo("grouping"); - if (infoGrouping.stat != ::NXstatus::NX_ERROR) { + if (infoGrouping.stat != NXstatus::NX_ERROR) { NXInt groupingData = dataEntry.openNXInt("grouping"); groupingData.load(); @@ -514,13 +514,13 @@ Workspace_sptr LoadMuonNexus1::loadDetectorGrouping(NXRoot &root, const Geometry if (numGroupingEntries < m_numberOfSpectra) { // Check number of dead time entries match the number of // spectra in the nexus file - throw Exception::FileError("Number of grouping entries is less than number of spectra", m_filename); + throw Kernel::Exception::FileError("Number of grouping entries is less than number of spectra", m_filename); } else if (numGroupingEntries % m_numberOfSpectra) { // At least the number of entries should cover all the spectra - throw Exception::FileError("Number of grouping entries doesn't cover " - "every spectrum in every period", - m_filename); + throw Kernel::Exception::FileError("Number of grouping entries doesn't cover " + "every spectrum in every period", + m_filename); } else { @@ -582,7 +582,7 @@ Workspace_sptr LoadMuonNexus1::loadDetectorGrouping(NXRoot &root, const Geometry if (tableGroup->size() != 0) { if (tableGroup->size() != static_cast(m_numberOfPeriods)) - throw Exception::FileError("Zero grouping for some of the periods", m_filename); + throw Kernel::Exception::FileError("Zero grouping for some of the periods", m_filename); return tableGroup; } @@ -815,7 +815,7 @@ void LoadMuonNexus1::addPeriodLog(const DataObjects::Workspace2D_sptr &localWork void LoadMuonNexus1::addGoodFrames(const DataObjects::Workspace2D_sptr &localWorkspace, int64_t period, int nperiods) { // Get handle to nexus file - ::NeXus::File handle(m_filename, ::NXACC_READ); + File handle(m_filename, ::NXACC_READ); // For single-period datasets, read /run/instrument/beam/frames_good if (nperiods == 1) { @@ -825,7 +825,7 @@ void LoadMuonNexus1::addGoodFrames(const DataObjects::Workspace2D_sptr &localWor handle.openPath("run/instrument/beam"); try { handle.openData("frames_good"); - } catch (::NeXus::Exception &) { + } catch (LegacyNexus::Exception &) { // If it's not there, read "frames" instead and assume they are good g_log.warning("Could not read /run/instrument/beam/frames_good"); handle.openData("frames"); @@ -839,7 +839,7 @@ void LoadMuonNexus1::addGoodFrames(const DataObjects::Workspace2D_sptr &localWor auto &run = localWorkspace->mutableRun(); run.addProperty("goodfrm", dataVals[0]); - } catch (::NeXus::Exception &) { + } catch (LegacyNexus::Exception &) { g_log.warning("Could not read number of good frames"); } @@ -851,7 +851,7 @@ void LoadMuonNexus1::addGoodFrames(const DataObjects::Workspace2D_sptr &localWor handle.openPath("run/instrument/beam/"); handle.openData("frames_period_daq"); - ::NeXus::Info info = handle.getInfo(); + Info info = handle.getInfo(); // Check that frames_period_daq contains values for // every period if (period >= info.dims[0]) { @@ -883,7 +883,7 @@ void LoadMuonNexus1::addGoodFrames(const DataObjects::Workspace2D_sptr &localWor run.removeLogData("goodfrm"); run.addProperty("goodfrm", dataVals[period]); } - } catch (::NeXus::Exception &) { + } catch (LegacyNexus::Exception &) { g_log.warning("Could not read /run/instrument/beam/frames_period_daq"); } } // else From 2fc2f4bec1abf0c7323eaa24f8062f95e85a28f8 Mon Sep 17 00:00:00 2001 From: Ross Whitfield Date: Wed, 12 Feb 2025 13:47:37 +1100 Subject: [PATCH 4/7] Cleanup includes in LoadMuonNexus.cpp --- Framework/Muon/src/LoadMuonNexus.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Framework/Muon/src/LoadMuonNexus.cpp b/Framework/Muon/src/LoadMuonNexus.cpp index a6271d09f7c8..a0ae023afa89 100644 --- a/Framework/Muon/src/LoadMuonNexus.cpp +++ b/Framework/Muon/src/LoadMuonNexus.cpp @@ -21,13 +21,6 @@ #include "MantidKernel/OptionalBool.h" #include "MantidKernel/TimeSeriesProperty.h" #include "MantidKernel/UnitFactory.h" -#include "MantidNexus/MuonNexusReader.h" -#include "MantidNexus/NexusClasses.h" -#include "MantidNexusCpp/NeXusException.hpp" -#include "MantidNexusCpp/NeXusFile.hpp" - -// must be after MantidNexusCpp/NeXusFile.hpp -#include "MantidLegacyNexus/NeXusFile.hpp" #include #include @@ -39,7 +32,6 @@ namespace Mantid::Algorithms { using namespace Kernel; using namespace API; using Geometry::Instrument; -using namespace Mantid::NeXus; /// Empty default constructor LoadMuonNexus::LoadMuonNexus() From ec12df801889916cad00bcb1be1d87b1aceb3bbe Mon Sep 17 00:00:00 2001 From: Ross Whitfield Date: Wed, 12 Feb 2025 15:16:10 +1100 Subject: [PATCH 5/7] Move MuonNexusReader to LegacyNexus --- Framework/Muon/src/LoadMuonNexus1.cpp | 4 +--- .../Nexus/inc/MantidNexus/MuonNexusReader.h | 8 ++++---- Framework/Nexus/src/MuonNexusReader.cpp | 20 +++++++++---------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Framework/Muon/src/LoadMuonNexus1.cpp b/Framework/Muon/src/LoadMuonNexus1.cpp index fcf230554b1e..347b58fa286e 100644 --- a/Framework/Muon/src/LoadMuonNexus1.cpp +++ b/Framework/Muon/src/LoadMuonNexus1.cpp @@ -28,12 +28,10 @@ #include "MantidKernel/Unit.h" #include "MantidKernel/UnitFactory.h" #include "MantidKernel/UnitLabelTypes.h" -#include "MantidNexus/MuonNexusReader.h" - -// must be after MantidNexus/MuonNexusReader.h which includes MantidNexusCpp/NeXusFile.hpp #include "MantidLegacyNexus/NeXusException.hpp" #include "MantidLegacyNexus/NeXusFile.hpp" #include "MantidNexus/LegacyNexusClasses.h" +#include "MantidNexus/MuonNexusReader.h" #include #include diff --git a/Framework/Nexus/inc/MantidNexus/MuonNexusReader.h b/Framework/Nexus/inc/MantidNexus/MuonNexusReader.h index 39c5fe24c139..524fc053fb86 100644 --- a/Framework/Nexus/inc/MantidNexus/MuonNexusReader.h +++ b/Framework/Nexus/inc/MantidNexus/MuonNexusReader.h @@ -6,8 +6,8 @@ // SPDX - License - Identifier: GPL - 3.0 + #pragma once +#include "MantidLegacyNexus/NeXusFile.hpp" #include "MantidNexus/DllConfig.h" -#include "MantidNexusCpp/NeXusFile.hpp" #include #include @@ -38,8 +38,8 @@ class MANTID_NEXUS_DLL MuonNexusReader { std::vector m_logNames; ///< stores name read from file std::vector m_logUnits; - void openFirstNXentry(NeXus::File &handle); - bool readMuonLogData(NeXus::File &handle); ///< method to read the fields of open NXlog section + void openFirstNXentry(Mantid::LegacyNexus::File &handle); + bool readMuonLogData(Mantid::LegacyNexus::File &handle); ///< method to read the fields of open NXlog section std::vector> m_logValues, ///< array of values for i'th NXlog section m_logTimes; ///< arrys of times for i'th NXlog section std::vector> m_logStringValues; ///< array of string values for i'th NXlog section @@ -61,7 +61,7 @@ class MANTID_NEXUS_DLL MuonNexusReader { boost::posix_time::ptime start(boost::gregorian::date(1970, 1, 1)); return (t - start).total_seconds(); } - void readPeriodInfo(NeXus::File &handle); + void readPeriodInfo(Mantid::LegacyNexus::File &handle); public: /// Default constructor diff --git a/Framework/Nexus/src/MuonNexusReader.cpp b/Framework/Nexus/src/MuonNexusReader.cpp index ef7c302d7a77..35bb64a77e9f 100644 --- a/Framework/Nexus/src/MuonNexusReader.cpp +++ b/Framework/Nexus/src/MuonNexusReader.cpp @@ -6,7 +6,7 @@ // SPDX - License - Identifier: GPL - 3.0 + #include "MantidNexus/MuonNexusReader.h" #include "MantidKernel/Logger.h" -#include "MantidNexusCpp/NeXusException.hpp" +#include "MantidLegacyNexus/NeXusException.hpp" #include #include #include @@ -54,7 +54,7 @@ using namespace Mantid; * * @param handle Object to work on. */ -void MuonNexusReader::openFirstNXentry(NeXus::File &handle) { +void MuonNexusReader::openFirstNXentry(Mantid::LegacyNexus::File &handle) { std::map entries = handle.getEntries(); const auto entry = std::find_if(entries.cbegin(), entries.cend(), [](const auto entry) { return entry.second == NXENTRY; }); @@ -80,7 +80,7 @@ void MuonNexusReader::openFirstNXentry(NeXus::File &handle) { // // @param filename :: name of existing NeXus Muon file to read void MuonNexusReader::readFromFile(const string &filename) { - NeXus::File handle(filename, NXACC_READ); + Mantid::LegacyNexus::File handle(filename, NXACC_READ); openFirstNXentry(handle); // find all of the NXdata in the entry @@ -94,7 +94,7 @@ void MuonNexusReader::readFromFile(const string &filename) { handle.openGroup(nxdataname.front(), NXDATA); // reused local variable - NeXus::Info info; + Mantid::LegacyNexus::Info info; // open NXdata section handle.openData("counts"); @@ -167,7 +167,7 @@ void MuonNexusReader::readFromFile(const string &filename) { * - Tag * - Total counts per period */ -void MuonNexusReader::readPeriodInfo(NeXus::File &handle) { +void MuonNexusReader::readPeriodInfo(Mantid::LegacyNexus::File &handle) { auto parsePeriod = [&handle](const std::string &logName, auto &destAttr) { try { std::remove_reference_t tempAttr; @@ -238,7 +238,7 @@ void MuonNexusReader::readLogData(const string &filename) { // reset the count of logs m_nexusLogCount = 0; - NeXus::File handle(filename, NXACC_READ); + Mantid::LegacyNexus::File handle(filename, NXACC_READ); openFirstNXentry(handle); // read nexus fields at this level looking for NXlog and loading these into @@ -276,7 +276,7 @@ void MuonNexusReader::readLogData(const string &filename) { // file will close on leaving the function } -bool MuonNexusReader::readMuonLogData(NeXus::File &handle) { +bool MuonNexusReader::readMuonLogData(Mantid::LegacyNexus::File &handle) { const string NAME("name"); const string VALUES("values"); const string TIME("time"); @@ -288,7 +288,7 @@ bool MuonNexusReader::readMuonLogData(NeXus::File &handle) { // read data values try { handle.openData(VALUES); - } catch (NeXus::Exception &) { + } catch (Mantid::LegacyNexus::Exception &) { g_log.warning() << "No " << VALUES << " set in " << handle.getPath() << "\n"; return false; } @@ -298,7 +298,7 @@ bool MuonNexusReader::readMuonLogData(NeXus::File &handle) { bool isNumeric(false); std::string units = ""; - NeXus::Info info = handle.getInfo(); + Mantid::LegacyNexus::Info info = handle.getInfo(); if (info.type == NXnumtype::FLOAT32 && info.dims.size() == 1) { isNumeric = true; boost::scoped_array dataVals(new float[info.dims[0]]); @@ -326,7 +326,7 @@ bool MuonNexusReader::readMuonLogData(NeXus::File &handle) { // read time values try { handle.openData(TIME); - } catch (NeXus::Exception &) { + } catch (Mantid::LegacyNexus::Exception &) { g_log.warning() << "No " << TIME << " set in " << handle.getPath() << "\n"; return false; } From 35b097fcc5fe90824a6380d1ab22fdcc89bc1a67 Mon Sep 17 00:00:00 2001 From: Ross Whitfield Date: Thu, 13 Feb 2025 11:23:37 +1100 Subject: [PATCH 6/7] Move to Muon --- Framework/DataHandling/CMakeLists.txt | 1 + .../inc/MantidDataHandling/LoadMuonLog.h | 2 +- .../src/LoadInstrumentFromNexus.cpp | 2 +- Framework/Muon/CMakeLists.txt | 6 +++- .../inc/MantidMuon}/LegacyNexusClasses.h | 30 +++++++++---------- .../Muon/inc/MantidMuon/LoadMuonNexus2.h | 2 +- .../inc/MantidMuon}/MuonNexusReader.h | 4 +-- .../src/LegacyNexusClasses.cpp | 2 +- Framework/Muon/src/LoadMuonNexus1.cpp | 4 +-- Framework/Muon/src/LoadMuonNexus2.cpp | 2 +- .../{Nexus => Muon}/src/MuonNexusReader.cpp | 2 +- Framework/Nexus/CMakeLists.txt | 8 ++--- 12 files changed, 34 insertions(+), 31 deletions(-) rename Framework/{Nexus/inc/MantidNexus => Muon/inc/MantidMuon}/LegacyNexusClasses.h (97%) rename Framework/{Nexus/inc/MantidNexus => Muon/inc/MantidMuon}/MuonNexusReader.h (98%) rename Framework/{Nexus => Muon}/src/LegacyNexusClasses.cpp (99%) rename Framework/{Nexus => Muon}/src/MuonNexusReader.cpp (99%) diff --git a/Framework/DataHandling/CMakeLists.txt b/Framework/DataHandling/CMakeLists.txt index 0bf5185acb7f..402a32b437d3 100644 --- a/Framework/DataHandling/CMakeLists.txt +++ b/Framework/DataHandling/CMakeLists.txt @@ -684,6 +684,7 @@ target_link_libraries( Mantid::Kernel Mantid::Indexing Mantid::Parallel + Mantid::Muon PRIVATE Mantid::Json Boost::filesystem Mantid::NexusGeometry ) diff --git a/Framework/DataHandling/inc/MantidDataHandling/LoadMuonLog.h b/Framework/DataHandling/inc/MantidDataHandling/LoadMuonLog.h index 72c9783eb5de..cc81f6fbcefb 100644 --- a/Framework/DataHandling/inc/MantidDataHandling/LoadMuonLog.h +++ b/Framework/DataHandling/inc/MantidDataHandling/LoadMuonLog.h @@ -12,7 +12,7 @@ #include "MantidAPI/Algorithm.h" #include "MantidAPI/MatrixWorkspace.h" #include "MantidDataHandling/DllConfig.h" -#include "MantidNexus/MuonNexusReader.h" +#include "MantidMuon/MuonNexusReader.h" namespace Mantid { diff --git a/Framework/DataHandling/src/LoadInstrumentFromNexus.cpp b/Framework/DataHandling/src/LoadInstrumentFromNexus.cpp index 74086f06041e..d19edbae4335 100644 --- a/Framework/DataHandling/src/LoadInstrumentFromNexus.cpp +++ b/Framework/DataHandling/src/LoadInstrumentFromNexus.cpp @@ -15,7 +15,7 @@ #include "MantidGeometry/Instrument/Component.h" #include "MantidGeometry/Instrument/Detector.h" #include "MantidKernel/ConfigService.h" -#include "MantidNexus/MuonNexusReader.h" +#include "MantidMuon/MuonNexusReader.h" #include diff --git a/Framework/Muon/CMakeLists.txt b/Framework/Muon/CMakeLists.txt index 88ebcbbd7344..e2fa68d2107f 100644 --- a/Framework/Muon/CMakeLists.txt +++ b/Framework/Muon/CMakeLists.txt @@ -26,6 +26,8 @@ set(SRC_FILES src/PlotAsymmetryByLogValue.cpp src/RemoveExpDecay.cpp src/RRFMuon.cpp + src/LegacyNexusClasses.cpp + src/MuonNexusReader.cpp ) set(INC_FILES @@ -56,6 +58,8 @@ set(INC_FILES inc/MantidMuon/PlotAsymmetryByLogValue.h inc/MantidMuon/RemoveExpDecay.h inc/MantidMuon/RRFMuon.h + inc/MantidMuon/LegacyNexusClasses.h + inc/MantidMuon/MuonNexusReader.h ) set(TEST_FILES @@ -115,7 +119,7 @@ set_property(TARGET Muon PROPERTY FOLDER "MantidFramework") target_link_libraries( Muon PUBLIC Mantid::API Mantid::Kernel Mantid::Geometry - PRIVATE Mantid::DataObjects Mantid::Indexing Mantid::Nexus + PRIVATE Mantid::DataObjects Mantid::Indexing ) # Add the unit tests directory add_subdirectory(test) diff --git a/Framework/Nexus/inc/MantidNexus/LegacyNexusClasses.h b/Framework/Muon/inc/MantidMuon/LegacyNexusClasses.h similarity index 97% rename from Framework/Nexus/inc/MantidNexus/LegacyNexusClasses.h rename to Framework/Muon/inc/MantidMuon/LegacyNexusClasses.h index 68fd19b12700..e58df5c3224c 100644 --- a/Framework/Nexus/inc/MantidNexus/LegacyNexusClasses.h +++ b/Framework/Muon/inc/MantidMuon/LegacyNexusClasses.h @@ -10,7 +10,7 @@ #include "MantidKernel/TimeSeriesProperty.h" #include "MantidLegacyNexus/NeXusFile_fwd.h" #include "MantidLegacyNexus/napi.h" -#include "MantidNexus/DllConfig.h" +#include "MantidMuon/DllConfig.h" #include #include @@ -62,7 +62,7 @@ const int g_processed_blocksize = 8; /** Nexus attributes. The type of each attribute is NX_CHAR */ -class MANTID_NEXUS_DLL NXAttributes { +class MANTID_MUON_DLL NXAttributes { public: int n() const { return int(m_values.size()); } ///< number of attributes std::vector names() const; ///< Returns the list of attribute names @@ -82,7 +82,7 @@ class NXClass; /** The base abstract class for NeXus classes and data sets. * NX classes and data sets are defined at www.nexusformat.org */ -class MANTID_NEXUS_DLL NXObject { +class MANTID_MUON_DLL NXObject { friend class NXDataSet; ///< a friend class declaration friend class NXClass; ///< a friend class declaration friend class NXRoot; ///< a friend class declaration @@ -124,7 +124,7 @@ class MANTID_NEXUS_DLL NXObject { * There is no need to free the memory allocated by the NXDataSet as it is done * at the destruction. */ -class MANTID_NEXUS_DLL NXDataSet : public NXObject { +class MANTID_MUON_DLL NXDataSet : public NXObject { public: // Constructor NXDataSet(const NXClass &parent, const std::string &name); @@ -476,7 +476,7 @@ using NXUInt = NXDataSetTyped; * (groups) can be created and loaded from * NeXus files. */ -class MANTID_NEXUS_DLL NXClass : public NXObject { +class MANTID_MUON_DLL NXClass : public NXObject { friend class NXRoot; public: @@ -618,7 +618,7 @@ class MANTID_NEXUS_DLL NXClass : public NXObject { /** Implements NXlog Nexus class. */ -class MANTID_NEXUS_DLL NXLog : public NXClass { +class MANTID_MUON_DLL NXLog : public NXClass { public: /** Constructor. * @param parent :: The parent Nexus class. In terms of HDF it is the group @@ -714,7 +714,7 @@ class MANTID_NEXUS_DLL NXLog : public NXClass { /** Implements NXnote Nexus class. */ -class MANTID_NEXUS_DLL NXNote : public NXClass { +class MANTID_MUON_DLL NXNote : public NXClass { public: /** Constructor. * @param parent :: The parent Nexus class. In terms of HDF it is the group @@ -743,7 +743,7 @@ class MANTID_NEXUS_DLL NXNote : public NXClass { /** Implements NXnote Nexus class with binary data. */ -class MANTID_NEXUS_DLL NXBinary : public NXNote { +class MANTID_MUON_DLL NXBinary : public NXNote { public: /** Constructor. * @param parent :: The parent Nexus class. In terms of HDF it is the group @@ -762,7 +762,7 @@ class MANTID_NEXUS_DLL NXBinary : public NXNote { /** Main class is the one that can contain auxiliary classes. */ -class MANTID_NEXUS_DLL NXMainClass : public NXClass { +class MANTID_MUON_DLL NXMainClass : public NXClass { public: /** Constructor. * @param parent :: The parent Nexus class. In terms of HDF it is the group @@ -784,7 +784,7 @@ class MANTID_NEXUS_DLL NXMainClass : public NXClass { /** Implements NXdata Nexus class. */ -class MANTID_NEXUS_DLL NXData : public NXMainClass { +class MANTID_MUON_DLL NXData : public NXMainClass { public: /** Constructor. * @param parent :: The parent Nexus class. In terms of HDF it is the group @@ -826,7 +826,7 @@ class MANTID_NEXUS_DLL NXData : public NXMainClass { /** Implements NXdetector Nexus class. */ -class MANTID_NEXUS_DLL NXDetector : public NXMainClass { +class MANTID_MUON_DLL NXDetector : public NXMainClass { public: /** Constructor. * @param parent :: The parent Nexus class. In terms of HDF it is the group @@ -846,7 +846,7 @@ class MANTID_NEXUS_DLL NXDetector : public NXMainClass { /** Implements NXdisk_chopper Nexus class. */ -class MANTID_NEXUS_DLL NXDiskChopper : public NXMainClass { +class MANTID_MUON_DLL NXDiskChopper : public NXMainClass { public: /** Constructor. * @param parent :: The parent Nexus class. In terms of HDF it is the group @@ -862,7 +862,7 @@ class MANTID_NEXUS_DLL NXDiskChopper : public NXMainClass { /** Implements NXinstrument Nexus class. */ -class MANTID_NEXUS_DLL NXInstrument : public NXMainClass { +class MANTID_MUON_DLL NXInstrument : public NXMainClass { public: /** Constructor. * @param parent :: The parent Nexus class. In terms of HDF it is the group @@ -887,7 +887,7 @@ class MANTID_NEXUS_DLL NXInstrument : public NXMainClass { /** Implements NXentry Nexus class. */ -class MANTID_NEXUS_DLL NXEntry : public NXMainClass { +class MANTID_MUON_DLL NXEntry : public NXMainClass { public: /** Constructor. * @param parent :: The parent Nexus class. In terms of HDF it is the group @@ -911,7 +911,7 @@ class MANTID_NEXUS_DLL NXEntry : public NXMainClass { /** Implements NXroot Nexus class. */ -class MANTID_NEXUS_DLL NXRoot : public NXClass { +class MANTID_MUON_DLL NXRoot : public NXClass { public: // Constructor NXRoot(std::string fname); diff --git a/Framework/Muon/inc/MantidMuon/LoadMuonNexus2.h b/Framework/Muon/inc/MantidMuon/LoadMuonNexus2.h index aa1848ff297e..46d1db5fc55d 100644 --- a/Framework/Muon/inc/MantidMuon/LoadMuonNexus2.h +++ b/Framework/Muon/inc/MantidMuon/LoadMuonNexus2.h @@ -11,8 +11,8 @@ //---------------------------------------------------------------------- #include "MantidDataObjects/Histogram1D.h" #include "MantidMuon/DllConfig.h" +#include "MantidMuon/LegacyNexusClasses.h" #include "MantidMuon/LoadMuonNexus.h" -#include "MantidNexus/LegacyNexusClasses.h" namespace Mantid { diff --git a/Framework/Nexus/inc/MantidNexus/MuonNexusReader.h b/Framework/Muon/inc/MantidMuon/MuonNexusReader.h similarity index 98% rename from Framework/Nexus/inc/MantidNexus/MuonNexusReader.h rename to Framework/Muon/inc/MantidMuon/MuonNexusReader.h index 524fc053fb86..13f8ea8291a6 100644 --- a/Framework/Nexus/inc/MantidNexus/MuonNexusReader.h +++ b/Framework/Muon/inc/MantidMuon/MuonNexusReader.h @@ -7,14 +7,14 @@ #pragma once #include "MantidLegacyNexus/NeXusFile.hpp" -#include "MantidNexus/DllConfig.h" +#include "MantidMuon/DllConfig.h" #include #include // class MuonNexusReader - based on ISISRAW this class implements a simple // reader for Nexus Muon data files. -class MANTID_NEXUS_DLL MuonNexusReader { +class MANTID_MUON_DLL MuonNexusReader { /** @class MuonNexusReader MuonNexusReader.h MuunNexusReader opens a Nexus file and reads certain fields expected for a diff --git a/Framework/Nexus/src/LegacyNexusClasses.cpp b/Framework/Muon/src/LegacyNexusClasses.cpp similarity index 99% rename from Framework/Nexus/src/LegacyNexusClasses.cpp rename to Framework/Muon/src/LegacyNexusClasses.cpp index d1d3df35ceb8..bbca71d4105c 100644 --- a/Framework/Nexus/src/LegacyNexusClasses.cpp +++ b/Framework/Muon/src/LegacyNexusClasses.cpp @@ -4,7 +4,7 @@ // NScD Oak Ridge National Laboratory, European Spallation Source, // Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS // SPDX - License - Identifier: GPL - 3.0 + -#include "MantidNexus/LegacyNexusClasses.h" +#include "MantidMuon/LegacyNexusClasses.h" #include "MantidKernel/Exception.h" #include "MantidKernel/PropertyWithValue.h" diff --git a/Framework/Muon/src/LoadMuonNexus1.cpp b/Framework/Muon/src/LoadMuonNexus1.cpp index 347b58fa286e..24a177573920 100644 --- a/Framework/Muon/src/LoadMuonNexus1.cpp +++ b/Framework/Muon/src/LoadMuonNexus1.cpp @@ -30,8 +30,8 @@ #include "MantidKernel/UnitLabelTypes.h" #include "MantidLegacyNexus/NeXusException.hpp" #include "MantidLegacyNexus/NeXusFile.hpp" -#include "MantidNexus/LegacyNexusClasses.h" -#include "MantidNexus/MuonNexusReader.h" +#include "MantidMuon/LegacyNexusClasses.h" +#include "MantidMuon/MuonNexusReader.h" #include #include diff --git a/Framework/Muon/src/LoadMuonNexus2.cpp b/Framework/Muon/src/LoadMuonNexus2.cpp index d517ff598cea..542d809442d8 100644 --- a/Framework/Muon/src/LoadMuonNexus2.cpp +++ b/Framework/Muon/src/LoadMuonNexus2.cpp @@ -25,7 +25,7 @@ #include "MantidKernel/UnitLabelTypes.h" #include "MantidLegacyNexus/NeXusException.hpp" #include "MantidLegacyNexus/NeXusFile.hpp" -#include "MantidNexus/LegacyNexusClasses.h" +#include "MantidMuon/LegacyNexusClasses.h" #include #include diff --git a/Framework/Nexus/src/MuonNexusReader.cpp b/Framework/Muon/src/MuonNexusReader.cpp similarity index 99% rename from Framework/Nexus/src/MuonNexusReader.cpp rename to Framework/Muon/src/MuonNexusReader.cpp index 35bb64a77e9f..39b2729502ff 100644 --- a/Framework/Nexus/src/MuonNexusReader.cpp +++ b/Framework/Muon/src/MuonNexusReader.cpp @@ -4,7 +4,7 @@ // NScD Oak Ridge National Laboratory, European Spallation Source, // Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS // SPDX - License - Identifier: GPL - 3.0 + -#include "MantidNexus/MuonNexusReader.h" +#include "MantidMuon/MuonNexusReader.h" #include "MantidKernel/Logger.h" #include "MantidLegacyNexus/NeXusException.hpp" #include diff --git a/Framework/Nexus/CMakeLists.txt b/Framework/Nexus/CMakeLists.txt index 16cc36594032..bf0c7823a610 100644 --- a/Framework/Nexus/CMakeLists.txt +++ b/Framework/Nexus/CMakeLists.txt @@ -1,8 +1,6 @@ -set(SRC_FILES src/H5Util.cpp src/MuonNexusReader.cpp src/NexusClasses.cpp src/LegacyNexusClasses.cpp) +set(SRC_FILES src/H5Util.cpp src/NexusClasses.cpp) -set(INC_FILES inc/MantidNexus/H5Util.h inc/MantidNexus/MuonNexusReader.h inc/MantidNexus/NexusClasses.h - inc/MantidNexus/LegacyNexusClasses.h inc/MantidNexus/NexusIOHelper.h -) +set(INC_FILES inc/MantidNexus/H5Util.h inc/MantidNexus/NexusClasses.h inc/MantidNexus/NexusIOHelper.h) set(TEST_FILES H5UtilTest.h NexusIOHelperTest.h) @@ -31,7 +29,7 @@ endif() # Add to the 'Framework' group in VS set_property(TARGET Nexus PROPERTY FOLDER "MantidFramework") -target_link_libraries(Nexus PUBLIC Mantid::API Mantid::DataObjects Mantid::Kernel Mantid::NexusCpp Mantid::LegacyNexus) +target_link_libraries(Nexus PUBLIC Mantid::API Mantid::DataObjects Mantid::Kernel Mantid::NexusCpp) # Add the unit tests directory add_subdirectory(test) From fd08894fc3cdcaa93759cf5d0becd0b82f6d6058 Mon Sep 17 00:00:00 2001 From: Pete Peterson Date: Wed, 19 Feb 2025 14:24:58 -0500 Subject: [PATCH 7/7] Migrate LoadMuonNexus3 over --- Framework/Muon/src/LoadMuonNexus3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Muon/src/LoadMuonNexus3.cpp b/Framework/Muon/src/LoadMuonNexus3.cpp index 5684f84b4ba0..508f3acc3187 100644 --- a/Framework/Muon/src/LoadMuonNexus3.cpp +++ b/Framework/Muon/src/LoadMuonNexus3.cpp @@ -11,7 +11,7 @@ #include "MantidAPI/Progress.h" #include "MantidAPI/RegisterFileLoader.h" #include "MantidKernel/Logger.h" -#include "MantidNexus/NexusClasses.h" +#include "MantidMuon/LegacyNexusClasses.h" #include #include