From 4dcef1a835d5e31d3489409564785359d1966cfc Mon Sep 17 00:00:00 2001 From: Andreas Suter Date: Sat, 7 Feb 2026 08:05:09 +0100 Subject: [PATCH] add HDF4/HDF5 library version information, needed for writing. --- src/external/nexus/PNeXus.cpp | 190 ++++++++++++++++++---------------- src/external/nexus/PNeXus.h | 33 +++--- 2 files changed, 118 insertions(+), 105 deletions(-) diff --git a/src/external/nexus/PNeXus.cpp b/src/external/nexus/PNeXus.cpp index 21b14e1d..3b1153c6 100644 --- a/src/external/nexus/PNeXus.cpp +++ b/src/external/nexus/PNeXus.cpp @@ -302,6 +302,10 @@ void nxH4::PNeXusDeadTime::minimize(const int i) //============================================================================= nxH4::PNeXus::PNeXus() : fSdId(-1), fFileId(-1) { + uint32 major_v, minor_v, release; + char string[256]; + Hgetlibversion(&major_v, &minor_v, &release, string); + fHdf4LibVersion = string; } //============================================================================= @@ -310,6 +314,11 @@ nxH4::PNeXus::PNeXus() : fSdId(-1), fFileId(-1) nxH4::PNeXus::PNeXus(const std::string fln, const bool printDebug) : fPrintDebug(printDebug), fFileName(fln), fSdId(-1), fFileId(-1) { + uint32 major_v, minor_v, release; + char string[256]; + Hgetlibversion(&major_v, &minor_v, &release, string); + fHdf4LibVersion = string; + if (ReadNexusFile() != 0) { throw std::runtime_error("Failed to read NeXus file: " + fln); } @@ -3231,7 +3240,15 @@ std::string nxH5::PNeXus::findDatasetPath(H5::Group& parent, const std::string& //============================================================================= nxH5::PNeXus::PNeXus() { - // empty + unsigned majnum, minnum, relnum; + herr_t status; + std::stringstream ss; + + status = H5get_libversion(&majnum, &minnum, &relnum); + if (status >= 0) { + ss << majnum << "." << minnum << "." << relnum; + } + fHdf5LibVersion = ss.str(); } //============================================================================= @@ -3262,6 +3279,16 @@ nxH5::PNeXus::PNeXus() */ nxH5::PNeXus::PNeXus(const std::string fln, const bool printDebug) : fFileName(fln), fPrintDebug(printDebug) { + unsigned majnum, minnum, relnum; + herr_t status; + std::stringstream ss; + + status = H5get_libversion(&majnum, &minnum, &relnum); + if (status >= 0) { + ss << majnum << "." << minnum << "." << relnum; + } + fHdf5LibVersion = ss.str(); + ReadNexusFile(); } @@ -5309,84 +5336,6 @@ void nxH5::PNeXus::WriteFileAttributes(H5::H5File& file) } } -//============================================================================= -// Write IDF version 2 structure -//============================================================================= -/** - * Writes all datasets from the data map to the HDF5 file following the - * IDF version 2 structure. Iterates through fDataMap and writes each - * dataset based on its type. - * - * @param file HDF5 file object - * @throws H5::Exception if writing fails - */ -void nxH5::PNeXus::WriteIdfV2(H5::H5File& file) -{ - if (fPrintDebug) { - std::cout << "debug> Writing IDF v2 structure..." << std::endl; - } - - // Iterate through all datasets in fDataMap - for (const auto& [path, anyData] : fDataMap) { - try { - // Try PNXdata - try { - auto intData = std::any_cast>(anyData); - WriteIntDataset(file, path, intData); - continue; - } catch (const std::bad_any_cast&) {} - - // Try PNXdata - convert to int - try { - auto int32Data = std::any_cast>(anyData); - - // Convert PNXdata to PNXdata - PNXdata intData(int32Data.GetDataType()); - intData.SetDimensions(int32Data.GetDimensions()); - - std::vector convertedData; - for (auto val : int32Data.GetData()) { - convertedData.push_back(static_cast(val)); - } - intData.SetData(convertedData); - - // Copy attributes - for (const auto& [name, value] : int32Data.GetAttributes()) { - intData.AddAttribute(name, value); - } - - WriteIntDataset(file, path, intData); - continue; - } catch (const std::bad_any_cast&) {} - - // Try PNXdata - try { - auto floatData = std::any_cast>(anyData); - WriteFloatDataset(file, path, floatData); - continue; - } catch (const std::bad_any_cast&) {} - - // Try PNXdata - try { - auto strData = std::any_cast>(anyData); - WriteStringDataset(file, path, strData); - continue; - } catch (const std::bad_any_cast&) {} - - // Unknown type - if (fPrintDebug) { - std::cerr << "Warning: Unknown data type for path " << path - << std::endl; - } - - } catch (const H5::Exception& err) { - std::cerr << "Error writing dataset " << path << ": " - << err.getDetailMsg() << std::endl; - throw; - } - } -} - //============================================================================= // Group attribute management methods //============================================================================= @@ -5533,29 +5482,86 @@ int nxH5::PNeXus::WriteNexusFile(const std::string& filename, int idfVersion) std::cout << "debug> Creating NeXus file: " << filename << std::endl; } - // Validate that we have data to write + // Validate that we have data to write if (fDataMap.empty()) { std::cerr << "Error: No data to write (data map is empty)" << std::endl; return 1; } - // Create new HDF5 file (truncate if exists) + // Create new HDF5 file (truncate if exists) H5::H5File file(filename, H5F_ACC_TRUNC); - // Turn off auto-printing for exceptions + // Turn off auto-printing for exceptions H5::Exception::dontPrint(); - // Write root-level file attributes + // Write root-level file attributes WriteFileAttributes(file); - // Write structure based on IDF version - if (idfVersion == 1) { - std::cerr << "Error: IDF version 1 writing not yet implemented" - << std::endl; - file.close(); - return 1; - } else if (idfVersion == 2) { - WriteIdfV2(file); + // Write structure based on IDF version + if ((idfVersion == 1) || (idfVersion == 2)) { + if (fPrintDebug) { + std::cout << "debug> Writing IDF v2 structure..." << std::endl; + } + + // Iterate through all datasets in fDataMap + for (const auto& [path, anyData] : fDataMap) { + try { + // Try PNXdata + try { + auto intData = std::any_cast>(anyData); + WriteIntDataset(file, path, intData); + continue; + } catch (const std::bad_any_cast&) {} + + // Try PNXdata - convert to int + try { + auto int32Data = std::any_cast>(anyData); + + // Convert PNXdata to PNXdata + PNXdata intData(int32Data.GetDataType()); + intData.SetDimensions(int32Data.GetDimensions()); + + std::vector convertedData; + for (auto val : int32Data.GetData()) { + convertedData.push_back(static_cast(val)); + } + intData.SetData(convertedData); + + // Copy attributes + for (const auto& [name, value] : int32Data.GetAttributes()) { + intData.AddAttribute(name, value); + } + + WriteIntDataset(file, path, intData); + continue; + } catch (const std::bad_any_cast&) {} + + // Try PNXdata + try { + auto floatData = std::any_cast>(anyData); + WriteFloatDataset(file, path, floatData); + continue; + } catch (const std::bad_any_cast&) {} + + // Try PNXdata + try { + auto strData = std::any_cast>(anyData); + WriteStringDataset(file, path, strData); + continue; + } catch (const std::bad_any_cast&) {} + + // Unknown type + if (fPrintDebug) { + std::cerr << "Warning: Unknown data type for path " << path + << std::endl; + } + + } catch (const H5::Exception& err) { + std::cerr << "Error writing dataset " << path << ": " + << err.getDetailMsg() << std::endl; + throw; + } + } } else { std::cerr << "Error: Unsupported IDF version " << idfVersion << std::endl; @@ -5563,7 +5569,7 @@ int nxH5::PNeXus::WriteNexusFile(const std::string& filename, int idfVersion) return 1; } - // Close file + // Close file file.close(); if (fPrintDebug) { diff --git a/src/external/nexus/PNeXus.h b/src/external/nexus/PNeXus.h index d0de0c0e..9f9e7f2c 100644 --- a/src/external/nexus/PNeXus.h +++ b/src/external/nexus/PNeXus.h @@ -543,6 +543,12 @@ public: */ std::string GetFileName() const { return fFileName; } + /** + * @brief Get the hdf4 library version + * @return The hdf4 version as a string + */ + std::string GetHdf4LibVersion() const { return fHdf4LibVersion; } + /** * @brief Get the hdf4 version of the NeXus file * @return The hdf4 version as a string @@ -874,6 +880,7 @@ private: bool fPrintDebug{false}; ///< if true print additional debug information std::string fFileName{""}; ///< NeXus HDF4 filename int fIdfVersion{-1}; ///< IDF version of the NeXus file + std::string fHdf4LibVersion{""}; ///< HDF4 library version used when calling PNeXus std::string fHdf4Version{""}; ///< HDF4 version of the file std::string fNeXusVersion{""}; ///< NeXus version of the file std::string fFileNameNxs{""}; @@ -981,9 +988,9 @@ private: */ void WriteIdfV2(int32 sd_id); - // ======================================================================== - // Case-insensitive lookup helper methods - // ======================================================================== + // ======================================================================== + // Case-insensitive lookup helper methods + // ======================================================================== /** * @brief Compare two strings case-insensitively @@ -1488,6 +1495,12 @@ public: */ std::string GetFileName() const { return fFileName; } + /** + * @brief Get the HDF5 library version string + * @return The HDF5 version as a string (e.g., "1.10.4") + */ + std::string GetHdf5LibVersion() const { return fHdf5LibVersion; } + /** * @brief Get the HDF5 version string from the file * @return The HDF5 version as a string (e.g., "1.10.4") @@ -1843,6 +1856,7 @@ private: bool fPrintDebug{false}; ///< if true print additional debug information std::string fFileName{""}; ///< NeXus HDF5 filename int fIdfVersion{-1}; ///< IDF version of the NeXus file + std::string fHdf5LibVersion{"n/a"}; ///< HDF5 library version used when calling PNeXus std::string fHdf5Version{""}; ///< HDF5 version of the file std::string fNeXusVersion{""}; ///< NeXus version of the file std::string fFileNameNxs{""}; @@ -1948,16 +1962,9 @@ private: */ void WriteFileAttributes(H5::H5File& file); - /** - * @brief Write IDF version 2 structure - * @param file HDF5 file object - * @throws H5::Exception if writing fails - */ - void WriteIdfV2(H5::H5File& file); - - // ======================================================================== - // Case-insensitive lookup helper methods - // ======================================================================== + // ======================================================================== + // Case-insensitive lookup helper methods + // ======================================================================== /** * @brief Compare two strings case-insensitively