You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

364 lines
15 KiB

#ifndef MEDUSA_BITS_IO_HDF_HPP_
#define MEDUSA_BITS_IO_HDF_HPP_
/**
* @file
* Implementation of HDF I/O utilities
*/
#include "HDF_fwd.hpp"
#include <medusa/bits/utils/assert.hpp>
#include <tuple>
namespace mm {
template <class T>
T HDF::readAttribute(const std::string& attr_name) const {
assert_msg(H5Iis_valid(group), "Group id %d invalid. Did you open a group before reading?",
group);
hid_t attr = H5Aopen(group, attr_name.c_str(), H5P_DEFAULT);
assert_msg(attr >= 0, "Attribute '%s' could not be accessed in group '%s' in file '%s'.",
attr_name, group_name_, filename_);
hid_t type = H5Aget_type(attr);
assert_msg(type >= 0, "Failed getting type of attribute '%s' in group '%s' in file '%s'.",
attr_name, group_name_, filename_);
T result;
herr_t status = H5Aread(attr, type, &result);
assert_msg(status >= 0, "Failed reading attribute '%s' from group '%s' in file '%s'.",
attr_name, groupName(), filename_);
H5Tclose(type);
H5Aclose(attr);
return result;
}
template <class T>
void HDF::writeAttribute(const std::string& attr_name, const T& value, const hid_t& type,
bool overwrite) const {
assert_msg(H5Iis_valid(group), "Group id %d invalid. Did you open a group before writing?",
group);
if (H5Aexists(group, attr_name.c_str())) {
if (!overwrite) {
assert_msg(false, "Attribute '%s' in group '%s' in file '%s' already exists. To "
"overwrite its contents use parameter overwrite=true.",
attr_name, group_name_, filename_);
return;
}
herr_t status = H5Adelete(group, attr_name.c_str());
assert_msg(status >= 0, "Failed deleting existing attribute '%s' in group '%s' in "
"file '%s' before writing a new one.",
attr_name, group_name_, filename_);
}
hid_t space = H5Screate(H5S_SCALAR);
hid_t attr = H5Acreate(group, attr_name.c_str(), type, space, H5P_DEFAULT, H5P_DEFAULT);
assert_msg(attr >= 0, "Failed creating attribute '%s' in group '%s' in file '%s'.",
attr_name, group_name_, filename_);
herr_t status = H5Awrite(attr, type, &value);
assert_msg(status >= 0, "Failed writing attribute '%s' to group '%s' in file '%s'.",
attr_name, group_name_, filename_);
H5Sclose(space);
H5Aclose(attr);
}
template <typename T>
std::pair<std::vector<hsize_t>, std::vector<T>>
HDF::readLinearArray(const std::string& dataset_name) const {
assert_msg(H5Iis_valid(group), "Group id %d invalid. Did you open a group before reading?",
group);
hid_t dataset = H5Dopen(group, dataset_name.c_str(), H5P_DEFAULT);
assert_msg(dataset >= 0, "Dataset '%s' could not be accessed in group '%s' in file '%s'.",
dataset_name, group_name_, filename_);
hid_t dataspace = H5Dget_space(dataset);
const int ndims = H5Sget_simple_extent_ndims(dataspace);
std::vector<hsize_t> dims(ndims);
H5Sget_simple_extent_dims(dataspace, dims.data(), nullptr); // read dimension into dims
hsize_t size = 1;
for (int d = 0; d < ndims; ++d) size *= dims[d];
std::vector<T> linear_value(size);
hid_t type = H5Dget_type(dataset);
herr_t status = H5Dread(dataset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT,
linear_value.data());
assert_msg(status >= 0, "Failed reading dataset '%s' from group '%s' in file '%s'.",
dataset_name, group_name_, filename_);
H5Tclose(type);
H5Sclose(dataspace);
H5Dclose(dataset);
return {dims, linear_value};
}
template <typename T>
std::vector<T> HDF::read1DArray(const std::string& dataset_name) const {
std::vector<hsize_t> dims;
std::vector<T> value;
std::tie(dims, value) = readLinearArray<T>(dataset_name);
assert_msg(dims.size() == 1, "This function is for one dimensional arrays only, got %d-D "
"array with sizes %s.", dims.size(), dims);
return value;
}
template <typename T>
std::vector<std::vector<T>> HDF::read2DArray(const std::string& dataset_name) const {
std::vector<hsize_t> dims;
std::vector<T> linear_value;
std::tie(dims, linear_value) = readLinearArray<T>(dataset_name);
assert_msg(dims.size() == 2, "This function is for two dimensional arrays only, got %d-D "
"array with sizes %s.", dims.size(), dims);
hsize_t cols = dims[0];
hsize_t rows = dims[1];
std::vector<std::vector<T>> value(rows, std::vector<T>(cols));
for (hsize_t i = 0; i < rows; i++)
for (hsize_t j = 0; j < cols; j++)
value[i][j] = linear_value[j * rows + i];
return value;
}
template <typename T>
std::vector<std::vector<std::vector<T>>> HDF::read3DArray(const std::string& dataset_name) const {
std::vector<hsize_t> dims;
std::vector<T> linear_value;
std::tie(dims, linear_value) = readLinearArray<T>(dataset_name);
assert_msg(dims.size() == 3, "This function is for three dimensional arrays only, got %d-D "
"array with sizes %s.", dims.size(), dims);
hsize_t tubes = dims[0];
hsize_t cols = dims[1];
hsize_t rows = dims[2];
std::vector<std::vector<std::vector<T>>> value(rows, std::vector<std::vector<T>>(
cols, std::vector<T>(tubes)));
for (hsize_t i = 0; i < rows; i++)
for (hsize_t j = 0; j < cols; j++)
for (hsize_t k = 0; k < tubes; k++)
value[i][j][k] = linear_value[k * cols * rows + j * rows + i];
return value;
}
template <int dim, typename T>
void HDF::writeLinearArray(const std::string& dataset_name, const T* value,
const std::array<hsize_t, dim>& sizes, hid_t type,
bool overwrite) const {
assert_msg(H5Iis_valid(group), "Group id %d invalid. Did you open a group before writing?",
group);
hid_t dataset;
if (H5Lexists(group, dataset_name.c_str(), H5P_DEFAULT)) {
if (!overwrite) {
assert_msg(false, "Dataset '%s' in group '%s' in file '%s' already exists. To "
"overwrite its contents use parameter overwrite=true.",
dataset_name, group_name_, filename_);
return;
}
dataset = H5Dopen(group, dataset_name.c_str(), H5P_DEFAULT);
assert_msg(dataset >= 0, "Failed opening dataset '%s' in group '%s' in file '%s'.",
dataset_name, group_name_, filename_);
hid_t dataspace = H5Dget_space(dataset);
const int ndims = H5Sget_simple_extent_ndims(dataspace);
assert_msg(ndims == dim, "Expected %d-dim array, got %d-dim.", dim, ndims);
hsize_t cur_dims[dim]; // NOLINT(*)
H5Sget_simple_extent_dims(dataspace, cur_dims, nullptr); // read dimension into sizes
for (int d = 0; d < dim; ++d) {
assert_msg(cur_dims[d] == sizes[d],
"Only data of same old_size can be overwritten, but new dataset has "
"old_size '%d' and existing has old_size '%d'.", sizes[d], cur_dims[d]);
}
H5Sclose(dataspace);
} else {
hid_t dataspace;
const int rank = dim;
const hsize_t* max_dims = nullptr; // same as sizes
dataspace = H5Screate_simple(rank, &sizes[0], max_dims);
dataset = H5Dcreate(group, dataset_name.c_str(), type, dataspace,
H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
assert_msg(dataset >= 0, "Failed creating dataset '%s' in group '%s' in file '%s'.",
dataset_name, group_name_, filename_);
H5Sclose(dataspace);
}
int size = 1; for (int d = 0; d < dim; ++d) { size *= sizes[d]; }
herr_t status = H5Dwrite(dataset, type, H5S_ALL, H5S_ALL, H5P_DEFAULT, value);
assert_msg(status >= 0, "Failed writing dataset '%s' to group '%s' in file '%s'.",
dataset_name, group_name_, filename_);
H5Dclose(dataset);
}
template <typename T, typename array_t>
void HDF::write1DArray(const std::string& dataset_name, const array_t& value, hid_t type,
bool overwrite) const {
hsize_t size = value.size();
std::vector<T> cast_value(size);
for (hsize_t j = 0; j < size; j++) { cast_value[j] = static_cast<T>(value[j]); }
writeLinearArray<1>(dataset_name, cast_value.data(), {size}, type, overwrite);
}
template <typename T, class array_t>
void HDF::write2DArray(const std::string& dataset_name, const array_t& value, hid_t type,
bool overwrite, std::false_type) const {
size_t rows = value.size();
size_t cols = (rows == 0) ? 0 : value[0].size();
std::vector<T> linear_value(rows * cols);
for (size_t i = 0; i < rows; i++) {
assert_msg(static_cast<size_t>(value[i].size()) == cols,
"Not all subarrays are of the same size, subarray 0 has size %d, but subarray "
"%d has size %d. Maybe use pad to fix raggedness?", cols, i, value[i].size());
for (size_t j = 0; j < cols; j++) {
linear_value[j * rows + i] = static_cast<T>(value[i][j]);
}
}
writeLinearArray<2>(dataset_name, linear_value.data(), {cols, rows}, type, overwrite);
}
template <typename T, class array_t>
void HDF::write3DArray(const std::string& dataset_name, const array_t& value, hid_t type,
bool overwrite) const {
size_t rows = value.size();
size_t cols = (rows == 0) ? 0 : value[0].size();
size_t tubes = (cols == 0) ? 0 : value[0][0].size();
std::vector<T> linear_value(rows * cols * tubes);
for (size_t i = 0; i < rows; i++) {
assert_msg(static_cast<size_t>(value[i].size()) == cols,
"Not all subarrays are of the same size, subarray 0 has size %d, but subarray "
"%d has size %d. Maybe use pad to fix raggedness?", cols, i, value[i].size());
for (size_t j = 0; j < cols; j++) {
assert_msg(static_cast<size_t>(value[i][j].size()) == tubes,
"Not all subarrays are of the same size, subarray (0, 0) has size %d, "
"but subarray (%d, %d) has size %d. Maybe use pad to fix raggedness?",
tubes, i, j, value[i][j].size());
for (size_t k = 0; k < tubes; ++k) {
linear_value[k * cols * rows + j * rows + i] = static_cast<T>(value[i][j][k]);
}
}
}
writeLinearArray<3>(dataset_name, linear_value.data(), {tubes, cols, rows}, type, overwrite);
}
template <typename array_t>
void HDF::writeIntArray(const std::string& dataset_name, const array_t& value,
bool overwrite) const {
write1DArray<int>(dataset_name, value, H5T_NATIVE_INT, overwrite);
}
template <typename array_t>
void HDF::writeDoubleArray(const std::string& dataset_name, const array_t& value,
bool overwrite) const {
write1DArray<double>(dataset_name, value, H5T_NATIVE_DOUBLE, overwrite);
}
template <typename array_t>
void HDF::writeFloatArray(const std::string& dataset_name, const array_t& value,
bool overwrite) const {
write1DArray<float>(dataset_name, value, H5T_NATIVE_FLOAT, overwrite);
}
template <typename array_t>
void HDF::writeInt2DArray(const std::string& dataset_name, const array_t& value,
bool overwrite) const {
write2DArray<int>(dataset_name, value, H5T_NATIVE_INT, overwrite);
}
template <typename array_t>
void HDF::writeDouble2DArray(const std::string& dataset_name, const array_t& value,
bool overwrite) const {
write2DArray<double>(dataset_name, value, H5T_NATIVE_DOUBLE, overwrite);
}
template <typename array_t>
void HDF::writeFloat2DArray(const std::string& dataset_name, const array_t& value,
bool overwrite) const {
write2DArray<float>(dataset_name, value, H5T_NATIVE_FLOAT, overwrite);
}
template <typename array_t>
void HDF::writeInt3DArray(const std::string& dataset_name, const array_t& value,
bool overwrite) const {
write3DArray<int>(dataset_name, value, H5T_NATIVE_INT, overwrite);
}
template <typename array_t>
void HDF::writeDouble3DArray(const std::string& dataset_name, const array_t& value,
bool overwrite) const {
write3DArray<double>(dataset_name, value, H5T_NATIVE_DOUBLE, overwrite);
}
template <typename array_t>
void HDF::writeFloat3DArray(const std::string& dataset_name, const array_t& value,
bool overwrite) const {
write3DArray<float>(dataset_name, value, H5T_NATIVE_FLOAT, overwrite);
}
template <typename conf_t>
void HDF::writeXML(const std::string& name, const conf_t& conf, bool overwrite) {
std::string group = group_name_;
assert_msg(!group.empty(), "Open a group before writing.");
if (group.back() == '/') openGroup(group+name);
else openGroup(group+'/'+name);
std::vector<std::pair<std::string, std::string>> data = conf.getAll();
for (const auto& kv : data) {
try {
std::string::size_type num_read;
double x = std::stod(kv.second, &num_read);
if (num_read < kv.second.size()) { // some characters were not read, it's a string
throw std::invalid_argument(kv.second);
}
writeDoubleAttribute(kv.first, x, overwrite);
} catch (const std::invalid_argument&) {
writeStringAttribute(kv.first, kv.second, overwrite);
}
}
openGroup(group);
}
template <typename SparseMatrixType>
void HDF::writeSparseMatrix(const std::string& name, SparseMatrixType& matrix, bool one_based,
bool overwrite) {
std::vector<std::array<double, 3>> triplets(matrix.nonZeros());
int c = 0;
for (int k = 0; k < matrix.outerSize(); ++k) {
for (typename SparseMatrixType::InnerIterator it(matrix, k); it; ++it) {
triplets[c][0] = one_based+it.row();
triplets[c][1] = one_based+it.col();
triplets[c][2] = it.value();
++c;
}
}
writeDouble2DArray(name, triplets, overwrite);
}
template <typename domain_t>
void HDF::writeDomain(const std::string& name, const domain_t& domain, bool overwrite) {
std::string group = group_name_;
assert_msg(!group.empty(), "Open a group before writing.");
if (group.back() == '/') openGroup(group+name);
else openGroup(group+'/'+name);
writeDouble2DArray("pos", domain.positions(), overwrite);
writeIntAttribute("N", domain.size(), overwrite);
writeIntArray("types", domain.types(), overwrite);
writeIntArray("bmap", domain.bmap(), overwrite);
writeDouble2DArray("normals", domain.normals(), overwrite);
openGroup(group);
}
template <typename timer_t>
void HDF::writeTimer(const std::string& name, const timer_t& timer, bool overwrite) {
std::string group = group_name_;
assert_msg(!group.empty(), "Open a group before writing.");
if (group.back() == '/') openGroup(group+name);
else openGroup(group+'/'+name);
std::vector<std::string> labels = timer.labels();
int size = labels.size();
if (size == 0) return;
for (int i = 1; i < size; ++i) {
writeDoubleAttribute(labels[i-1]+"-"+labels[i], timer.duration(labels[i-1], labels[i]),
overwrite);
}
writeDoubleAttribute("total", timer.duration(labels.front(), labels.back()), overwrite);
openGroup(group);
}
} // namespace mm
#endif // MEDUSA_BITS_IO_HDF_HPP_