Program Listing for File h5write.hpp¶
↰ Return to documentation for file (include/dx2/h5/h5write.hpp)
#pragma once
#include "dx2/h5/h5dispatch.hpp"
#include "dx2/h5/h5utils.hpp"
#include <array>
#include <cstdlib>
#include <hdf5.h>
#include <string>
#include <vector>
#pragma region Raw writer
h5utils::H5Group traverse_or_create_groups(hid_t parent,
const std::string &path);
template <typename T>
void write_raw_data_to_h5_file(std::string_view filename,
std::string_view dataset_path, const T *data_ptr,
const std::vector<hsize_t> &shape) {
// Convert to std::string when needed for C-style API
std::string fname(filename);
std::string dset_path(dataset_path);
// Suppress errors when opening non-existent files, groups, datasets..
H5ErrorSilencer silencer;
// Open or create the file
h5utils::H5File file(H5Fopen(fname.c_str(), H5F_ACC_RDWR, H5P_DEFAULT));
if (!file) {
file = h5utils::H5File(
H5Fcreate(fname.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT));
if (!file) {
throw std::runtime_error("Failed to create or open file: " + fname);
}
}
// Split group and dataset name
size_t slash_pos = dset_path.find_last_of('/');
if (slash_pos == std::string::npos) {
throw std::runtime_error("Invalid dataset path: " + dset_path);
}
std::string group_path = dset_path.substr(0, slash_pos);
std::string dataset_name = dset_path.substr(slash_pos + 1);
h5utils::H5Group group = traverse_or_create_groups(file, group_path);
if (!group) {
throw std::runtime_error("Failed to create or open group: " + group_path);
}
// Create dataspace and determine type
h5utils::H5Space dataspace(
H5Screate_simple(shape.size(), shape.data(), nullptr));
h5utils::H5Type h5_type = h5dispatch::make_h5_type<T>();
// Create or open dataset
h5utils::H5Dataset dset(H5Dcreate2(group, dataset_name.c_str(), h5_type,
dataspace, H5P_DEFAULT, H5P_DEFAULT,
H5P_DEFAULT));
if (!dset) {
dset =
h5utils::H5Dataset(H5Dopen2(group, dataset_name.c_str(), H5P_DEFAULT));
if (!dset) {
throw std::runtime_error("Failed to create or open dataset: " +
dataset_name);
}
}
herr_t status =
H5Dwrite(dset, h5_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, data_ptr);
if (status < 0) {
throw std::runtime_error("Failed to write dataset: " + dset_path);
}
}
template <typename T>
void write_raw_data_to_h5_group(h5utils::H5Group &group,
const std::string &dataset_name,
const T *data_ptr,
const std::vector<hsize_t> &shape) {
// Suppress errors when opening non-existent files, groups, datasets..
H5ErrorSilencer silencer;
// Create dataspace and determine type
h5utils::H5Space dataspace(
H5Screate_simple(shape.size(), shape.data(), nullptr));
h5utils::H5Type h5_type = h5dispatch::make_h5_type<T>();
// Create or open dataset
h5utils::H5Dataset dset(H5Dcreate2(group, dataset_name.c_str(), h5_type,
dataspace, H5P_DEFAULT, H5P_DEFAULT,
H5P_DEFAULT));
if (!dset) {
dset =
h5utils::H5Dataset(H5Dopen2(group, dataset_name.c_str(), H5P_DEFAULT));
if (!dset) {
throw std::runtime_error("Failed to create or open dataset: " +
dataset_name);
}
}
herr_t status =
H5Dwrite(dset, h5_type, H5S_ALL, H5S_ALL, H5P_DEFAULT, data_ptr);
if (status < 0) {
throw std::runtime_error("Failed to write dataset: " + dataset_name);
}
}
#pragma endregion
#pragma region High-level writer
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* The high-level writer is useful for writing nested containers. While
* not utilised within the DX2 backend itself, it is a useful utility
* for writing data to HDF5 files, thus it is included here.
*/
template <typename Container>
std::vector<hsize_t> deduce_shape(const Container &container) {
if (container.empty()) {
return {0};
}
if constexpr (std::is_arithmetic_v<typename Container::value_type>) {
// Base case: container holds arithmetic types (e.g., double, int)
return {container.size()};
} else {
// Recursive case: container holds other containers
// Check that all inner containers have the same size
size_t inner_size = container.begin()->size();
for (const auto &sub_container : container) {
if (sub_container.size() != inner_size) {
throw std::runtime_error("Cannot deduce shape: inner containers have "
"different sizes.");
}
}
auto sub_shape = deduce_shape(*container.begin());
sub_shape.insert(sub_shape.begin(), container.size());
return sub_shape;
}
}
template <typename Container> auto flatten(const Container &container) {
// Determine the type of the elements in the container
using ValueType = typename Container::value_type;
// Base case: If the container holds arithmetic types (e.g., int, double),
// it is already 1D, so we return a copy of the container as a std::vector.
if constexpr (std::is_arithmetic_v<ValueType>) {
return std::vector<ValueType>(container.begin(), container.end());
} else {
// Recursive case: The container holds nested containers, so we need to
// flatten them.
// Determine the type of elements in the flattened result.
// This is deduced by recursively calling flatten on the first
// sub-container.
using InnerType =
typename decltype(flatten(*container.begin()))::value_type;
// Create a vector to store the flattened data
std::vector<InnerType> flat_data;
// Iterate over the outer container
for (const auto &sub_container : container) {
// Recursively flatten each sub-container
auto sub_flat = flatten(sub_container);
// Append the flattened sub-container to the result
flat_data.insert(flat_data.end(), sub_flat.begin(), sub_flat.end());
}
// Return the fully flattened data
return flat_data;
}
}
template <typename Container>
void write_data_to_h5_file(std::string_view filename,
std::string_view dataset_path,
const Container &data) {
std::string fname(filename);
std::string dset_path(dataset_path);
// Suppress errors when opening non-existent files, groups, datasets..
H5ErrorSilencer silencer;
// Open or create the HDF5 file
h5utils::H5File file(H5Fopen(fname.c_str(), H5F_ACC_RDWR, H5P_DEFAULT));
if (!file) {
file = h5utils::H5File(
H5Fcreate(fname.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT));
if (!file) {
throw std::runtime_error("Failed to create or open file: " + fname);
}
}
// Separate the dataset path into group path and dataset name
size_t last_slash_pos = dset_path.find_last_of('/');
if (last_slash_pos == std::string::npos) {
throw std::runtime_error("Error: Invalid dataset path, no '/' found: " +
dset_path);
}
std::string group_path = dset_path.substr(0, last_slash_pos);
std::string dataset_name = dset_path.substr(last_slash_pos + 1);
// Traverse or create the groups leading to the dataset
h5utils::H5Group group = traverse_or_create_groups(file, group_path);
if (!group) {
throw std::runtime_error("Failed to create or open group: " + group_path);
}
// Deduce the shape of the data
std::vector<hsize_t> shape = deduce_shape(data);
// Flatten the data into a 1D vector
auto flat_data = flatten(data);
// Check if dataset exists
h5utils::H5Dataset dataset(H5Dopen(group, dataset_name.c_str(), H5P_DEFAULT));
if (!dataset) {
// Dataset does not exist, create it
h5utils::H5Space dataspace(
H5Screate_simple(shape.size(), shape.data(), nullptr));
dataset = h5utils::H5Dataset(
H5Dcreate(group, dataset_name.c_str(), H5T_NATIVE_DOUBLE, dataspace,
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT));
if (!dataset) {
throw std::runtime_error("Error: Unable to create dataset: " +
dataset_name);
}
} else {
// Dataset exists, check if the shape matches
h5utils::H5Space existing_space(H5Dget_space(dataset));
int ndims = H5Sget_simple_extent_ndims(existing_space);
std::vector<hsize_t> existing_dims(ndims);
H5Sget_simple_extent_dims(existing_space, existing_dims.data(), nullptr);
if (existing_dims != shape) {
throw std::runtime_error(
"Error: Dataset shape mismatch. Cannot overwrite dataset: " +
dataset_name);
}
}
// Write the data to the dataset
herr_t status = H5Dwrite(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL,
H5P_DEFAULT, flat_data.data());
if (status < 0) {
throw std::runtime_error("Error: Unable to write data to dataset: " +
dataset_name);
}
}
#pragma endregion
#pragma region Attribute writer
void write_experiment_metadata(hid_t group_id,
const std::vector<uint64_t> &experiment_ids,
const std::vector<std::string> &identifiers);
#pragma endregion