.. _program_listing_file_include_dx2_h5_h5write.hpp: Program Listing for File h5write.hpp ==================================== |exhale_lsh| :ref:`Return to documentation for file ` (``include/dx2/h5/h5write.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #pragma once #include "dx2/h5/h5dispatch.hpp" #include "dx2/h5/h5utils.hpp" #include #include #include #include #include #pragma region Raw writer h5utils::H5Group traverse_or_create_groups(hid_t parent, const std::string &path); template void write_raw_data_to_h5_file(std::string_view filename, std::string_view dataset_path, const T *data_ptr, const std::vector &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(); // 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 void write_raw_data_to_h5_group(h5utils::H5Group &group, const std::string &dataset_name, const T *data_ptr, const std::vector &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(); // 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 std::vector deduce_shape(const Container &container) { if (container.empty()) { return {0}; } if constexpr (std::is_arithmetic_v) { // 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 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) { return std::vector(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 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 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 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 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 &experiment_ids, const std::vector &identifiers); #pragma endregion