9#ifndef THEORETICA_IO_HDF5_H
10#define THEORETICA_IO_HDF5_H
16#include <unordered_map>
69 std::unordered_map<std::string, hdf5_node>
children;
121 if (
id == H5I_INVALID_HID || !H5Iis_valid(
id))
return;
122 switch (H5Iget_type(
id)) {
123 case H5I_FILE: H5Fclose(
id);
break;
124 case H5I_GROUP: H5Gclose(
id);
break;
125 case H5I_DATASET: H5Dclose(
id);
break;
126 case H5I_DATATYPE: H5Tclose(
id);
break;
127 case H5I_DATASPACE: H5Sclose(
id);
break;
128 case H5I_ATTR: H5Aclose(
id);
break;
129 case H5I_GENPROP_LST: H5Pclose(
id);
break;
130 default: H5Idec_ref(
id);
break;
142 other.id = H5I_INVALID_HID;
149 if (
this != &other) {
151 if (
id >= 0 && H5Iis_valid(
id))
155 other.id = H5I_INVALID_HID;
163 operator hid_t()
const {
169 namespace _internal {
174 template<
typename Type>
177 return H5I_INVALID_HID;
180 template<>
inline hid_t hdf5_type<double>() {
return H5T_NATIVE_DOUBLE; }
181 template<>
inline hid_t hdf5_type<float>() {
return H5T_NATIVE_FLOAT; }
182 template<>
inline hid_t hdf5_type<int>() {
return H5T_NATIVE_INT; }
189 H5Eset_auto(H5E_DEFAULT,
nullptr,
nullptr);
199 if (H5Lexists(
id, path.c_str(), H5P_DEFAULT) > 0) {
200 H5Ldelete(
id, path.c_str(), H5P_DEFAULT);
208 static herr_t attribute_callback(hid_t
id,
const char* attr_name,
const H5A_info_t* info_ptr,
void* op_data) {
210 auto* attrs = (std::vector<std::string>*) op_data;
211 attrs->emplace_back(attr_name);
217 inline void load_attributes(hid_t obj_id, std::vector<std::string>& attributes) {
218 H5Aiterate2(obj_id, H5_INDEX_CRT_ORDER, H5_ITER_INC, NULL, attribute_callback, &attributes);
223 template<
typename Type>
224 inline void read_attribute(hid_t attr_id, Type& value) {
226 if (H5Aread(attr_id, hdf5_type<Type>(), &value) < 0) {
234 inline void read_attribute(hid_t attr_id, std::string& value) {
236 hdf5_handle type_id = H5Aget_type(attr_id);
237 if (H5Tget_class(type_id) != H5T_STRING) {
239 value = std::string();
243 const int is_varstring = H5Tis_variable_str(type_id);
244 if (is_varstring > 0) {
247 hdf5_handle mem_type = H5Tcopy(H5T_C_S1);
248 H5Tset_size(mem_type, H5T_VARIABLE);
250 if (H5Aread(attr_id, mem_type, &buf) < 0) {
252 value = std::string();
256 value = buf ? std::string(buf) : std::string();
260 }
else if (is_varstring == 0) {
262 const size_t size = H5Tget_size(type_id);
263 std::vector<char> buf (size + 1,
'\0');
265 hdf5_handle mem_type = H5Tcopy(H5T_C_S1);
266 H5Tset_size(mem_type, size);
267 if (H5Aread(attr_id, mem_type, buf.data()) < 0) {
269 value = std::string();
273 value = std::string(buf.data());
277 value = std::string();
284 template<
typename Type>
285 inline void write_attribute(hid_t obj_id,
const std::string& name,
const Type& value) {
287 hdf5_handle space_id = H5Screate(H5S_SCALAR);
288 hdf5_handle attr_id = H5Acreate2(obj_id, name.c_str(),
hdf5_type<Type>(), space_id, H5P_DEFAULT, H5P_DEFAULT);
301 inline void write_attribute(hid_t obj_id,
const std::string& name,
const std::string& value) {
303 hdf5_handle space_id = H5Screate(H5S_SCALAR);
304 hdf5_handle type_id = H5Tcopy(H5T_C_S1);
305 H5Tset_size(type_id, value.empty() ? 1 : value.length());
306 H5Tset_strpad(type_id, H5T_STR_NULLTERM);
308 hdf5_handle attr_id = H5Acreate2(obj_id, name.c_str(), type_id, space_id, H5P_DEFAULT, H5P_DEFAULT);
314 if (H5Awrite(attr_id, type_id, value.c_str()) < 0) {
321 inline void write_attribute(hid_t obj_id,
const std::string& name,
const char* str) {
322 write_attribute(obj_id, name, std::string(str));
327 inline herr_t iter_callback(hid_t
id,
const char* name,
const H5L_info_t* info,
void* opdata) {
329 hdf5_node* parent = (hdf5_node*) opdata;
334 if (parent->path ==
"/")
335 child.path =
"/" + std::string(name);
337 child.path = parent->path +
"/" + std::string(name);
340 hdf5_handle obj_id = H5Oopen(
id, name, H5P_DEFAULT);
343 parent->children.emplace(child.name, std::move(child));
347 const H5I_type_t obj_type = H5Iget_type(obj_id);
348 if (obj_type == H5I_GROUP) {
353 load_attributes(obj_id, child.attributes);
356 hdf5_handle gid = H5Gopen2(
id, name, H5P_DEFAULT);
358 H5Literate(gid, H5_INDEX_NAME, H5_ITER_INC, NULL, iter_callback, &child);
360 }
else if (obj_type == H5I_DATASET) {
365 hdf5_handle space_id = H5Dget_space(obj_id);
366 const int ndims = H5Sget_simple_extent_ndims(space_id);
368 std::vector<hsize_t> dims (
static_cast<size_t>(ndims));
369 H5Sget_simple_extent_dims(space_id, dims.data(), NULL);
370 child.dimensions = dims;
374 load_attributes(obj_id, child.attributes);
379 load_attributes(obj_id, child.attributes);
382 parent->children.emplace(child.name, std::move(child));
389 inline void build_tree(
390 std::ostringstream& oss,
const hdf5_node& node,
391 const std::string& prefix,
bool is_last,
bool is_root) {
395 oss << (node.name.empty() ?
"/" : node.name);
397 oss << prefix << (is_last ?
"└── " :
"├── ") << node.name;
401 if (node.is_dataset()) {
404 for (
size_t i = 0; i < node.dimensions.size(); ++i) {
405 oss << node.dimensions[i];
406 if (i + 1 < node.dimensions.size())
414 if (!node.attributes.empty()) {
415 oss <<
" (@" << node.attributes.size() <<
" attrs)";
420 if (node.children.empty())
425 const std::string child_prefix = is_root ?
"" : (prefix + (is_last ?
" " :
"│ "));
426 for (
auto& pair : node.children) {
428 const bool is_last_child = (node.children.size() == i + 1);
450 std::ostringstream oss;
451 _internal::build_tree(oss, node,
"",
true,
true);
462 return os << to_string(node);
482 file_id = H5Fopen(filename.c_str(), H5F_ACC_RDWR, H5P_DEFAULT);
486 file_id = H5Fcreate(filename.c_str(), H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
490 return H5I_INVALID_HID;
497 file_id = H5Fopen(filename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
501 return H5I_INVALID_HID;
514 return H5Iis_valid(handle.
id);
530 root.dimensions.clear();
531 root.attributes.clear();
532 root.children.clear();
534 _internal::load_attributes(
id,
root.attributes);
535 H5Literate(
id, H5_INDEX_NAME, H5_ITER_INC, NULL, _internal::iter_callback, &
root);
550 if (H5Lexists(
id, path.c_str(), H5P_DEFAULT) > 0)
553 hid_t gid = H5Gcreate2(
id, path.c_str(), H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
572 if (H5Lexists(
id, path.c_str(), H5P_DEFAULT) <= 0)
575 if (H5Ldelete(
id, path.c_str(), H5P_DEFAULT) < 0) {
590 template<
typename Type>
595 hid_t obj_id = H5Oopen(
id, path.c_str(), H5P_DEFAULT);
602 hid_t attr_id = H5Aopen(obj_id, attr_name.c_str(), H5P_DEFAULT);
610 _internal::read_attribute(attr_id, value);
624 hid_t obj_id = H5Oopen(
id, path.c_str(), H5P_DEFAULT);
632 if (H5Aexists(obj_id, attr_name.c_str()) > 0) {
633 if (H5Adelete(obj_id, attr_name.c_str()) < 0) {
647 template<
typename Type>
651 hid_t obj_id = H5Oopen(
id, path.c_str(), H5P_DEFAULT);
658 if (H5Aexists(obj_id, attr_name.c_str()) > 0)
659 H5Adelete(obj_id, attr_name.c_str());
661 _internal::write_attribute(obj_id, attr_name, value);
674 template<
typename Vector = vec<real>>
677 using Type = vector_element_t<Vector>;
680 hid_t data_id = H5Dopen2(
id, path.c_str(), H5P_DEFAULT);
690 if (H5Sget_simple_extent_ndims(h_space) != 1) {
696 H5Sget_simple_extent_dims(h_space, dims, NULL);
698 v.resize(dims[0],
nan());
699 if (v.size() != dims[0]) {
719 template<
typename Vector = vec<real>>
732 template<
typename Vector>
735 using Type = vector_element_t<Vector>;
738 hsize_t dims[1] = { (hsize_t) v.size() };
739 hdf5_handle h_space = H5Screate_simple(1, dims, NULL);
762 if (H5Lexists(
id, path.c_str(), H5P_DEFAULT) > 0) {
763 if (H5Ldelete(
id, path.c_str(), H5P_DEFAULT) < 0) {
776 template<
typename Matrix = mat<real>>
779 using Type = matrix_element_t<Matrix>;
782 hid_t data_id = H5Dopen2(
id, path.c_str(), H5P_DEFAULT);
790 if (H5Sget_simple_extent_ndims(h_space) != 2) {
796 H5Sget_simple_extent_dims(h_space, dims, NULL);
798 m.resize(dims[0], dims[1]);
799 if (m.rows() != dims[0] || m.cols() != dims[1]) {
819 template<
typename Matrix = mat<real>>
832 template<
typename Matrix>
835 using Type = matrix_element_t<Matrix>;
838 hsize_t dims[2] = { hsize_t(m.rows()), hsize_t(m.cols()) };
839 hdf5_handle h_space = H5Screate_simple(2, dims, NULL);
860 std::string m_filename;
875 m_file_id =
hdf5_open(m_filename, write);
890 m_root.dimensions.clear();
891 m_root.attributes.clear();
892 m_root.children.clear();
898 m_root.name = m_filename;
906 m_file_id = H5I_INVALID_HID;
936 return m_root[child_name];
943 return m_root[child_name];
959 const std::string path = (parent.
path ==
"/")
961 : (parent.
path +
"/" + child_name);
989 template<
typename Vector = vec<real>>
1001 template<
typename Vector = vec<real>>
1012 template<
typename Vector>
1022 template<
typename Vector>
1035 template<
typename Matrix = mat<real>>
1046 template<
typename Matrix = mat<real>>
1057 template<
typename Matrix>
1068 template<
typename Matrix>
1098 template<
typename Type>
1110 template<
typename Type>
1122 template<
typename Type>
1123 void write_attribute(
const std::string& path,
const std::string& attr_name,
const Type& value) {
1134 template<
typename Type>
1163 return io::to_string(m_root);
hid_t id() const
Get the raw ID of the HDF5 file handle for direct API access.
Definition hdf5.h:928
Matrix read_mat(const std::string &path) const
Read a 2D dataset array into a matrix from the given path.
Definition hdf5.h:1036
void write_vec(const std::string &path, const Vector &v)
Writes a 1D vector to an HDF5 dataset at the given path, overwriting if it exists.
Definition hdf5.h:1013
Type read_attribute(const hdf5_node &node, const std::string &attr_name) const
Read metadata (attribute) attached to a specific node by reference.
Definition hdf5.h:1111
void write_mat(const hdf5_node &node, const Matrix &m)
Write a 2D matrix to an HDF5 dataset at the given node, overwriting if it exists.
Definition hdf5.h:1069
void delete_attribute(const std::string &path, const std::string &attr_name)
Delete an attribute attached to a specific node by path, if it exists.
Definition hdf5.h:1144
void delete_group(const hdf5_node &node)
Delete a group referenced by a node.
Definition hdf5.h:976
const hdf5_node & root() const
Get the base node containing the entire loaded file structure.
Definition hdf5.h:921
void delete_dataset(const std::string &path)
Delete a dataset at the given path if it exists.
Definition hdf5.h:1077
void create_group(const std::string &path)
Create a new group inside the file.
Definition hdf5.h:949
void create_group(const hdf5_node &parent, const std::string &child_name)
Create a new group using a parent node and a child name.
Definition hdf5.h:957
void write_attribute(const std::string &path, const std::string &attr_name, const Type &value)
Write or overwrite metadata (attribute) attached to a specific node by path.
Definition hdf5.h:1123
void write_attribute(const hdf5_node &node, const std::string &attr_name, const Type &value)
Write or overwrite metadata (attribute) attached to a specific node by reference.
Definition hdf5.h:1135
void write_mat(const std::string &path, const Matrix &m)
Write a 2D matrix to an HDF5 dataset at the given path, overwriting if it exists.
Definition hdf5.h:1058
void delete_dataset(const hdf5_node &node)
Delete a dataset at the given node if it exists.
Definition hdf5.h:1085
void delete_group(const std::string &path)
Delete a group inside the file.
Definition hdf5.h:969
void close()
Close the HDF5 file immediately.
Definition hdf5.h:903
Vector read_vec(const hdf5_node &node) const
Loads a 1D dataset array into a vector from the given node inside the HDF5 file.
Definition hdf5.h:1002
hdf5_file(const std::string &filename, bool write=false)
Open the file persistently.
Definition hdf5.h:874
void write_vec(const hdf5_node &node, const Vector &v)
Writes a 1D vector to the HDF5 dataset represented by the given node, overwriting if it exists.
Definition hdf5.h:1023
Matrix read_mat(const hdf5_node &node) const
Read a 2D dataset array into a matrix from the given node.
Definition hdf5.h:1047
hdf5_node & operator[](const std::string &child_name)
Access children of the root node by reference using dictionary-like syntax.
Definition hdf5.h:935
void refresh()
Refresh the cached tree structure from disk.
Definition hdf5.h:883
Type read_attribute(const std::string &path, const std::string &attr_name) const
Read metadata (attribute) attached to a specific node by path.
Definition hdf5.h:1099
const std::string & filename() const
Get the name of the file.
Definition hdf5.h:914
Vector read_vec(const std::string &path) const
Loads a 1D dataset array into a vector from the given path inside the HDF5 file.
Definition hdf5.h:990
std::string to_string() const
Converts an entire HDF5 file structure into a formatted tree string.
Definition hdf5.h:1162
friend std::ostream & operator<<(std::ostream &os, const hdf5_file &file)
Print the HDF5 file structure to a stream in a formatted tree representation.
Definition hdf5.h:1172
const hdf5_node & operator[](const std::string &child_name) const
Access children of the root node by const reference using dictionary-like syntax.
Definition hdf5.h:942
void delete_attribute(const hdf5_node &node, const std::string &attr_name)
Delete an attribute attached to a specific node by path, if it exists.
Definition hdf5.h:1153
Mathematical constants and default algorithm parameters.
void remove_link(const hdf5_handle &id, const std::string &path)
Removes a link (dataset or group) if it exists at the given path.
Definition hdf5.h:196
void suppress_errors()
Suppress HDF5 error messages by setting a custom dummy error handler.
Definition hdf5.h:188
hid_t hdf5_type()
Helper template to map C++ types to HDF5 Native Types.
Definition hdf5.h:175
Error handling for IO operations.
Matrix class and operations.
Matrix & mat_error(Matrix &m)
Overwrite the given matrix with the error matrix with NaN values on the diagonal and zeroes everywher...
Definition algebra.h:40
Vector & vec_error(Vector &v)
Overwrite the given vector with the error vector with NaN values.
Definition algebra.h:58
Input and output module.
Definition csv.h:23
hdf5_node hdf5_load(const hdf5_handle &id)
Recursively loads the structure of an active HDF5 group or file.
Definition hdf5.h:522
void hdf5_create_group(const hdf5_handle &id, const std::string &path)
Create a new group at the given path under an already open HDF5 location.
Definition hdf5.h:545
void hdf5_delete_dataset(const hdf5_handle &id, const std::string &path)
Deletes a dataset at the given path if it exists.
Definition hdf5.h:758
void hdf5_delete_group(const hdf5_handle &id, const std::string &path)
Delete a group (link) at the given path under an already open HDF5 location.
Definition hdf5.h:567
std::ostream & operator<<(std::ostream &os, const hdf5_node &node)
Prints the HDF5 file tree to a stream.
Definition hdf5.h:461
bool hdf5_is_valid(const hdf5_handle &handle)
Check whether a given HDF5 handle is valid.
Definition hdf5.h:513
Matrix hdf5_read_mat(const hdf5_handle &id, const std::string &path, Matrix &m)
Loads a 2D dataset array into a matrix.
Definition hdf5.h:777
@ FileNotFound
File or directory not found.
Definition error.h:31
@ WriteError
Error occurred while writing to the file or stream.
Definition error.h:40
@ FormatError
The file format is invalid or the data is corrupted.
Definition error.h:43
@ ReadError
Error occurred while reading from the file or stream.
Definition error.h:37
Type hdf5_read_attribute(const hdf5_handle &id, const std::string &path, const std::string &attr_name)
Reads an attribute attached to a specific node.
Definition hdf5.h:591
void hdf5_write_vec(const hdf5_handle &id, const std::string &path, const Vector &v)
Writes a 1D vector to an HDF5 dataset, overwriting if it exists.
Definition hdf5.h:733
void hdf5_delete_attribute(const hdf5_handle &id, const std::string &path, const std::string &attr_name)
Deletes an attribute attached to a specific node, if it exists.
Definition hdf5.h:620
Vector & hdf5_read_vec(const hdf5_handle &id, const std::string &path, Vector &v)
Loads a 1D dataset array into a vector.
Definition hdf5.h:675
HDF5NodeType
Type of node inside an HDF5 file.
Definition hdf5.h:35
@ DATASET
A node containing multi-dimensional array data.
Definition hdf5.h:44
@ UNKNOWN
An unsupported node type.
Definition hdf5.h:38
@ GROUP
A structural grouping containing other groups or datasets.
Definition hdf5.h:41
void hdf5_write_mat(const hdf5_handle &id, const std::string &path, const Matrix &m)
Writes a 2D matrix to an HDF5 dataset, overwriting if it exists.
Definition hdf5.h:833
void hdf5_write_attribute(const hdf5_handle &id, const std::string &path, const std::string &attr_name, const Type &value)
Writes or overwrites an attribute attached to a specific node.
Definition hdf5.h:648
hdf5_handle hdf5_open(const std::string &filename, bool write=false)
Open an HDF5 file with the given filename, returning a file handle.
Definition hdf5.h:473
Main namespace of the library which contains all functions and objects.
Definition algebra.h:27
TH_CONSTEXPR real nan()
Return a quiet NaN number in floating point representation.
Definition error.h:74
real root(real x, int n)
Compute the n-th root of x.
Definition real_analysis.h:823
RAII wrapper for managing HDF5 C-style handles, allowing direct API usage.
Definition hdf5.h:105
hid_t id
Underlying ID obtained by opening a file or group.
Definition hdf5.h:109
hdf5_handle(const hdf5_handle &)=delete
Prevent copying.
hdf5_handle(hdf5_handle &&other) noexcept
Move constructor.
Definition hdf5.h:141
hdf5_handle & operator=(hdf5_handle &&other) noexcept
Move assignment.
Definition hdf5.h:147
~hdf5_handle()
Safely decrements reference count and closes the handle if valid.
Definition hdf5.h:119
hdf5_handle(hid_t id=H5I_INVALID_HID)
Initialize the handle with a given HDF5 ID, for example obtained by opening a file or group,...
Definition hdf5.h:115
Represents a single node (group or dataset) in the HDF5 file hierarchy.
Definition hdf5.h:50
std::unordered_map< std::string, hdf5_node > children
For groups, the child nodes indexed by their local name.
Definition hdf5.h:69
std::vector< std::string > attributes
List of metadata attribute names attached to the node.
Definition hdf5.h:66
bool is_group() const noexcept
Check whether the node is a group.
Definition hdf5.h:73
std::vector< size_t > dimensions
For datasets, the dimensions of the array.
Definition hdf5.h:63
bool is_dataset() const noexcept
Check whether the node is a dataset.
Definition hdf5.h:79
HDF5NodeType type
The type of the node.
Definition hdf5.h:60
const hdf5_node & operator[](const std::string &child_name) const
Read-only dictionary-like access to child nodes by name.
Definition hdf5.h:97
std::string name
The local name of the node (e.g. "my_dataset").
Definition hdf5.h:53
hdf5_node & operator[](const std::string &child_name)
Dictionary-like access to child nodes by name.
Definition hdf5.h:88
std::string path
The absolute internal path to the node (e.g.
Definition hdf5.h:57
Vector class and operations.