From 9c9c5b4216766c4aa13b68b9f9131d834ac02022 Mon Sep 17 00:00:00 2001 From: Pete Peterson Date: Mon, 3 Feb 2025 10:20:42 -0500 Subject: [PATCH] Merge pull request #38767 from rosswhitfield/SaveNexusProcessedHelper_napi_to_NeXusFile Migrate SaveNexusProcessedHelper from napi to NeXusFile --- .../inc/MantidDataHandling/LoadNexus.h | 3 + .../SaveNexusProcessedHelper.h | 265 +----- Framework/DataHandling/src/LoadNexus.cpp | 77 +- .../DataHandling/src/SaveNexusProcessed.cpp | 27 +- .../src/SaveNexusProcessedHelper.cpp | 777 +++--------------- .../StartAndEndTimeFromNexusFileExtractor.cpp | 3 +- .../NexusCpp/inc/MantidNexusCpp/NeXusFile.hpp | 2 +- Framework/NexusCpp/src/NeXusFile.cpp | 4 +- .../CMake/CppCheck_Suppressions.txt.in | 2 +- 9 files changed, 243 insertions(+), 917 deletions(-) diff --git a/Framework/DataHandling/inc/MantidDataHandling/LoadNexus.h b/Framework/DataHandling/inc/MantidDataHandling/LoadNexus.h index 61014f5c3132..02b061bd9c05 100644 --- a/Framework/DataHandling/inc/MantidDataHandling/LoadNexus.h +++ b/Framework/DataHandling/inc/MantidDataHandling/LoadNexus.h @@ -66,6 +66,9 @@ class MANTID_DATAHANDLING_DLL LoadNexus final : public API::Algorithm { static const std::string muonTD; static const std::string pulsedTD; + static int getNexusEntryTypes(const std::string &fileName, std::vector &entryName, + std::vector &definition); + private: /// Overwrites Algorithm method. void init() override; diff --git a/Framework/DataHandling/inc/MantidDataHandling/SaveNexusProcessedHelper.h b/Framework/DataHandling/inc/MantidDataHandling/SaveNexusProcessedHelper.h index e0c02655bfb4..a164b68d6d69 100644 --- a/Framework/DataHandling/inc/MantidDataHandling/SaveNexusProcessedHelper.h +++ b/Framework/DataHandling/inc/MantidDataHandling/SaveNexusProcessedHelper.h @@ -9,12 +9,12 @@ #include "MantidAPI/ITableWorkspace_fwd.h" #include "MantidAPI/MatrixWorkspace_fwd.h" #include "MantidAPI/Progress.h" -#include "MantidAPI/Run.h" #include "MantidDataHandling/DllConfig.h" #include "MantidDataObjects/EventList.h" #include "MantidDataObjects/EventWorkspace_fwd.h" #include "MantidKernel/TimeSeriesProperty.h" #include "MantidKernel/cow_ptr.h" +#include "MantidNexusCpp/NeXusException.hpp" #include "MantidNexusCpp/NeXusFile.hpp" #include @@ -28,9 +28,6 @@ namespace Mantid { namespace NeXus { -MANTID_DATAHANDLING_DLL int getNexusEntryTypes(const std::string &fileName, std::vector &entryName, - std::vector &definition); - /** @class NexusFileIO SaveNexusProcessedHelper.h NeXus/SaveNexusProcessedHelper.h Utility method for saving NeXus format of Mantid Workspace @@ -63,8 +60,6 @@ class MANTID_DATAHANDLING_DLL NexusFileIO { void closeNexusFile(); /// Close the group. void closeGroup(); - /// Write a lgos section - int writeNexusSampleLogs(const Mantid::API::Run &runProperties) const; /// write the workspace data int writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr &localworkspace, const bool &uniformSpectra, const bool &raggedSpectra, const std::vector &indices, const char *group_name, @@ -73,28 +68,13 @@ class MANTID_DATAHANDLING_DLL NexusFileIO { /// write table workspace int writeNexusTableWorkspace(const API::ITableWorkspace_const_sptr &itableworkspace, const char *group_name) const; - int writeNexusProcessedDataEvent(const DataObjects::EventWorkspace_const_sptr &ws); - int writeNexusProcessedDataEventCombined(const DataObjects::EventWorkspace_const_sptr &ws, std::vector const &indices, double const *tofs, float const *weights, float const *errorSquareds, int64_t const *pulsetimes, bool compress) const; - int writeEventList(const DataObjects::EventList &el, const std::string &group_name) const; - - template - void writeEventListData(std::vector events, bool writeTOF, bool writePulsetime, bool writeWeight, - bool writeError) const; - void NXwritedata(const char *name, NXnumtype datatype, int rank, int *dims_array, void const *data, - bool compress = false) const; - - /// find size of open entry data section - int getWorkspaceSize(int &numberOfSpectra, int &numberOfChannels, int &numberOfXpoints, bool &uniformBounds, - std::string &axesUnits, std::string &yUnits) const; - /// read X values for one (or the generic if uniform) spectra - int getXValues(MantidVec &xValues, const int &spectra) const; - /// read values and errors for spectra - int getSpectra(MantidVec &values, MantidVec &errors, const int &spectra) const; + void writeData(const char *name, NXnumtype datatype, std::vector dims_array, void const *data, + bool compress = false) const; /// write bin masking information bool writeNexusBinMasking(const API::MatrixWorkspace_const_sptr &ws) const; @@ -103,80 +83,24 @@ class MANTID_DATAHANDLING_DLL NexusFileIO { void resetProgress(Mantid::API::Progress *prog); /// Nexus file handle - NXhandle fileID; + std::shared_ptr<::NeXus::File> filehandle() const { return m_filehandle; } private: /// C++ API file handle std::shared_ptr<::NeXus::File> m_filehandle; /// Nexus compression method - int m_nexuscompression; + ::NeXus::NXcompression m_nexuscompression; /// Allow an externally supplied progress object to be used API::Progress *m_progress; /// Write a simple value plus possible attributes - template - bool writeNxValue(const std::string &name, const TYPE &value, const NXnumtype nxType, - const std::vector &attributes, const std::vector &avalues) const; - /// Returns true if the given property is a time series property - bool isTimeSeries(Kernel::Property *prop) const; - /// Write a time series log entry - bool writeTimeSeriesLog(Kernel::Property *prop) const; - /// Write a single value log entry - bool writeSingleValueLog(Kernel::Property *prop) const; - /// Writes a numeric log to the Nexus file - template void writeNumericTimeLog(const Kernel::TimeSeriesProperty *timeSeries) const; - /// Write a single valued NXLog entry to the Nexus file - template - bool writeSingleValueNXLog(const std::string &name, const TYPE &value, const NXnumtype nxType, - const std::vector &attributes, const std::vector &avalues) const; - /// write an NXnote with standard fields (but NX_CHAR rather than NX_BINARY - /// data) - bool writeNxNote(const std::string ¬eName, const std::string &author, const std::string &date, - const std::string &description, const std::string &pairValues) const; - /// write a float array along with any defined attributes - void writeNxFloatArray(const std::string &name, const std::vector &values, - const std::vector &attributes, const std::vector &avalues) const; - /// write a char array along with any defined attributes - bool writeNxStringArray(const std::string &name, const std::vector &values, - const std::vector &attributes, const std::vector &avalues) const; - /// Write NXlog data for given string TimeSeriesProperty - void writeNumericTimeLog_String(const Kernel::TimeSeriesProperty *s_timeSeries) const; - /// check if the gievn item exists in the current level - bool checkEntryAtLevel(const std::string &item) const; - /// check if given attribute name is in currently opened entry - bool checkAttributeName(const std::string &target) const; - /// Look for entry with given attribute (eg "signal") - bool checkEntryAtLevelByAttribute(const std::string &attribute, std::string &entry) const; + bool writeNxValue(const std::string &name, const std::string &value, const std::vector &attributes, + const std::vector &avalues) const; /// search for exisiting MantidWorkpace_n entries in opened file int findMantidWSEntries() const; - /// convert posix time to time_t - std::time_t to_time_t(const boost::posix_time::ptime &t) ///< convert posix time to time_t - { - /** - Take the input Posix time, subtract the unix epoch, and return the seconds - as a std::time_t value. - @param t :: time of interest as ptime - @return :: time_t value of t - */ - if (t == boost::posix_time::neg_infin) - return 0; - else if (t == boost::posix_time::pos_infin) - return LONG_MAX; - boost::posix_time::ptime start(boost::gregorian::date(1970, 1, 1)); - return (t - start).total_seconds(); - } /// nexus file name std::string m_filename; - /** Writes a numeric log to the Nexus file - * @tparam T A numeric type (double, int, bool) - * @param timeSeries :: A pointer to the log property - */ - template void writeNexusTimeLog(const Kernel::TimeSeriesProperty *timeSeries) const; - - /// Return the log value type as string - template std::string logValueType() const { return "unknown"; } - /// Writes given vector column to the currently open Nexus file template void writeNexusVectorColumn(const API::Column_const_sptr &col, const std::string &columnName, NXnumtype nexusType, @@ -188,190 +112,35 @@ class MANTID_DATAHANDLING_DLL NexusFileIO { const std::string &columnName) const; }; -/** - * Write a single valued entry to the Nexus file - * @param name :: The name of the entry - * @param value :: The value of the entry - * @param nxType :: The nxType of the entry - * @param attributes :: A list of attributes 1:1 mapped to their values in the - * avalues argument - * @param avalues :: A list of attribute values in the same order as the - * attributes argument - * @returns A boolean indicating success or failure - */ -template -bool NexusFileIO::writeNxValue(const std::string &name, const TYPE &value, const NXnumtype nxType, - const std::vector &attributes, - const std::vector &avalues) const { - int dimensions[1] = {1}; - if (NXmakedata(fileID, name.c_str(), nxType, 1, dimensions) == NXstatus::NX_ERROR) - return false; - if (NXopendata(fileID, name.c_str()) == NXstatus::NX_ERROR) - return false; - for (unsigned int it = 0; it < attributes.size(); ++it) { - NXputattr(fileID, attributes[it].c_str(), static_cast(avalues[it].c_str()), - static_cast(avalues[it].size() + 1), NXnumtype::CHAR); - } - NXputdata(fileID, static_cast(&value)); - NXclosedata(fileID); - return true; -} - /** * Write a single valued entry to the Nexus file (specialization for a string) * @param name :: The name of the entry * @param value :: The value of the entry - * @param nxType :: The nxType of the entry * @param attributes :: A list of attributes 1:1 mapped to their values in the * avalues argument * @param avalues :: A list of attribute values in the same order as the * attributes argument * @returns A boolean indicating success or failure */ -template <> -inline bool NexusFileIO::writeNxValue(const std::string &name, const std::string &value, const NXnumtype nxType, +inline bool NexusFileIO::writeNxValue(const std::string &name, const std::string &value, const std::vector &attributes, const std::vector &avalues) const { - (void)nxType; - int dimensions[1] = {0}; - std::string nxstr = value; - if (nxstr.empty()) - nxstr += " "; - dimensions[0] = static_cast(nxstr.size() + 1); - if (NXmakedata(fileID, name.c_str(), nxType, 1, dimensions) == NXstatus::NX_ERROR) - return false; - if (NXopendata(fileID, name.c_str()) == NXstatus::NX_ERROR) - return false; - for (unsigned int it = 0; it < attributes.size(); ++it) { - NXputattr(fileID, attributes[it].c_str(), avalues[it].c_str(), static_cast(avalues[it].size() + 1), - NXnumtype::CHAR); - } - NXputdata(fileID, reinterpret_cast(const_cast(nxstr.c_str()))); - NXclosedata(fileID); - return true; -} + try { + m_filehandle->writeData(name, value); -/** - * Write a single valued NXLog entry to the Nexus file - * @param name :: The name of the entry - * @param value :: The value of the entry - * @param nxType :: The nxType of the entry - * @param attributes :: A list of attributes 1:1 mapped to their values in the - * avalues argument - * @param avalues :: A list of attribute values in the same order as the - * attributes argument - * @returns A boolean indicating success or failure - */ -template -bool NexusFileIO::writeSingleValueNXLog(const std::string &name, const TYPE &value, const NXnumtype nxType, - const std::vector &attributes, - const std::vector &avalues) const { - if (NXmakegroup(fileID, name.c_str(), "NXlog") == NXstatus::NX_ERROR) - return false; - NXopengroup(fileID, name.c_str(), "NXlog"); - int dimensions[1] = {1}; - if (NXmakedata(fileID, "value", nxType, 1, dimensions) == NXstatus::NX_ERROR) - return false; - if (NXopendata(fileID, "value") == NXstatus::NX_ERROR) + // open it again to add attributes + m_filehandle->openData(name); + for (unsigned int it = 0; it < attributes.size(); ++it) { + m_filehandle->putAttr(attributes[it], avalues[it]); + } + m_filehandle->closeData(); + } catch (::NeXus::Exception &) { return false; - for (unsigned int it = 0; it < attributes.size(); ++it) { - NXputattr(fileID, attributes[it].c_str(), static_cast(avalues[it].c_str()), - static_cast(avalues[it].size() + 1), NXnumtype::CHAR); } - NXputdata(fileID, static_cast(&value)); - NXclosedata(fileID); - NXclosegroup(fileID); - return true; -} -/** - * Write a single valued NXLog entry to the Nexus file (specialization for a - * string) - * @param name :: The name of the entry - * @param value :: The value of the entry - * @param nxType :: The nxType of the entry - * @param attributes :: A list of attributes 1:1 mapped to their values in the - * avalues argument - * @param avalues :: A list of attribute values in the same order as the - * attributes argument - * @returns A boolean indicating success or failure - */ -template <> -inline bool NexusFileIO::writeSingleValueNXLog(const std::string &name, const std::string &value, - const NXnumtype nxType, const std::vector &attributes, - const std::vector &avalues) const { - (void)nxType; - if (NXmakegroup(fileID, name.c_str(), "NXlog") == NXstatus::NX_ERROR) - return false; - NXopengroup(fileID, name.c_str(), "NXlog"); - int dimensions[1] = {0}; - std::string nxstr = value; - if (nxstr.empty()) - nxstr += " "; - dimensions[0] = static_cast(nxstr.size() + 1); // Allow for null-terminator - if (NXmakedata(fileID, "value", NXnumtype::CHAR, 1, dimensions) == NXstatus::NX_ERROR) - return false; - if (NXopendata(fileID, "value") == NXstatus::NX_ERROR) - return false; - for (unsigned int it = 0; it < attributes.size(); ++it) { - NXputattr(fileID, attributes[it].c_str(), reinterpret_cast(const_cast(avalues[it].c_str())), - static_cast(avalues[it].size() + 1), NXnumtype::CHAR); - } - NXputdata(fileID, reinterpret_cast(const_cast(nxstr.c_str()))); - NXclosedata(fileID); - NXclosegroup(fileID); return true; } -/** Writes a numeric log to the Nexus file - * @tparam T A numeric type (double, int, bool) - * @param timeSeries :: A pointer to the log property - */ -template void NexusFileIO::writeNumericTimeLog(const Kernel::TimeSeriesProperty *timeSeries) const { - // write NXlog section for double values - NXstatus status; - // get a name for the log, possibly removing the path component - std::string logName = timeSeries->name(); - size_t ipos = logName.find_last_of("/\\"); - if (ipos != std::string::npos) - logName = logName.substr(ipos + 1); - // extract values from timeseries - std::map dV = timeSeries->valueAsMap(); - std::vector values; - std::vector times; - Types::Core::DateAndTime t0; - bool first = true; - for (typename std::map::const_iterator dv = dV.begin(); dv != dV.end(); ++dv) { - T val = dv->second; - Types::Core::DateAndTime time = dv->first; - values.emplace_back(val); - if (first) { - t0 = time; // start time of log - first = false; - } - times.emplace_back(Types::Core::DateAndTime::secondsFromDuration(time - t0)); - } - // create log - status = NXmakegroup(fileID, logName.c_str(), "NXlog"); - if (status == NXstatus::NX_ERROR) - return; - - NXopengroup(fileID, logName.c_str(), "NXlog"); - // write log data - std::vector attributes, avalues; - attributes.emplace_back("type"); - avalues.emplace_back(logValueType()); - writeNxFloatArray("value", values, attributes, avalues); - attributes.clear(); - avalues.clear(); - // get ISO time, and save it as an attribute - attributes.emplace_back("start"); - avalues.emplace_back(t0.toISO8601String()); - - writeNxFloatArray("time", times, attributes, avalues); - NXclosegroup(fileID); -} - /// Helper typedef for a shared pointer of a NexusFileIO. using NexusFileIO_sptr = std::shared_ptr; diff --git a/Framework/DataHandling/src/LoadNexus.cpp b/Framework/DataHandling/src/LoadNexus.cpp index 310dafb06640..6eaa6ce8255d 100644 --- a/Framework/DataHandling/src/LoadNexus.cpp +++ b/Framework/DataHandling/src/LoadNexus.cpp @@ -15,11 +15,12 @@ #include "MantidDataHandling/LoadNexus.h" #include "MantidAPI/FileProperty.h" #include "MantidAPI/WorkspaceGroup.h" -#include "MantidDataHandling/SaveNexusProcessedHelper.h" #include "MantidDataObjects/Workspace2D.h" #include "MantidKernel/ArrayProperty.h" #include "MantidKernel/BoundedValidator.h" #include "MantidNexus/NexusClasses.h" +#include "MantidNexusCpp/NeXusException.hpp" +#include "MantidNexusCpp/NeXusFile.hpp" #include #include @@ -36,6 +37,10 @@ using namespace Kernel; using namespace API; using namespace DataObjects; +namespace { +const std::string NULL_STR("NULL"); +} + /// Empty default constructor LoadNexus::LoadNexus() : Algorithm(), m_filename() {} @@ -84,7 +89,7 @@ void LoadNexus::exec() { // documentation of this algorithm. std::vector entryName, definition; - int count = Mantid::NeXus::getNexusEntryTypes(m_filename, entryName, definition); + int count = getNexusEntryTypes(m_filename, entryName, definition); if (count <= -1) { g_log.error("Error reading file " + m_filename); throw Exception::FileError("Unable to read data in File:", m_filename); @@ -258,4 +263,72 @@ void LoadNexus::setOutputWorkspace(const API::IAlgorithm_sptr &loader) { } } +/** Get all the Nexus entry types for a file + * + * Try to open named Nexus file and return all entries plus the definition found + *for each. + * If definition not found, try and return "analysis" field (Muon V1 files) + * Closes file on exit. + * + * @param fileName :: file to open + * @param entryName :: vector that gets filled with strings with entry names + * @param definition :: vector that gets filled with the "definition" or + *"analysis" string. + * @return count of entries if OK, -1 failed to open file. + */ +int LoadNexus::getNexusEntryTypes(const std::string &fileName, std::vector &entryName, + std::vector &definition) { + std::unique_ptr<::NeXus::File> fileH; + + try { + fileH = std::make_unique<::NeXus::File>(fileName); + } catch (::NeXus::Exception &) { + return -1; + } + entryName.clear(); + definition.clear(); + + // + // Loop through all entries looking for the definition section in each (or + // analysis for MuonV1) + // + std::vector entryList; + + std::pair entry; + while (true) { + entry = fileH->getNextEntry(); + if (entry.first == NULL_STR && entry.second == NULL_STR) + break; + + if (entry.second == "NXentry") + entryList.emplace_back(entry.first); + } + + // for each entry found, look for "analysis" or "definition" text data fields + // and return value plus entry name + + for (auto &item : entryList) { + fileH->openGroup(item, "NXentry"); + // loop through field names in this entry + while (true) { + entry = fileH->getNextEntry(); + if (entry.first == NULL_STR && entry.second == NULL_STR) + break; + // if a data field + if (entry.second == "SDS") { + // if one of the two names we are looking for + if (entry.first == "definition" || entry.first == "analysis") { + std::string value; + fileH->readData(entry.first, value); + definition.emplace_back(value); + entryName.emplace_back(item); + } + } + } + fileH->closeGroup(); + } + + return (static_cast(entryName.size())); +} + } // namespace Mantid::DataHandling diff --git a/Framework/DataHandling/src/SaveNexusProcessed.cpp b/Framework/DataHandling/src/SaveNexusProcessed.cpp index 3f1d09526ed4..f2c906d18363 100644 --- a/Framework/DataHandling/src/SaveNexusProcessed.cpp +++ b/Framework/DataHandling/src/SaveNexusProcessed.cpp @@ -269,9 +269,6 @@ void SaveNexusProcessed::doExec(const Workspace_sptr &inputWorkspace, nexusFile->resetProgress(&prog_init); nexusFile->openNexusWrite(filename, entryNumber, append_to_file || keepFile); - // Equivalent C++ API handle - ::NeXus::File cppFile(nexusFile->fileID); - prog_init.reportIncrement(1, "Opening file"); if (nexusFile->writeNexusProcessedHeader(title, wsName) != 0) throw Exception::FileError("Failed to write to file", filename); @@ -281,7 +278,7 @@ void SaveNexusProcessed::doExec(const Workspace_sptr &inputWorkspace, // write instrument data, if present and writer enabled if (matrixWorkspace) { // Save the instrument names, ParameterMap, sample, run - matrixWorkspace->saveExperimentInfoNexus(&cppFile, saveLegacyInstrument()); + matrixWorkspace->saveExperimentInfoNexus(nexusFile->filehandle().get(), saveLegacyInstrument()); prog_init.reportIncrement(1, "Writing sample and instrument"); // check if all X() are in fact the same array @@ -312,23 +309,23 @@ void SaveNexusProcessed::doExec(const Workspace_sptr &inputWorkspace, } if (saveLegacyInstrument()) { - cppFile.openGroup("instrument", "NXinstrument"); - cppFile.makeGroup("detector", "NXdetector", true); - - cppFile.putAttr("version", 1); - saveSpectraDetectorMapNexus(*matrixWorkspace, &cppFile, indices, ::NeXus::LZW); - saveSpectrumNumbersNexus(*matrixWorkspace, &cppFile, indices, ::NeXus::LZW); - cppFile.closeGroup(); - cppFile.closeGroup(); + nexusFile->filehandle()->openGroup("instrument", "NXinstrument"); + nexusFile->filehandle()->makeGroup("detector", "NXdetector", true); + + nexusFile->filehandle()->putAttr("version", 1); + saveSpectraDetectorMapNexus(*matrixWorkspace, nexusFile->filehandle().get(), indices, ::NeXus::LZW); + saveSpectrumNumbersNexus(*matrixWorkspace, nexusFile->filehandle().get(), indices, ::NeXus::LZW); + nexusFile->filehandle()->closeGroup(); + nexusFile->filehandle()->closeGroup(); } } // finish matrix workspace specifics if (peaksWorkspace) { // Save the instrument names, ParameterMap, sample, run - peaksWorkspace->saveExperimentInfoNexus(&cppFile); + peaksWorkspace->saveExperimentInfoNexus(nexusFile->filehandle().get()); prog_init.reportIncrement(1, "Writing sample and instrument"); - peaksWorkspace->saveNexus(&cppFile); + peaksWorkspace->saveNexus(nexusFile->filehandle().get()); } else if (tableWorkspace) { nexusFile->writeNexusTableWorkspace(tableWorkspace, "table_workspace"); } @@ -346,7 +343,7 @@ void SaveNexusProcessed::doExec(const Workspace_sptr &inputWorkspace, } } - inputWorkspace->history().saveNexus(&cppFile); + inputWorkspace->history().saveNexus(nexusFile->filehandle().get()); nexusFile->closeGroup(); } diff --git a/Framework/DataHandling/src/SaveNexusProcessedHelper.cpp b/Framework/DataHandling/src/SaveNexusProcessedHelper.cpp index f45c29fbe47f..a6cc952a9453 100644 --- a/Framework/DataHandling/src/SaveNexusProcessedHelper.cpp +++ b/Framework/DataHandling/src/SaveNexusProcessedHelper.cpp @@ -21,13 +21,6 @@ #include "MantidDataObjects/PeaksWorkspace.h" #include "MantidDataObjects/RebinnedOutput.h" #include "MantidDataObjects/TableWorkspace.h" -#include "MantidDataObjects/Workspace2D.h" -#include "MantidGeometry/Instrument.h" -#include "MantidKernel/ArrayProperty.h" -#include "MantidKernel/TimeSeriesProperty.h" -#include "MantidKernel/Unit.h" -#include "MantidKernel/UnitFactory.h" -#include "MantidKernel/VectorHelper.h" #include #include @@ -43,12 +36,11 @@ Logger g_log("NexusFileIO"); } // namespace /// Empty default constructor -NexusFileIO::NexusFileIO() - : fileID(), m_filehandle(), m_nexuscompression(NX_COMP_LZW), m_progress(nullptr), m_filename() {} +NexusFileIO::NexusFileIO() : m_filehandle(), m_nexuscompression(::NeXus::LZW), m_progress(nullptr), m_filename() {} /// Constructor that supplies a progress object NexusFileIO::NexusFileIO(Progress *prog) - : fileID(), m_filehandle(), m_nexuscompression(NX_COMP_LZW), m_progress(prog), m_filename() {} + : m_filehandle(), m_nexuscompression(::NeXus::LZW), m_progress(prog), m_filename() {} void NexusFileIO::resetProgress(Progress *prog) { m_progress = prog; } @@ -97,7 +89,7 @@ void NexusFileIO::openNexusWrite(const std::string &fileName, NexusFileIO::optio else { if (fileName.find(".xml") < fileName.size() || fileName.find(".XML") < fileName.size()) { mode = NXACC_CREATEXML; - m_nexuscompression = NX_COMP_NONE; + m_nexuscompression = ::NeXus::NONE; } mantidEntryName = "mantid_workspace_1"; } @@ -113,14 +105,7 @@ void NexusFileIO::openNexusWrite(const std::string &fileName, NexusFileIO::optio throw Exception::FileError(message, fileName); } - // open the file and copy the handle into the NeXus::File object - NXstatus status = NXopen(fileName.c_str(), mode, &fileID); - if (status == NXstatus::NX_ERROR) { - g_log.error("Unable to open file " + fileName); - throw Exception::FileError("Unable to open File:", fileName); - } - - auto file = new ::NeXus::File(fileID, true); + auto file = new ::NeXus::File(fileName, mode); m_filehandle = std::shared_ptr<::NeXus::File>(file); } @@ -180,12 +165,12 @@ int NexusFileIO::writeNexusProcessedHeader(const std::string &title, const std:: std::vector attributes, avalues; attributes.reserve(2); avalues.reserve(2); - if (!writeNxValue("title", title, NXnumtype::CHAR, attributes, avalues)) + if (!writeNxValue("title", title, attributes, avalues)) return (3); // name for workspace if this is a multi workspace nexus file if (!wsName.empty()) { - if (!writeNxValue("workspace_name", wsName, NXnumtype::CHAR, attributes, avalues)) + if (!writeNxValue("workspace_name", wsName, attributes, avalues)) return (3); } @@ -194,87 +179,16 @@ int NexusFileIO::writeNexusProcessedHeader(const std::string &title, const std:: attributes.emplace_back("Version"); avalues.emplace_back("1.0"); // this may not be the "correct" long term path, but it is valid at present - if (!writeNxValue("definition", className, NXnumtype::CHAR, attributes, avalues)) + if (!writeNxValue("definition", className, attributes, avalues)) return (3); avalues.clear(); avalues.emplace_back("http://www.isis.rl.ac.uk/xml/IXmantid.xml"); avalues.emplace_back("1.0"); - if (!writeNxValue("definition_local", className, NXnumtype::CHAR, attributes, avalues)) + if (!writeNxValue("definition_local", className, attributes, avalues)) return (3); return (0); } -//----------------------------------------------------------------------------------------------- -// -// write an NXdata entry with Float array values -// -void NexusFileIO::writeNxFloatArray(const std::string &name, const std::vector &values, - const std::vector &attributes, - const std::vector &avalues) const { - m_filehandle->writeData(name, values); - m_filehandle->openData(name); - for (size_t it = 0; it < attributes.size(); ++it) - m_filehandle->putAttr(attributes[it], avalues[it]); - m_filehandle->closeData(); -} - -//----------------------------------------------------------------------------------------------- -// -// write an NXdata entry with String array values -// -bool NexusFileIO::writeNxStringArray(const std::string &name, const std::vector &values, - const std::vector &attributes, - const std::vector &avalues) const { - int dimensions[2]; - const auto maxlen = std::max_element(values.cbegin(), values.cend(), - [](const auto &lhs, const auto &rhs) { return lhs.size() < rhs.size(); }); - dimensions[0] = static_cast(values.size()); - dimensions[1] = static_cast(maxlen->size()); - NXstatus status = NXmakedata(fileID, name.c_str(), NXnumtype::CHAR, 2, dimensions); - if (status == NXstatus::NX_ERROR) - return (false); - NXopendata(fileID, name.c_str()); - for (size_t it = 0; it < attributes.size(); ++it) - NXputattr(fileID, attributes[it].c_str(), avalues[it].c_str(), static_cast(avalues[it].size() + 1), - NXnumtype::CHAR); - auto strs = new char[values.size() * maxlen->size()]; - for (size_t i = 0; i < values.size(); i++) { - strncpy(&strs[i * maxlen->size()], values[i].c_str(), maxlen->size()); - } - NXputdata(fileID, strs); - NXclosedata(fileID); - delete[] strs; - return (true); -} -// -// Write an NXnote entry with data giving parameter pair values for algorithm -// history and environment -// Use NXnumtype::CHAR instead of NX_BINARY for the parameter values to make more -// simple. -// -bool NexusFileIO::writeNxNote(const std::string ¬eName, const std::string &author, const std::string &date, - const std::string &description, const std::string &pairValues) const { - m_filehandle->makeGroup(noteName, "NXnote", true); - - std::vector attributes, avalues; - if (!date.empty()) { - attributes.emplace_back("date"); - avalues.emplace_back(date); - } - if (!writeNxValue("author", author, NXnumtype::CHAR, attributes, avalues)) - return (false); - attributes.clear(); - avalues.clear(); - - if (!writeNxValue("description", description, NXnumtype::CHAR, attributes, avalues)) - return (false); - if (!writeNxValue("data", pairValues, NXnumtype::CHAR, attributes, avalues)) - return (false); - - m_filehandle->closeGroup(); - return (true); -} - //------------------------------------------------------------------------------------- /** Write out a MatrixWorkspace's data as a 2D matrix. * Use writeNexusProcessedDataEvent if writing an EventWorkspace. @@ -283,13 +197,15 @@ int NexusFileIO::writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr const bool &uniformSpectra, const bool &raggedSpectra, const std::vector &indices, const char *group_name, bool write2Ddata) const { - NXstatus status; + // NXstatus status; // write data entry - status = NXmakegroup(fileID, group_name, "NXdata"); - if (status == NXstatus::NX_ERROR) - return (2); - NXopengroup(fileID, group_name, "NXdata"); + try { + m_filehandle->makeGroup(group_name, "NXdata", true); + } catch (const ::NeXus::Exception &) { + return 2; + } + // write workspace data const size_t nHist = localworkspace->getNumberHistograms(); if (nHist < 1) @@ -299,7 +215,7 @@ int NexusFileIO::writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr if (raggedSpectra) for (size_t i = 0; i < nSpect; i++) nSpectBins = std::max(nSpectBins, localworkspace->y(indices[i]).size()); - int dims_array[2] = {static_cast(nSpect), static_cast(nSpectBins)}; + std::vector dims_array = {static_cast(nSpect), static_cast(nSpectBins)}; // Set the axis labels and values Mantid::API::Axis *xAxis = localworkspace->getAxis(0); @@ -330,38 +246,32 @@ int NexusFileIO::writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr for (size_t i = 0; i < sAxis->length(); i++) axis2.emplace_back((*sAxis)(i)); - int start[2] = {0, 0}; - int asize[2] = {1, dims_array[1]}; + std::vector start = {0, 0}; + std::vector asize = {1, dims_array[1]}; // -------------- Actually write the 2D data ---------------------------- if (write2Ddata) { std::string name = "values"; - NXcompmakedata(fileID, name.c_str(), NXnumtype::FLOAT64, 2, dims_array, m_nexuscompression, asize); - NXopendata(fileID, name.c_str()); + m_filehandle->makeCompData(name, NXnumtype::FLOAT64, dims_array, m_nexuscompression, asize, true); for (size_t i = 0; i < nSpect; i++) { - NXputslab(fileID, localworkspace->y(indices[i]).rawData().data(), start, asize); + m_filehandle->putSlab(localworkspace->y(indices[i]).rawData().data(), start, asize); start[0]++; } if (m_progress != nullptr) m_progress->reportIncrement(1, "Writing data"); - int signal = 1; - NXputattr(fileID, "signal", &signal, 1, NXnumtype::INT32); + m_filehandle->putAttr("signal", 1); // More properties - const std::string axesNames = "axis2,axis1"; - NXputattr(fileID, "axes", axesNames.c_str(), static_cast(axesNames.size()), NXnumtype::CHAR); - std::string yUnits = localworkspace->YUnit(); - std::string yUnitLabel = localworkspace->YUnitLabel(); - NXputattr(fileID, "units", yUnits.c_str(), static_cast(yUnits.size()), NXnumtype::CHAR); - NXputattr(fileID, "unit_label", yUnitLabel.c_str(), static_cast(yUnitLabel.size()), NXnumtype::CHAR); - NXclosedata(fileID); + m_filehandle->putAttr("axes", "axis2,axis1"); + m_filehandle->putAttr("units", localworkspace->YUnit(), false); + m_filehandle->putAttr("unit_label", localworkspace->YUnitLabel(), false); + m_filehandle->closeData(); // error name = "errors"; - NXcompmakedata(fileID, name.c_str(), NXnumtype::FLOAT64, 2, dims_array, m_nexuscompression, asize); - NXopendata(fileID, name.c_str()); + m_filehandle->makeCompData(name, NXnumtype::FLOAT64, dims_array, m_nexuscompression, asize, true); start[0] = 0; for (size_t i = 0; i < nSpect; i++) { - NXputslab(fileID, localworkspace->e(indices[i]).rawData().data(), start, asize); + m_filehandle->putSlab(localworkspace->e(indices[i]).rawData().data(), start, asize); start[0]++; } @@ -372,18 +282,17 @@ int NexusFileIO::writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr if (localworkspace->id() == "RebinnedOutput") { RebinnedOutput_const_sptr rebin_workspace = std::dynamic_pointer_cast(localworkspace); name = "frac_area"; - NXcompmakedata(fileID, name.c_str(), NXnumtype::FLOAT64, 2, dims_array, m_nexuscompression, asize); - NXopendata(fileID, name.c_str()); + m_filehandle->makeCompData(name, NXnumtype::FLOAT64, dims_array, m_nexuscompression, asize, true); start[0] = 0; for (size_t i = 0; i < nSpect; i++) { - NXputslab(fileID, rebin_workspace->readF(indices[i]).data(), start, asize); + m_filehandle->putSlab(rebin_workspace->readF(indices[i]).data(), start, asize); start[0]++; } std::string finalized = (rebin_workspace->isFinalized()) ? "1" : "0"; - NXputattr(fileID, "finalized", finalized.c_str(), 2, NXnumtype::CHAR); + m_filehandle->putAttr("finalized", finalized); std::string sqrdErrs = (rebin_workspace->hasSqrdErrors()) ? "1" : "0"; - NXputattr(fileID, "sqrd_errors", sqrdErrs.c_str(), 2, NXnumtype::CHAR); + m_filehandle->putAttr("sqrd_errors", sqrdErrs); if (m_progress != nullptr) m_progress->reportIncrement(1, "Writing data"); @@ -394,18 +303,16 @@ int NexusFileIO::writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr dims_array[0] = static_cast(nSpect); dims_array[1] = static_cast(localworkspace->dx(0).size()); std::string dxErrorName = "xerrors"; - NXcompmakedata(fileID, dxErrorName.c_str(), NXnumtype::FLOAT64, 2, dims_array, m_nexuscompression, asize); - NXopendata(fileID, dxErrorName.c_str()); + m_filehandle->makeCompData(dxErrorName, NXnumtype::FLOAT64, dims_array, m_nexuscompression, asize, true); start[0] = 0; asize[1] = dims_array[1]; for (size_t i = 0; i < nSpect; i++) { - - NXputslab(fileID, localworkspace->dx(indices[i]).rawData().data(), start, asize); + m_filehandle->putSlab(localworkspace->dx(indices[i]).rawData().data(), start, asize); start[0]++; } } - NXclosedata(fileID); + m_filehandle->closeData(); } // write X data, as single array or all values if "ragged" @@ -415,8 +322,7 @@ int NexusFileIO::writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr max_x_size = std::max(max_x_size, localworkspace->x(indices[i]).size()); dims_array[0] = static_cast(nSpect); dims_array[1] = static_cast(max_x_size); - NXmakedata(fileID, "axis1", NXnumtype::FLOAT64, 2, dims_array); - NXopendata(fileID, "axis1"); + m_filehandle->makeData("axis1", NXnumtype::FLOAT64, dims_array, true); start[0] = 0; // create vector of NaNs to fill invalid space at end of ragged array @@ -425,90 +331,82 @@ int NexusFileIO::writeNexusProcessedData2D(const API::MatrixWorkspace_const_sptr for (size_t i = 0; i < nSpect; i++) { size_t nBins = localworkspace->x(indices[i]).size(); asize[1] = static_cast(nBins); - NXputslab(fileID, localworkspace->x(indices[i]).rawData().data(), start, asize); + m_filehandle->putSlab(localworkspace->x(indices[i]).rawData().data(), start, asize); if (nBins < max_x_size) { start[1] = asize[1]; asize[1] = static_cast(max_x_size - nBins); - NXputslab(fileID, nans.data(), start, asize); + m_filehandle->putSlab(nans.data(), start, asize); start[1] = 0; } start[0]++; } } else if (uniformSpectra) { - dims_array[0] = static_cast(localworkspace->x(0).size()); - NXmakedata(fileID, "axis1", NXnumtype::FLOAT64, 1, dims_array); - NXopendata(fileID, "axis1"); - NXputdata(fileID, localworkspace->x(0).rawData().data()); + m_filehandle->makeData("axis1", NXnumtype::FLOAT64, std::vector{static_cast(localworkspace->x(0).size())}, + true); + m_filehandle->putData(localworkspace->x(0).rawData().data()); } else { dims_array[0] = static_cast(nSpect); dims_array[1] = static_cast(localworkspace->x(0).size()); - NXmakedata(fileID, "axis1", NXnumtype::FLOAT64, 2, dims_array); - NXopendata(fileID, "axis1"); + m_filehandle->makeData("axis1", NXnumtype::FLOAT64, dims_array, true); start[0] = 0; asize[1] = dims_array[1]; for (size_t i = 0; i < nSpect; i++) { - NXputslab(fileID, localworkspace->x(indices[i]).rawData().data(), start, asize); + m_filehandle->putSlab(localworkspace->x(indices[i]).rawData().data(), start, asize); start[0]++; } } std::string dist = (localworkspace->isDistribution()) ? "1" : "0"; - NXputattr(fileID, "distribution", dist.c_str(), 2, NXnumtype::CHAR); - NXputattr(fileID, "units", xLabel.c_str(), static_cast(xLabel.size()), NXnumtype::CHAR); + m_filehandle->putAttr("distribution", dist); + m_filehandle->putAttr("units", xLabel); auto label = std::dynamic_pointer_cast(xAxis->unit()); if (label) { - NXputattr(fileID, "caption", label->caption().c_str(), static_cast(label->caption().size()), NXnumtype::CHAR); + m_filehandle->putAttr("caption", label->caption(), false); auto unitLbl = label->label(); - NXputattr(fileID, "label", unitLbl.ascii().c_str(), static_cast(unitLbl.ascii().size()), NXnumtype::CHAR); + m_filehandle->putAttr("label", unitLbl.ascii(), false); } - NXclosedata(fileID); + m_filehandle->closeData(); if (!sAxis->isText()) { // write axis2, maybe just spectra number - dims_array[0] = static_cast(axis2.size()); - NXmakedata(fileID, "axis2", NXnumtype::FLOAT64, 1, dims_array); - NXopendata(fileID, "axis2"); - NXputdata(fileID, axis2.data()); - NXputattr(fileID, "units", sLabel.c_str(), static_cast(sLabel.size()), NXnumtype::CHAR); + m_filehandle->makeData("axis2", NXnumtype::FLOAT64, std::vector{static_cast(axis2.size())}, true); + m_filehandle->putData(axis2.data()); + m_filehandle->putAttr("units", sLabel, false); auto unitLabel = std::dynamic_pointer_cast(sAxis->unit()); if (unitLabel) { - NXputattr(fileID, "caption", unitLabel->caption().c_str(), static_cast(unitLabel->caption().size()), - NXnumtype::CHAR); + m_filehandle->putAttr("caption", unitLabel->caption(), false); auto unitLbl = unitLabel->label(); - NXputattr(fileID, "label", unitLbl.ascii().c_str(), static_cast(unitLbl.ascii().size()), NXnumtype::CHAR); + m_filehandle->putAttr("label", unitLbl.ascii(), false); } - NXclosedata(fileID); + m_filehandle->closeData(); } else { std::string textAxis; for (size_t i = 0; i < sAxis->length(); i++) { textAxis += sAxis->label(i) + "\n"; } - dims_array[0] = static_cast(textAxis.size()); - NXmakedata(fileID, "axis2", NXnumtype::CHAR, 1, dims_array); - NXopendata(fileID, "axis2"); - NXputdata(fileID, textAxis.c_str()); - NXputattr(fileID, "units", "TextAxis", 8, NXnumtype::CHAR); + m_filehandle->makeData("axis2", NXnumtype::CHAR, std::vector{static_cast(textAxis.size())}, true); + m_filehandle->putData(textAxis.c_str()); + m_filehandle->putAttr("units", "TextAxis"); auto unitLabel = std::dynamic_pointer_cast(sAxis->unit()); if (unitLabel) { - NXputattr(fileID, "caption", unitLabel->caption().c_str(), static_cast(unitLabel->caption().size()), - NXnumtype::CHAR); + m_filehandle->putAttr("caption", unitLabel->caption(), false); auto unitLbl = unitLabel->label(); - NXputattr(fileID, "label", unitLbl.ascii().c_str(), static_cast(unitLbl.ascii().size()), NXnumtype::CHAR); + m_filehandle->putAttr("label", unitLbl.ascii(), false); } - NXclosedata(fileID); + m_filehandle->closeData(); } writeNexusBinMasking(localworkspace); - status = NXclosegroup(fileID); - return ((status == NXstatus::NX_ERROR) ? 3 : 0); + m_filehandle->closeGroup(); + return 0; } //------------------------------------------------------------------------------------- @@ -524,20 +422,20 @@ template void NexusFileIO::writeTableColumn(NXnumtype type, const std::string &interpret_as, const API::Column &col, const std::string &columnName) const { const auto nRows = static_cast(col.size()); - int dims_array[1] = {nRows}; + std::vector dims_array = {nRows}; auto toNexus = new NexusT[nRows]; for (int ii = 0; ii < nRows; ii++) toNexus[ii] = static_cast(col.cell(ii)); - NXwritedata(columnName.c_str(), type, 1, dims_array, toNexus, false); + writeData(columnName.c_str(), type, dims_array, toNexus, false); delete[] toNexus; // attributes - NXopendata(fileID, columnName.c_str()); + m_filehandle->openData(columnName); std::string units = "Not known"; - NXputattr(fileID, "units", units.c_str(), static_cast(units.size()), NXnumtype::CHAR); - NXputattr(fileID, "interpret_as", interpret_as.c_str(), static_cast(interpret_as.size()), NXnumtype::CHAR); - NXclosedata(fileID); + m_filehandle->putAttr("units", units, false); + m_filehandle->putAttr("interpret_as", interpret_as, false); + m_filehandle->closeData(); } // Helper templated definitions @@ -573,7 +471,7 @@ void NexusFileIO::writeNexusVectorColumn(const Column_const_sptr &col, const std } // Set-up dimensions - int dims[2]{static_cast(rowCount), static_cast(maxSize)}; + std::vector dims{static_cast(rowCount), static_cast(maxSize)}; // Create data array boost::scoped_array data(new ElemType[rowCount * maxSize]); @@ -591,9 +489,9 @@ void NexusFileIO::writeNexusVectorColumn(const Column_const_sptr &col, const std } // Write data - NXwritedata(columnName.c_str(), nexusType, 2, dims, data.get(), false); + writeData(columnName.c_str(), nexusType, dims, data.get(), false); - NXopendata(fileID, columnName.c_str()); + m_filehandle->openData(columnName); // Add sizes of rows as attributes. We can't use padding zeroes to determine // that because the @@ -604,24 +502,22 @@ void NexusFileIO::writeNexusVectorColumn(const Column_const_sptr &col, const std std::ostringstream rowSizeAttrName; rowSizeAttrName << "row_size_" << i; - NXputattr(fileID, rowSizeAttrName.str().c_str(), &size, 1, NXnumtype::INT32); + m_filehandle->putAttr(rowSizeAttrName.str(), size); } std::string units = "Not known"; // Write general attributes - NXputattr(fileID, "units", units.c_str(), static_cast(units.size()), NXnumtype::CHAR); - NXputattr(fileID, "interpret_as", interpret_as.c_str(), static_cast(interpret_as.size()), NXnumtype::CHAR); + m_filehandle->putAttr("units", units, false); + m_filehandle->putAttr("interpret_as", interpret_as, false); - NXclosedata(fileID); + m_filehandle->closeData(); } //------------------------------------------------------------------------------------- /** Write out a table Workspace's */ int NexusFileIO::writeNexusTableWorkspace(const API::ITableWorkspace_const_sptr &itableworkspace, const char *group_name) const { - NXstatus status = NXstatus::NX_ERROR; - std::shared_ptr tableworkspace = std::dynamic_pointer_cast(itableworkspace); std::shared_ptr peakworkspace = @@ -631,10 +527,11 @@ int NexusFileIO::writeNexusTableWorkspace(const API::ITableWorkspace_const_sptr return 3; // write data entry - status = NXmakegroup(fileID, group_name, "NXdata"); - if (status == NXstatus::NX_ERROR) - return (2); - NXopengroup(fileID, group_name, "NXdata"); + try { + m_filehandle->makeGroup(group_name, "NXdata", true); + } catch (::NeXus::Exception &) { + return 2; + } auto nRows = static_cast(itableworkspace->rowCount()); @@ -669,11 +566,12 @@ int NexusFileIO::writeNexusTableWorkspace(const API::ITableWorkspace_const_sptr if (maxStr == 0) { maxStr = 1; } - int dims_array[2] = {nRows, static_cast(maxStr)}; - int asize[2] = {1, dims_array[1]}; + std::vector dims_array = {nRows, static_cast(maxStr)}; + std::vector asize = {1, dims_array[1]}; - NXcompmakedata(fileID, str.c_str(), NXnumtype::CHAR, 2, dims_array, false, asize); - NXopendata(fileID, str.c_str()); + m_filehandle->makeCompData(str, NXnumtype::CHAR, dims_array, ::NeXus::LZW, asize); + + m_filehandle->openData(str); auto toNexus = new char[maxStr * nRows]; for (int ii = 0; ii < nRows; ii++) { std::string rowStr = col->cell(ii); @@ -683,16 +581,16 @@ int NexusFileIO::writeNexusTableWorkspace(const API::ITableWorkspace_const_sptr toNexus[ii * maxStr + ic] = ' '; } - NXputdata(fileID, toNexus); + m_filehandle->putData(toNexus); delete[] toNexus; // attributes std::string units = "N/A"; std::string interpret_as = "A string"; - NXputattr(fileID, "units", units.c_str(), static_cast(units.size()), NXnumtype::CHAR); - NXputattr(fileID, "interpret_as", interpret_as.c_str(), static_cast(interpret_as.size()), NXnumtype::CHAR); + m_filehandle->putAttr("units", units); + m_filehandle->putAttr("interpret_as", interpret_as); - NXclosedata(fileID); + m_filehandle->closeData(); } else if (col->isType>()) { writeNexusVectorColumn, int>(col, str, NXnumtype::INT32, ""); } else if (col->isType>()) { @@ -702,13 +600,17 @@ int NexusFileIO::writeNexusTableWorkspace(const API::ITableWorkspace_const_sptr } // write out title - NXopendata(fileID, str.c_str()); - NXputattr(fileID, "name", col->name().c_str(), static_cast(col->name().size()), NXnumtype::CHAR); - NXclosedata(fileID); + m_filehandle->openData(str); + m_filehandle->putAttr("name", col->name()); + m_filehandle->closeData(); } - status = NXclosegroup(fileID); - return ((status == NXstatus::NX_ERROR) ? 3 : 0); + try { + m_filehandle->closeGroup(); + } catch (::NeXus::Exception &) { + return 3; + } + return 0; } //------------------------------------------------------------------------------------- @@ -726,350 +628,53 @@ int NexusFileIO::writeNexusProcessedDataEventCombined(const DataObjects::EventWo std::vector const &indices, double const *tofs, float const *weights, float const *errorSquareds, int64_t const *pulsetimes, bool compress) const { - NXopengroup(fileID, "event_workspace", "NXdata"); + m_filehandle->openGroup("event_workspace", "NXdata"); // The array of indices for each event list # - int dims_array[1] = {static_cast(indices.size())}; + std::vector dims_array = {static_cast(indices.size())}; if (!indices.empty()) { if (compress) - NXcompmakedata(fileID, "indices", NXnumtype::INT64, 1, dims_array, m_nexuscompression, dims_array); + m_filehandle->makeCompData("indices", NXnumtype::INT64, dims_array, m_nexuscompression, dims_array); else - NXmakedata(fileID, "indices", NXnumtype::INT64, 1, dims_array); - NXopendata(fileID, "indices"); - NXputdata(fileID, indices.data()); - std::string yUnits = ws->YUnit(); - std::string yUnitLabel = ws->YUnitLabel(); - NXputattr(fileID, "units", yUnits.c_str(), static_cast(yUnits.size()), NXnumtype::CHAR); - NXputattr(fileID, "unit_label", yUnitLabel.c_str(), static_cast(yUnitLabel.size()), NXnumtype::CHAR); - NXclosedata(fileID); + m_filehandle->makeData("indices", NXnumtype::INT64, dims_array); + m_filehandle->openData("indices"); + m_filehandle->putData(indices.data()); + m_filehandle->putAttr("units", ws->YUnit(), false); + m_filehandle->putAttr("unit_label", ws->YUnitLabel(), false); + m_filehandle->closeData(); } // Write out each field dims_array[0] = static_cast(indices.back()); // TODO big truncation error! This is the # of events if (tofs) - NXwritedata("tof", NXnumtype::FLOAT64, 1, dims_array, tofs, compress); + writeData("tof", NXnumtype::FLOAT64, dims_array, tofs, compress); if (pulsetimes) - NXwritedata("pulsetime", NXnumtype::INT64, 1, dims_array, pulsetimes, compress); + writeData("pulsetime", NXnumtype::INT64, dims_array, pulsetimes, compress); if (weights) - NXwritedata("weight", NXnumtype::FLOAT32, 1, dims_array, weights, compress); + writeData("weight", NXnumtype::FLOAT32, dims_array, weights, compress); if (errorSquareds) - NXwritedata("error_squared", NXnumtype::FLOAT32, 1, dims_array, errorSquareds, compress); + writeData("error_squared", NXnumtype::FLOAT32, dims_array, errorSquareds, compress); // Close up the overall group - NXstatus status = NXclosegroup(fileID); - return ((status == NXstatus::NX_ERROR) ? 3 : 0); -} - -//------------------------------------------------------------------------------------- -/** Write out all of the event lists in the given workspace - * @param ws :: an EventWorkspace */ -int NexusFileIO::writeNexusProcessedDataEvent(const DataObjects::EventWorkspace_const_sptr &ws) { - // write data entry - NXstatus status = NXmakegroup(fileID, "event_workspace", "NXdata"); - if (status == NXstatus::NX_ERROR) - return (2); - NXopengroup(fileID, "event_workspace", "NXdata"); - - for (size_t wi = 0; wi < ws->getNumberHistograms(); wi++) { - std::ostringstream group_name; - group_name << "event_list_" << wi; - this->writeEventList(ws->getSpectrum(wi), group_name.str()); - } - - // Close up the overall group - status = NXclosegroup(fileID); - return ((status == NXstatus::NX_ERROR) ? 3 : 0); + m_filehandle->closeGroup(); + return 0; } //------------------------------------------------------------------------------------- /** Write out an array to the open file. */ -void NexusFileIO::NXwritedata(const char *name, NXnumtype datatype, int rank, int *dims_array, void const *data, - bool compress) const { +void NexusFileIO::writeData(const char *name, NXnumtype datatype, std::vector dims_array, void const *data, + bool compress) const { if (compress) { // We'll use the same slab/buffer size as the size of the array - NXcompmakedata(fileID, name, datatype, rank, dims_array, m_nexuscompression, dims_array); + m_filehandle->makeCompData(name, datatype, dims_array, m_nexuscompression, dims_array); } else { // Write uncompressed. - NXmakedata(fileID, name, datatype, rank, dims_array); + m_filehandle->makeData(name, datatype, dims_array); } - NXopendata(fileID, name); - NXputdata(fileID, data); - NXclosedata(fileID); -} - -//------------------------------------------------------------------------------------- -/** Write out the event list data, no matter what the underlying event type is - * @param events :: vector of TofEvent or WeightedEvent, etc. - * @param writeTOF :: if true, write the TOF values - * @param writePulsetime :: if true, write the pulse time values - * @param writeWeight :: if true, write the event weights - * @param writeError :: if true, write the errors - */ -template -void NexusFileIO::writeEventListData(std::vector events, bool writeTOF, bool writePulsetime, bool writeWeight, - bool writeError) const { - // Do nothing if there are no events. - if (events.empty()) - return; - - size_t num = events.size(); - auto tofs = new double[num]; - auto weights = new double[num]; - auto errorSquareds = new double[num]; - auto pulsetimes = new int64_t[num]; - - size_t i = 0; - // Fill the C-arrays with the fields from all the events, as requested. - for (auto it = events.cbegin(); it != events.cend(); ++it) { - if (writeTOF) - tofs[i] = it->tof(); - if (writePulsetime) - pulsetimes[i] = it->pulseTime().totalNanoseconds(); - if (writeWeight) - weights[i] = it->weight(); - if (writeError) - errorSquareds[i] = it->errorSquared(); - ++i; - } - - // Write out all the required arrays. - int dims_array[1] = {static_cast(num)}; - // In this mode, compressing makes things extremely slow! Not to be used for - // managed event workspaces. - bool compress = true; //(num > 100); - if (writeTOF) - NXwritedata("tof", NXnumtype::FLOAT64, 1, dims_array, tofs, compress); - if (writePulsetime) - NXwritedata("pulsetime", NXnumtype::INT64, 1, dims_array, pulsetimes, compress); - if (writeWeight) - NXwritedata("weight", NXnumtype::FLOAT32, 1, dims_array, weights, compress); - if (writeError) - NXwritedata("error_squared", NXnumtype::FLOAT32, 1, dims_array, errorSquareds, compress); - - // Free mem. - delete[] tofs; - delete[] weights; - delete[] errorSquareds; - delete[] pulsetimes; -} - -//------------------------------------------------------------------------------------- -/** Write out an event list into an already-opened group - * @param el :: reference to the EventList to write. - * @param group_name :: group_name to create. - * */ -int NexusFileIO::writeEventList(const DataObjects::EventList &el, const std::string &group_name) const { - // write data entry - NXstatus status = NXmakegroup(fileID, group_name.c_str(), "NXdata"); - if (status == NXstatus::NX_ERROR) - return (2); - NXopengroup(fileID, group_name.c_str(), "NXdata"); - - // Copy the detector IDs to an array. - const auto &dets = el.getDetectorIDs(); - - // Write out the detector IDs - if (!dets.empty()) { - std::vector detectorIDs(dets.begin(), dets.end()); - int dims_array[1]; - NXwritedata("detector_IDs", NXnumtype::INT64, 1, dims_array, detectorIDs.data(), false); - } - - std::string eventType("UNKNOWN"); - size_t num = el.getNumberEvents(); - switch (el.getEventType()) { - case TOF: - eventType = "TOF"; - writeEventListData(el.getEvents(), true, true, false, false); - break; - case WEIGHTED: - eventType = "WEIGHTED"; - writeEventListData(el.getWeightedEvents(), true, true, true, true); - break; - case WEIGHTED_NOTIME: - eventType = "WEIGHTED_NOTIME"; - writeEventListData(el.getWeightedEventsNoTime(), true, false, true, true); - break; - } - - // --- Save the type of sorting ----- - std::string sortType; - switch (el.getSortType()) { - case TOF_SORT: - sortType = "TOF_SORT"; - break; - case PULSETIME_SORT: - sortType = "PULSETIME_SORT"; - break; - case UNSORTED: - default: - sortType = "UNSORTED"; - break; - } - NXputattr(fileID, "sort_type", sortType.c_str(), static_cast(sortType.size()), NXnumtype::CHAR); - - // Save an attribute with the type of each event. - NXputattr(fileID, "event_type", eventType.c_str(), static_cast(eventType.size()), NXnumtype::CHAR); - // Save an attribute with the number of events - NXputattr(fileID, "num_events", &num, 1, NXnumtype::INT64); - - // Close it up! - status = NXclosegroup(fileID); - return ((status == NXstatus::NX_ERROR) ? 3 : 0); -} - -//------------------------------------------------------------------------------------- -/** Read the size of the data section in a mantid_workspace_entry and also get - *the names of axes - * - */ - -int NexusFileIO::getWorkspaceSize(int &numberOfSpectra, int &numberOfChannels, int &numberOfXpoints, - bool &uniformBounds, std::string &axesUnits, std::string &yUnits) const { - NXstatus status; - // open workspace group - status = NXopengroup(fileID, "workspace", "NXdata"); - if (status == NXstatus::NX_ERROR) - return (1); - // open "values" data which is identified by attribute "signal", if it exists - std::string entry; - if (checkEntryAtLevelByAttribute("signal", entry)) - status = NXopendata(fileID, entry.c_str()); - else { - NXclosegroup(fileID); - return (2); - } - if (status == NXstatus::NX_ERROR) { - NXclosegroup(fileID); - return (2); - } - // read workspace data size - int rank, dim[2]; - NXnumtype type; - status = NXgetinfo(fileID, &rank, dim, &type); - if (status == NXstatus::NX_ERROR) - return (3); - numberOfSpectra = dim[0]; - numberOfChannels = dim[1]; - // get axes attribute - char sbuf[NX_MAXNAMELEN]; - int len = NX_MAXNAMELEN; - type = NXnumtype::CHAR; - - char unitsAttrName[] = "units"; - if (checkAttributeName(unitsAttrName)) { - status = NXgetattr(fileID, unitsAttrName, sbuf, &len, &type); - if (status != NXstatus::NX_ERROR) - yUnits = sbuf; - NXclosedata(fileID); - } - // - // read axis1 size - status = NXopendata(fileID, "axis1"); - if (status == NXstatus::NX_ERROR) - return (4); - len = NX_MAXNAMELEN; - type = NXnumtype::CHAR; - NXgetattr(fileID, unitsAttrName, sbuf, &len, &type); - axesUnits = std::string(sbuf, len); - NXgetinfo(fileID, &rank, dim, &type); - // non-uniform X has 2D axis1 data - if (rank == 1) { - numberOfXpoints = dim[0]; - uniformBounds = true; - } else { - numberOfXpoints = dim[1]; - uniformBounds = false; - } - NXclosedata(fileID); - NXopendata(fileID, "axis2"); - len = NX_MAXNAMELEN; - type = NXnumtype::CHAR; - NXgetattr(fileID, unitsAttrName, sbuf, &len, &type); - axesUnits += std::string(":") + std::string(sbuf, len); - NXclosedata(fileID); - NXclosegroup(fileID); - return (0); -} - -bool NexusFileIO::checkAttributeName(const std::string &target) const { - // see if the given attribute name is in the current level - // return true if it is. - const std::vector<::NeXus::AttrInfo> infos = m_filehandle->getAttrInfos(); - return std::any_of(infos.cbegin(), infos.cend(), [&target](const auto &info) { return info.name == target; }); -} - -int NexusFileIO::getXValues(MantidVec &xValues, const int &spectra) const { - // - // find the X values for spectra. If uniform, the spectra number is ignored. - // - int rank, dim[2]; - NXnumtype type; - - // open workspace group - NXstatus status = NXopengroup(fileID, "workspace", "NXdata"); - if (status == NXstatus::NX_ERROR) - return (1); - // read axis1 size - status = NXopendata(fileID, "axis1"); - if (status == NXstatus::NX_ERROR) - return (2); - NXgetinfo(fileID, &rank, dim, &type); - if (rank == 1) { - NXgetdata(fileID, xValues.data()); - } else { - int start[2] = {spectra, 0}; - int size[2] = {1, dim[1]}; - NXgetslab(fileID, xValues.data(), start, size); - } - NXclosedata(fileID); - NXclosegroup(fileID); - return (0); -} - -int NexusFileIO::getSpectra(MantidVec &values, MantidVec &errors, const int &spectra) const { - // - // read the values and errors for spectra - // - int rank, dim[2]; - NXnumtype type; - - // open workspace group - NXstatus status = NXopengroup(fileID, "workspace", "NXdata"); - if (status == NXstatus::NX_ERROR) - return (1); - std::string entry; - if (checkEntryAtLevelByAttribute("signal", entry)) - status = NXopendata(fileID, entry.c_str()); - else { - NXclosegroup(fileID); - return (2); - } - if (status == NXstatus::NX_ERROR) { - NXclosegroup(fileID); - return (2); - } - NXgetinfo(fileID, &rank, dim, &type); - // get buffer and block size - int start[2] = {spectra - 1, 0}; - int size[2] = {1, dim[1]}; - NXgetslab(fileID, values.data(), start, size); - NXclosedata(fileID); - - // read errors - status = NXopendata(fileID, "errors"); - if (status == NXstatus::NX_ERROR) - return (2); - NXgetinfo(fileID, &rank, dim, &type); - // set block size; - size[1] = dim[1]; - NXgetslab(fileID, errors.data(), start, size); - NXclosedata(fileID); - - NXclosegroup(fileID); - - return (0); + m_filehandle->openData(name); + m_filehandle->putData(data); + m_filehandle->closeData(); } int NexusFileIO::findMantidWSEntries() const { @@ -1087,31 +692,6 @@ int NexusFileIO::findMantidWSEntries() const { return count; } -bool NexusFileIO::checkEntryAtLevel(const std::string &item) const { - // Search the currently open level for name "item" - std::map entries = m_filehandle->getEntries(); - return std::any_of(entries.cbegin(), entries.cend(), [&item](const auto &entry) { return entry.first == item; }); -} - -bool NexusFileIO::checkEntryAtLevelByAttribute(const std::string &attribute, std::string &entry) const { - // Search the currently open level for a section with "attribute" and return - // entry name - std::map entries = m_filehandle->getEntries(); - for (auto &entrie : entries) { - if (entrie.second == "SDS") { - m_filehandle->openData(entrie.first); - bool result = checkAttributeName(attribute); - m_filehandle->closeData(); - if (result) { - entry = entrie.first; - return true; - } - } - } - - return (false); -} - /** * Write bin masking information * @param ws :: The workspace @@ -1140,124 +720,29 @@ bool NexusFileIO::writeNexusBinMasking(const API::MatrixWorkspace_const_sptr &ws if (spectra_count == 0) return false; - NXstatus status; - // save spectra offsets as a 2d array of ints - int dimensions[2]{spectra_count, 2}; - status = NXmakedata(fileID, "masked_spectra", NXnumtype::INT32, 2, dimensions); - if (status == NXstatus::NX_ERROR) - return false; - NXopendata(fileID, "masked_spectra"); - const std::string description = "spectra index,offset in masked_bins and mask_weights"; - NXputattr(fileID, "description", description.c_str(), static_cast(description.size() + 1), NXnumtype::CHAR); - NXputdata(fileID, spectra.data()); - NXclosedata(fileID); + std::vector dimensions = {spectra_count, 2}; + + m_filehandle->makeData("masked_spectra", NXnumtype::INT32, dimensions, true); + m_filehandle->putAttr("description", "spectra index,offset in masked_bins and mask_weights"); + m_filehandle->putData(spectra.data()); + m_filehandle->closeData(); // save masked bin indices dimensions[0] = static_cast(bins.size()); - status = NXmakedata(fileID, "masked_bins", NXnumtype::UINT64, 1, dimensions); - if (status == NXstatus::NX_ERROR) - return false; - NXopendata(fileID, "masked_bins"); - NXputdata(fileID, bins.data()); - NXclosedata(fileID); + m_filehandle->makeData("masked_bins", NXnumtype::UINT64, dimensions, true); + m_filehandle->putData(bins.data()); + m_filehandle->closeData(); // save masked bin weights dimensions[0] = static_cast(bins.size()); - status = NXmakedata(fileID, "mask_weights", NXnumtype::FLOAT64, 1, dimensions); - if (status == NXstatus::NX_ERROR) - return false; - NXopendata(fileID, "mask_weights"); - NXputdata(fileID, weights.data()); - NXclosedata(fileID); + m_filehandle->makeData("mask_weights", NXnumtype::FLOAT64, dimensions, true); + m_filehandle->putData(weights.data()); + m_filehandle->closeData(); return true; } -template <> std::string NexusFileIO::logValueType() const { return "double"; } - -template <> std::string NexusFileIO::logValueType() const { return "int"; } - -template <> std::string NexusFileIO::logValueType() const { return "bool"; } - -/** Get all the Nexus entry types for a file - * - * Try to open named Nexus file and return all entries plus the definition found - *for each. - * If definition not found, try and return "analysis" field (Muon V1 files) - * Closes file on exit. - * - * @param fileName :: file to open - * @param entryName :: vector that gets filled with strings with entry names - * @param definition :: vector that gets filled with the "definition" or - *"analysis" string. - * @return count of entries if OK, -1 failed to open file. - */ -int getNexusEntryTypes(const std::string &fileName, std::vector &entryName, - std::vector &definition) { - // - // - NXhandle fileH; - NXaccess mode = NXACC_READ; - NXstatus stat = NXopen(fileName.c_str(), mode, &fileH); - if (stat == NXstatus::NX_ERROR) - return (-1); - // - entryName.clear(); - definition.clear(); - char *nxname, *nxclass; - NXnumtype nxdatatype; - nxname = new char[NX_MAXNAMELEN]; - nxclass = new char[NX_MAXNAMELEN]; - int rank, dims[2]; - NXnumtype type; - // - // Loop through all entries looking for the definition section in each (or - // analysis for MuonV1) - // - std::vector entryList; - while ((stat = NXgetnextentry(fileH, nxname, nxclass, &nxdatatype)) == NXstatus::NX_OK) { - std::string nxc(nxclass); - if (nxc == "NXentry") - entryList.emplace_back(nxname); - } - // for each entry found, look for "analysis" or "definition" text data fields - // and return value plus entry name - for (auto &entry : entryList) { - NXopengroup(fileH, entry.c_str(), "NXentry"); - // loop through field names in this entry - while ((NXgetnextentry(fileH, nxname, nxclass, &nxdatatype)) == NXstatus::NX_OK) { - // if a data field - if (std::string(nxclass) == "SDS") { - // if one of the two names we are looking for - const std::string nxn(nxname); - if (nxn == "definition" || nxn == "analysis") { - NXopendata(fileH, nxname); - stat = NXgetinfo(fileH, &rank, dims, &type); - if (stat == NXstatus::NX_ERROR) - continue; - auto value = new char[dims[0] + 1]; - stat = NXgetdata(fileH, value); - if (stat == NXstatus::NX_ERROR) - continue; - value[dims[0]] = '\0'; - // return e.g entryName "analysis"/definition "muonTD" - definition.emplace_back(value); - entryName.emplace_back(entry); - delete[] value; - NXclosegroup(fileH); // close data group, then entry - NXclosegroup(fileH); - break; - } - } - } - } - NXclose(&fileH); - delete[] nxname; - delete[] nxclass; - return (static_cast(entryName.size())); -} - /** Destructor */ diff --git a/Framework/DataHandling/src/StartAndEndTimeFromNexusFileExtractor.cpp b/Framework/DataHandling/src/StartAndEndTimeFromNexusFileExtractor.cpp index ba8b0e762db3..6e5161d51a8d 100644 --- a/Framework/DataHandling/src/StartAndEndTimeFromNexusFileExtractor.cpp +++ b/Framework/DataHandling/src/StartAndEndTimeFromNexusFileExtractor.cpp @@ -7,7 +7,6 @@ #include "MantidDataHandling/StartAndEndTimeFromNexusFileExtractor.h" #include "MantidAPI/FileFinder.h" #include "MantidDataHandling/LoadNexus.h" -#include "MantidDataHandling/SaveNexusProcessedHelper.h" #include "MantidKernel/Exception.h" #include "MantidKernel/Logger.h" #include "MantidNexus/NexusClasses.h" @@ -62,7 +61,7 @@ Mantid::Types::Core::DateAndTime handleTofRawNexusFile(TimeType type, const std: NexusType whichNexusType(const std::string &filename) { std::vector entryName; std::vector definition; - auto count = Mantid::NeXus::getNexusEntryTypes(filename, entryName, definition); + auto count = LoadNexus::getNexusEntryTypes(filename, entryName, definition); // Issues with the file if (count <= -1) { diff --git a/Framework/NexusCpp/inc/MantidNexusCpp/NeXusFile.hpp b/Framework/NexusCpp/inc/MantidNexusCpp/NeXusFile.hpp index e25afa59cc5e..929b97ea6332 100644 --- a/Framework/NexusCpp/inc/MantidNexusCpp/NeXusFile.hpp +++ b/Framework/NexusCpp/inc/MantidNexusCpp/NeXusFile.hpp @@ -396,7 +396,7 @@ class MANTID_NEXUSCPP_DLL File { * \param name Name of the attribute to add. * \param value The attribute value. */ - void putAttr(const std::string &name, const std::string &value); + void putAttr(const std::string &name, const std::string &value, const bool empty_add_space = true); /** * \copydoc NeXus::File::putSlab(void* data, std::vector& start, diff --git a/Framework/NexusCpp/src/NeXusFile.cpp b/Framework/NexusCpp/src/NeXusFile.cpp index 0804e9bdcd4a..e4b756dad08e 100644 --- a/Framework/NexusCpp/src/NeXusFile.cpp +++ b/Framework/NexusCpp/src/NeXusFile.cpp @@ -451,9 +451,9 @@ void File::putAttr(const char *name, const char *value) { this->putAttr(s_name, s_value); } -void File::putAttr(const std::string &name, const string &value) { +void File::putAttr(const std::string &name, const string &value, const bool empty_add_space) { string my_value(value); - if (my_value.empty()) + if (my_value.empty() && empty_add_space) my_value = " "; // Make a default "space" to avoid errors. AttrInfo info; info.name = name; diff --git a/buildconfig/CMake/CppCheck_Suppressions.txt.in b/buildconfig/CMake/CppCheck_Suppressions.txt.in index d1bf5a9921da..a720ccf3eb02 100644 --- a/buildconfig/CMake/CppCheck_Suppressions.txt.in +++ b/buildconfig/CMake/CppCheck_Suppressions.txt.in @@ -628,7 +628,7 @@ constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/DataHandling/src/SaveGSS.cpp: constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/DataHandling/src/SaveGSS.cpp:566 constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/DataHandling/src/SaveNXSPE.cpp:324 constVariableReference:${CMAKE_SOURCE_DIR}/Framework/DataHandling/src/SaveNXTomo.cpp:150 -knownConditionTrueFalse:${CMAKE_SOURCE_DIR}/Framework/DataHandling/src/SaveNexusProcessed.cpp:575 +knownConditionTrueFalse:${CMAKE_SOURCE_DIR}/Framework/DataHandling/src/SaveNexusProcessed.cpp:572 syntaxError:${CMAKE_SOURCE_DIR}/Framework/DataObjects/inc/MantidDataObjects/MortonIndex/CoordinateConversion.h:109 constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/DataObjects/src/EventWorkspace.cpp:712 constVariablePointer:${CMAKE_SOURCE_DIR}/Framework/DataObjects/src/EventWorkspace.cpp:738