adopt HDF4 support for proper hierarchical writing of the data.
This commit is contained in:
256
src/external/nexus/PNeXus.cpp
vendored
256
src/external/nexus/PNeXus.cpp
vendored
@@ -1971,48 +1971,105 @@ void nxH4::PNeXus::Dump()
|
|||||||
//=============================================================================
|
//=============================================================================
|
||||||
int nxH4::PNeXus::WriteNexusFile(const std::string& filename, int idfVersion)
|
int nxH4::PNeXus::WriteNexusFile(const std::string& filename, int idfVersion)
|
||||||
{
|
{
|
||||||
// Create new HDF4 file
|
// Create new HDF4 file using SD interface (for datasets)
|
||||||
int32 sd_id = SDstart(filename.c_str(), DFACC_CREATE);
|
int32 sd_id = SDstart(filename.c_str(), DFACC_CREATE);
|
||||||
if (sd_id == FAIL) {
|
if (sd_id == FAIL) {
|
||||||
std::cerr << "**ERROR** Failed to create HDF4 file: " << filename << std::endl;
|
std::cerr << "**ERROR** Failed to create HDF4 file: " << filename << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write file attributes
|
// Write file-level attributes (NeXus_version, HDF4_version, etc.)
|
||||||
WriteFileAttributes(sd_id);
|
WriteFileAttributes(sd_id);
|
||||||
|
|
||||||
// Write datasets based on IDF version
|
if ((idfVersion != 1) && (idfVersion != 2)) {
|
||||||
if ((idfVersion == 1) || (idfVersion == 2)) {
|
|
||||||
// Write all datasets from data map
|
|
||||||
for (const auto& [path, data_any] : fDataMap) {
|
|
||||||
// Try to write as int dataset
|
|
||||||
try {
|
|
||||||
auto data = std::any_cast<PNXdata<int>>(data_any);
|
|
||||||
WriteIntDataset(sd_id, path, data);
|
|
||||||
continue;
|
|
||||||
} catch (...) {}
|
|
||||||
|
|
||||||
// Try to write as float dataset
|
|
||||||
try {
|
|
||||||
auto data = std::any_cast<PNXdata<float>>(data_any);
|
|
||||||
WriteFloatDataset(sd_id, path, data);
|
|
||||||
continue;
|
|
||||||
} catch (...) {}
|
|
||||||
|
|
||||||
// Try to write as string dataset
|
|
||||||
try {
|
|
||||||
auto data = std::any_cast<PNXdata<std::string>>(data_any);
|
|
||||||
WriteStringDataset(sd_id, path, data);
|
|
||||||
continue;
|
|
||||||
} catch (...) {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::cerr << "**ERROR** Unsupported IDF version for writing: " << idfVersion << std::endl;
|
std::cerr << "**ERROR** Unsupported IDF version for writing: " << idfVersion << std::endl;
|
||||||
SDend(sd_id);
|
SDend(sd_id);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write all SDS datasets and collect their references keyed by path
|
||||||
|
std::map<std::string, int32> sdsRefMap;
|
||||||
|
|
||||||
|
for (const auto& [path, data_any] : fDataMap) {
|
||||||
|
int32 sds_ref = -1;
|
||||||
|
|
||||||
|
// Try to write as int dataset
|
||||||
|
try {
|
||||||
|
auto data = std::any_cast<PNXdata<int>>(data_any);
|
||||||
|
sds_ref = WriteIntDataset(sd_id, path, data);
|
||||||
|
} catch (...) {}
|
||||||
|
|
||||||
|
if (sds_ref == -1) {
|
||||||
|
// Try to write as float dataset
|
||||||
|
try {
|
||||||
|
auto data = std::any_cast<PNXdata<float>>(data_any);
|
||||||
|
sds_ref = WriteFloatDataset(sd_id, path, data);
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sds_ref == -1) {
|
||||||
|
// Try to write as string dataset
|
||||||
|
try {
|
||||||
|
auto data = std::any_cast<PNXdata<std::string>>(data_any);
|
||||||
|
sds_ref = WriteStringDataset(sd_id, path, data);
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sds_ref != -1) {
|
||||||
|
sdsRefMap[path] = sds_ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close SD interface before building Vgroup hierarchy
|
||||||
SDend(sd_id);
|
SDend(sd_id);
|
||||||
|
|
||||||
|
// Now open the file with Hopen/Vstart to build the Vgroup hierarchy
|
||||||
|
int32 file_id = Hopen(filename.c_str(), DFACC_WRITE, 0);
|
||||||
|
if (file_id == FAIL) {
|
||||||
|
std::cerr << "**ERROR** Failed to reopen HDF4 file for Vgroup creation: " << filename << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Vstart(file_id) == FAIL) {
|
||||||
|
std::cerr << "**ERROR** Failed to initialize Vgroup interface" << std::endl;
|
||||||
|
Hclose(file_id);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build Vgroup hierarchy and link SDS datasets into the correct groups
|
||||||
|
std::map<std::string, int32> vgroupCache; // path -> vgroup ref
|
||||||
|
|
||||||
|
for (const auto& [path, sds_ref] : sdsRefMap) {
|
||||||
|
// Extract parent path
|
||||||
|
size_t lastSlash = path.find_last_of('/');
|
||||||
|
if (lastSlash == std::string::npos || lastSlash == 0) {
|
||||||
|
// Dataset is at root level, no Vgroup needed
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::string parentPath = path.substr(0, lastSlash);
|
||||||
|
|
||||||
|
// Create the Vgroup hierarchy for the parent path
|
||||||
|
int32 parent_vg_ref = CreateVGroupHierarchy(file_id, parentPath, vgroupCache);
|
||||||
|
if (parent_vg_ref == -1) {
|
||||||
|
std::cerr << "**ERROR** Failed to create Vgroup hierarchy for: " << parentPath << std::endl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach to parent Vgroup and add the SDS dataset
|
||||||
|
int32 parent_vg_id = Vattach(file_id, parent_vg_ref, "w");
|
||||||
|
if (parent_vg_id != FAIL) {
|
||||||
|
Vaddtagref(parent_vg_id, DFTAG_NDG, sds_ref);
|
||||||
|
Vdetach(parent_vg_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach all cached Vgroups (they were left attached for reuse)
|
||||||
|
// No — Vgroups were detached after creation in CreateVGroupHierarchy.
|
||||||
|
// We only re-attach briefly above to add tag/refs.
|
||||||
|
|
||||||
|
Vend(file_id);
|
||||||
|
Hclose(file_id);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2064,7 +2121,7 @@ void nxH4::PNeXus::WriteFileAttributes(int32 sd_id)
|
|||||||
//=============================================================================
|
//=============================================================================
|
||||||
// nxH4::PNeXus::WriteIntDataset
|
// nxH4::PNeXus::WriteIntDataset
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
void nxH4::PNeXus::WriteIntDataset(int32 sd_id, const std::string& path,
|
int32 nxH4::PNeXus::WriteIntDataset(int32 sd_id, const std::string& path,
|
||||||
const PNXdata<int>& data)
|
const PNXdata<int>& data)
|
||||||
{
|
{
|
||||||
// Extract dataset name from path
|
// Extract dataset name from path
|
||||||
@@ -2101,13 +2158,18 @@ void nxH4::PNeXus::WriteIntDataset(int32 sd_id, const std::string& path,
|
|||||||
// Write attributes
|
// Write attributes
|
||||||
WriteDatasetAttributes(sds_id, data);
|
WriteDatasetAttributes(sds_id, data);
|
||||||
|
|
||||||
|
// Get the SDS reference for Vgroup linking
|
||||||
|
int32 sds_ref = SDidtoref(sds_id);
|
||||||
|
|
||||||
SDendaccess(sds_id);
|
SDendaccess(sds_id);
|
||||||
|
|
||||||
|
return sds_ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// nxH4::PNeXus::WriteFloatDataset
|
// nxH4::PNeXus::WriteFloatDataset
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
void nxH4::PNeXus::WriteFloatDataset(int32 sd_id, const std::string& path,
|
int32 nxH4::PNeXus::WriteFloatDataset(int32 sd_id, const std::string& path,
|
||||||
const PNXdata<float>& data)
|
const PNXdata<float>& data)
|
||||||
{
|
{
|
||||||
// Extract dataset name from path
|
// Extract dataset name from path
|
||||||
@@ -2144,13 +2206,18 @@ void nxH4::PNeXus::WriteFloatDataset(int32 sd_id, const std::string& path,
|
|||||||
// Write attributes
|
// Write attributes
|
||||||
WriteDatasetAttributes(sds_id, data);
|
WriteDatasetAttributes(sds_id, data);
|
||||||
|
|
||||||
|
// Get the SDS reference for Vgroup linking
|
||||||
|
int32 sds_ref = SDidtoref(sds_id);
|
||||||
|
|
||||||
SDendaccess(sds_id);
|
SDendaccess(sds_id);
|
||||||
|
|
||||||
|
return sds_ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// nxH4::PNeXus::WriteStringDataset
|
// nxH4::PNeXus::WriteStringDataset
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
void nxH4::PNeXus::WriteStringDataset(int32 sd_id, const std::string& path,
|
int32 nxH4::PNeXus::WriteStringDataset(int32 sd_id, const std::string& path,
|
||||||
const PNXdata<std::string>& data)
|
const PNXdata<std::string>& data)
|
||||||
{
|
{
|
||||||
// Extract dataset name from path
|
// Extract dataset name from path
|
||||||
@@ -2162,7 +2229,7 @@ void nxH4::PNeXus::WriteStringDataset(int32 sd_id, const std::string& path,
|
|||||||
|
|
||||||
const auto& string_data = data.GetData();
|
const auto& string_data = data.GetData();
|
||||||
if (string_data.empty()) {
|
if (string_data.empty()) {
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string str = string_data[0];
|
std::string str = string_data[0];
|
||||||
@@ -2185,7 +2252,12 @@ void nxH4::PNeXus::WriteStringDataset(int32 sd_id, const std::string& path,
|
|||||||
// Write attributes
|
// Write attributes
|
||||||
WriteDatasetAttributes(sds_id, data);
|
WriteDatasetAttributes(sds_id, data);
|
||||||
|
|
||||||
|
// Get the SDS reference for Vgroup linking
|
||||||
|
int32 sds_ref = SDidtoref(sds_id);
|
||||||
|
|
||||||
SDendaccess(sds_id);
|
SDendaccess(sds_id);
|
||||||
|
|
||||||
|
return sds_ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
@@ -2238,6 +2310,126 @@ void nxH4::PNeXus::WriteDatasetAttributes(int32 sds_id, const PNXdata<T>& data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// nxH4::PNeXus::WriteVGroupAttributes
|
||||||
|
//=============================================================================
|
||||||
|
void nxH4::PNeXus::WriteVGroupAttributes(int32 vgroup_id,
|
||||||
|
const std::map<std::string, std::any>& attributes)
|
||||||
|
{
|
||||||
|
for (const auto& [attr_name, attr_value] : attributes) {
|
||||||
|
// Try string
|
||||||
|
try {
|
||||||
|
std::string str = std::any_cast<std::string>(attr_value);
|
||||||
|
Vsetattr(vgroup_id, attr_name.c_str(), DFNT_CHAR8,
|
||||||
|
static_cast<int32>(str.length()), str.c_str());
|
||||||
|
continue;
|
||||||
|
} catch (...) {}
|
||||||
|
|
||||||
|
// Try scalar int
|
||||||
|
try {
|
||||||
|
int32 value = std::any_cast<int>(attr_value);
|
||||||
|
Vsetattr(vgroup_id, attr_name.c_str(), DFNT_INT32, 1, &value);
|
||||||
|
continue;
|
||||||
|
} catch (...) {}
|
||||||
|
|
||||||
|
// Try scalar float
|
||||||
|
try {
|
||||||
|
float32 value = std::any_cast<float>(attr_value);
|
||||||
|
Vsetattr(vgroup_id, attr_name.c_str(), DFNT_FLOAT32, 1, &value);
|
||||||
|
continue;
|
||||||
|
} catch (...) {}
|
||||||
|
|
||||||
|
// Try vector<int>
|
||||||
|
try {
|
||||||
|
std::vector<int> int_vec = std::any_cast<std::vector<int>>(attr_value);
|
||||||
|
std::vector<int32> data32(int_vec.begin(), int_vec.end());
|
||||||
|
Vsetattr(vgroup_id, attr_name.c_str(), DFNT_INT32,
|
||||||
|
static_cast<int32>(data32.size()), data32.data());
|
||||||
|
continue;
|
||||||
|
} catch (...) {}
|
||||||
|
|
||||||
|
// Try vector<float>
|
||||||
|
try {
|
||||||
|
std::vector<float> float_vec = std::any_cast<std::vector<float>>(attr_value);
|
||||||
|
std::vector<float32> data32(float_vec.begin(), float_vec.end());
|
||||||
|
Vsetattr(vgroup_id, attr_name.c_str(), DFNT_FLOAT32,
|
||||||
|
static_cast<int32>(data32.size()), data32.data());
|
||||||
|
continue;
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// nxH4::PNeXus::CreateVGroupHierarchy
|
||||||
|
//=============================================================================
|
||||||
|
int32 nxH4::PNeXus::CreateVGroupHierarchy(int32 file_id, const std::string& path,
|
||||||
|
std::map<std::string, int32>& vgroupCache)
|
||||||
|
{
|
||||||
|
std::vector<std::string> components = splitPath(path);
|
||||||
|
if (components.empty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string currentPath;
|
||||||
|
int32 parent_vg_ref = -1;
|
||||||
|
|
||||||
|
for (const auto& component : components) {
|
||||||
|
if (component.empty()) continue;
|
||||||
|
|
||||||
|
currentPath += "/" + component;
|
||||||
|
|
||||||
|
// Check if this Vgroup was already created
|
||||||
|
auto it = vgroupCache.find(currentPath);
|
||||||
|
if (it != vgroupCache.end()) {
|
||||||
|
parent_vg_ref = it->second;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new Vgroup
|
||||||
|
int32 vg_id = Vattach(file_id, -1, "w");
|
||||||
|
if (vg_id == FAIL) {
|
||||||
|
std::cerr << "**ERROR** Failed to create Vgroup for: " << currentPath << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vsetname(vg_id, component.c_str());
|
||||||
|
|
||||||
|
// Write group attributes if any exist for this path
|
||||||
|
auto attrIt = fGroupAttributes.find(currentPath);
|
||||||
|
if (attrIt != fGroupAttributes.end()) {
|
||||||
|
// Check for NX_class attribute and set it as Vgroup class
|
||||||
|
auto classIt = attrIt->second.find("NX_class");
|
||||||
|
if (classIt != attrIt->second.end()) {
|
||||||
|
try {
|
||||||
|
std::string nxClass = std::any_cast<std::string>(classIt->second);
|
||||||
|
Vsetclass(vg_id, nxClass.c_str());
|
||||||
|
} catch (...) {}
|
||||||
|
}
|
||||||
|
WriteVGroupAttributes(vg_id, attrIt->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the reference of the newly created Vgroup
|
||||||
|
int32 vg_ref = VQueryref(vg_id);
|
||||||
|
|
||||||
|
// If there is a parent Vgroup, add this one as a child
|
||||||
|
if (parent_vg_ref != -1) {
|
||||||
|
int32 parent_vg_id = Vattach(file_id, parent_vg_ref, "w");
|
||||||
|
if (parent_vg_id != FAIL) {
|
||||||
|
Vinsert(parent_vg_id, vg_id);
|
||||||
|
Vdetach(parent_vg_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vdetach(vg_id);
|
||||||
|
|
||||||
|
// Cache the reference for this path
|
||||||
|
vgroupCache[currentPath] = vg_ref;
|
||||||
|
parent_vg_ref = vg_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent_vg_ref;
|
||||||
|
}
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// Group attribute methods - manage attributes associated with HDF4 groups
|
// Group attribute methods - manage attributes associated with HDF4 groups
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
|
|||||||
57
src/external/nexus/PNeXus.h
vendored
57
src/external/nexus/PNeXus.h
vendored
@@ -944,43 +944,60 @@ private:
|
|||||||
void WriteDatasetAttributes(int32 sds_id, const PNXdata<T>& data);
|
void WriteDatasetAttributes(int32 sds_id, const PNXdata<T>& data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write attributes to a group
|
* @brief Write attributes to a Vgroup
|
||||||
* @param sd_id HDF4 SD interface ID
|
* @param vgroup_id HDF4 Vgroup ID to write attributes to
|
||||||
* @param groupPath Group path
|
|
||||||
* @param attributes Map of attribute names to values
|
* @param attributes Map of attribute names to values
|
||||||
*/
|
*/
|
||||||
void WriteGroupAttributes(int32 sd_id, const std::string& groupPath,
|
void WriteVGroupAttributes(int32 vgroup_id,
|
||||||
const std::map<std::string, std::any>& attributes);
|
const std::map<std::string, std::any>& attributes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write an integer dataset with attributes
|
* @brief Create the Vgroup hierarchy for a given path
|
||||||
* @param sd_id HDF4 SD interface ID
|
*
|
||||||
* @param path Dataset path
|
* Given a path like "/run/sample", creates Vgroups "run" and "sample"
|
||||||
* @param data PNXdata object containing data, dimensions, and attributes
|
* (nested), writing any associated group attributes from fGroupAttributes.
|
||||||
* @throws std::runtime_error if writing fails
|
* Uses a cache to avoid creating duplicate Vgroups.
|
||||||
|
*
|
||||||
|
* @param file_id HDF4 file ID (from Hopen)
|
||||||
|
* @param path The group path to create (e.g., "/run/sample")
|
||||||
|
* @param vgroupCache Map of already-created group paths to their Vgroup references
|
||||||
|
* @return The Vgroup reference of the deepest group in the path
|
||||||
*/
|
*/
|
||||||
void WriteIntDataset(int32 sd_id, const std::string& path,
|
int32 CreateVGroupHierarchy(int32 file_id, const std::string& path,
|
||||||
const PNXdata<int>& data);
|
std::map<std::string, int32>& vgroupCache);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write a float dataset with attributes
|
* @brief Write an integer dataset with attributes and return its SDS reference
|
||||||
* @param sd_id HDF4 SD interface ID
|
* @param sd_id HDF4 SD interface ID
|
||||||
* @param path Dataset path
|
* @param path Dataset path (leaf name used for SDS name)
|
||||||
* @param data PNXdata object containing data, dimensions, and attributes
|
* @param data PNXdata object containing data, dimensions, and attributes
|
||||||
|
* @return SDS reference number for linking into Vgroups
|
||||||
* @throws std::runtime_error if writing fails
|
* @throws std::runtime_error if writing fails
|
||||||
*/
|
*/
|
||||||
void WriteFloatDataset(int32 sd_id, const std::string& path,
|
int32 WriteIntDataset(int32 sd_id, const std::string& path,
|
||||||
const PNXdata<float>& data);
|
const PNXdata<int>& data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write a string dataset with attributes
|
* @brief Write a float dataset with attributes and return its SDS reference
|
||||||
* @param sd_id HDF4 SD interface ID
|
* @param sd_id HDF4 SD interface ID
|
||||||
* @param path Dataset path
|
* @param path Dataset path (leaf name used for SDS name)
|
||||||
* @param data PNXdata object containing data, dimensions, and attributes
|
* @param data PNXdata object containing data, dimensions, and attributes
|
||||||
|
* @return SDS reference number for linking into Vgroups
|
||||||
* @throws std::runtime_error if writing fails
|
* @throws std::runtime_error if writing fails
|
||||||
*/
|
*/
|
||||||
void WriteStringDataset(int32 sd_id, const std::string& path,
|
int32 WriteFloatDataset(int32 sd_id, const std::string& path,
|
||||||
const PNXdata<std::string>& data);
|
const PNXdata<float>& data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write a string dataset with attributes and return its SDS reference
|
||||||
|
* @param sd_id HDF4 SD interface ID
|
||||||
|
* @param path Dataset path (leaf name used for SDS name)
|
||||||
|
* @param data PNXdata object containing data, dimensions, and attributes
|
||||||
|
* @return SDS reference number for linking into Vgroups, or -1 for empty strings
|
||||||
|
* @throws std::runtime_error if writing fails
|
||||||
|
*/
|
||||||
|
int32 WriteStringDataset(int32 sd_id, const std::string& path,
|
||||||
|
const PNXdata<std::string>& data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Write root-level file attributes
|
* @brief Write root-level file attributes
|
||||||
|
|||||||
Reference in New Issue
Block a user