Skip to content

Commit

Permalink
Merge pull request #2 from dials/h5_writer
Browse files Browse the repository at this point in the history
Create a generic H5 writer — along with unit tests — that can take
generic data and write it to a file. As well as adding more robust error
handling to the reader.

- Create new subdirectory `h5` in `include/dx2` to logically group h5
  related code.
- Create new header file `dx2/h5/h5write.hpp` to define the writer.
- Move `h5read_processed` into `dx2/h5` and add error handling.
- Create new test file `test/test_write_h5_array.cxx` for validation.
- Modify `CMakeLists.txt` to include the new header file, tests and
  changes to allow this to be included by other projects.

There are several changes that we would like to make to the code, mostly
involving better file handling and separately compiled implementations.
However, this commit serves as a stepping-stone for cross development
where its functionality is needed. These improvements will be followed
up in future commits.
  • Loading branch information
dimitrivlachos authored Feb 14, 2025
2 parents 2654929 + b666dc5 commit 556051d
Show file tree
Hide file tree
Showing 9 changed files with 730 additions and 94 deletions.
76 changes: 38 additions & 38 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,44 +68,44 @@ add_subdirectory(tests)

# #######################################################################
# Installation
install(
TARGETS dx2
EXPORT DX2Targets
LIBRARY DESTINATION lib
# install(
# TARGETS dx2
# EXPORT DX2Targets
# LIBRARY DESTINATION lib

# FILE_SET HEADERS DESTINATION include/dx2
)
install(
FILES
include/dx2/dx2.h
DESTINATION include/dx2
)
# # FILE_SET HEADERS DESTINATION include/dx2
# )
# install(
# FILES
# include/dx2/dx2.h
# DESTINATION include/dx2
# )

install(
EXPORT DX2Targets
FILE DX2Targets.cmake
DESTINATION lib/cmake/DX2
)
# install(
# EXPORT DX2Targets
# FILE DX2Targets.cmake
# DESTINATION lib/cmake/DX2
# )

include(CMakePackageConfigHelpers)
configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/DX2Config.cmake"
INSTALL_DESTINATION "lib/cmake/DX2"
NO_SET_AND_CHECK_MACRO
NO_CHECK_REQUIRED_COMPONENTS_MACRO
)
write_basic_package_version_file(
"${CMAKE_CURRENT_BINARY_DIR}/DX2ConfigVersion.cmake"
VERSION "${DX2_VERSION_MAJOR}.${DX2_VERSION_MINOR}"
COMPATIBILITY AnyNewerVersion
)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/DX2Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/DX2ConfigVersion.cmake
DESTINATION lib/cmake/DX2
)
export(EXPORT DX2Targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/DX2Targets.cmake"
)
# include(CMakePackageConfigHelpers)
# configure_package_config_file(
# ${CMAKE_CURRENT_SOURCE_DIR}/Config.cmake.in
# "${CMAKE_CURRENT_BINARY_DIR}/DX2Config.cmake"
# INSTALL_DESTINATION "lib/cmake/DX2"
# NO_SET_AND_CHECK_MACRO
# NO_CHECK_REQUIRED_COMPONENTS_MACRO
# )
# write_basic_package_version_file(
# "${CMAKE_CURRENT_BINARY_DIR}/DX2ConfigVersion.cmake"
# VERSION "${DX2_VERSION_MAJOR}.${DX2_VERSION_MINOR}"
# COMPATIBILITY AnyNewerVersion
# )
# install(
# FILES
# ${CMAKE_CURRENT_BINARY_DIR}/DX2Config.cmake
# ${CMAKE_CURRENT_BINARY_DIR}/DX2ConfigVersion.cmake
# DESTINATION lib/cmake/DX2
# )
# export(EXPORT DX2Targets
# FILE "${CMAKE_CURRENT_BINARY_DIR}/DX2Targets.cmake"
# )
3 changes: 1 addition & 2 deletions dx2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ add_library(
)
target_link_libraries(
dx2
PRIVATE
PUBLIC
Eigen3::Eigen
nlohmann_json::nlohmann_json
hdf5::hdf5
Expand All @@ -16,4 +16,3 @@ target_include_directories(
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(dx2 PUBLIC $<TARGET_NAME_IF_EXISTS:hdf5::hdf5>)
87 changes: 87 additions & 0 deletions include/dx2/h5/h5read_processed.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#pragma once

#include <cassert>
#include <chrono>
#include <cstring>
#include <hdf5.h>
#include <hdf5_hl.h>
#include <iostream>
#include <string>
#include <vector>

/**
* @brief Reads a dataset from an HDF5 file into an std::vector.
*
* @tparam T The type of data to read (e.g., int, double).
* @param filename The path to the HDF5 file.
* @param dataset_name The name of the dataset to read.
* @return A std::vector containing the data from the dataset.
* @throws std::runtime_error If the file, dataset, or datatype cannot be opened
* or read.
*/
template <typename T>
std::vector<T> read_array_from_h5_file(const std::string &filename,
const std::string &dataset_name) {
// Start measuring time
auto start_time = std::chrono::high_resolution_clock::now();

// Open the HDF5 file
hid_t file = H5Fopen(filename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
if (file < 0) {
throw std::runtime_error("Error: Unable to open file: " + filename);
}

try {
// Open the dataset
hid_t dataset = H5Dopen(file, dataset_name.c_str(), H5P_DEFAULT);
if (dataset < 0) {
throw std::runtime_error("Error: Unable to open dataset: " +
dataset_name);
}

try {
// Get the datatype and check size
hid_t datatype = H5Dget_type(dataset);
size_t datatype_size = H5Tget_size(datatype);
if (datatype_size != sizeof(T)) {
throw std::runtime_error(
"Error: Dataset type size does not match expected type size.");
}

// Get the dataspace and the number of elements
hid_t dataspace = H5Dget_space(dataset);
size_t num_elements = H5Sget_simple_extent_npoints(dataspace);

// Allocate a vector to hold the data
std::vector<T> data_out(num_elements);

// Read the data into the vector
herr_t status = H5Dread(dataset, datatype, H5S_ALL, H5S_ALL, H5P_DEFAULT,
data_out.data());
if (status < 0) {
throw std::runtime_error("Error: Unable to read dataset: " +
dataset_name);
}

// Close the dataset and return the data
H5Dclose(dataset);
H5Fclose(file);

// Log timing
auto end_time = std::chrono::high_resolution_clock::now();
double elapsed_time =
std::chrono::duration<double>(end_time - start_time).count();
std::cout << "READ TIME for " << dataset_name << " : " << elapsed_time
<< "s" << std::endl;

return data_out;

} catch (...) {
H5Dclose(dataset); // Ensure dataset is closed in case of failure
throw;
}
} catch (...) {
H5Fclose(file); // Ensure file is closed in case of failure
throw;
}
}
Loading

0 comments on commit 556051d

Please sign in to comment.