Skip to content

Commit

Permalink
Encapsulate HDF5 module
Browse files Browse the repository at this point in the history
Rewrite h5xx and hdf5 bindings such that they no longer propagate
their compiler flags to the entire codebase.
  • Loading branch information
jngrad committed Jan 23, 2025
1 parent cb95fb9 commit e419967
Show file tree
Hide file tree
Showing 10 changed files with 207 additions and 128 deletions.
2 changes: 1 addition & 1 deletion src/core/io/writer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ if(ESPRESSO_BUILD_WITH_HDF5)
PRIVATE h5xx hdf5 espresso::cpp_flags espresso::h5xx_cpp_flags MPI::MPI_CXX
Boost::mpi Boost::filesystem espresso::utils espresso::config
espresso::instrumentation)
target_link_libraries(espresso_core PRIVATE espresso_hdf5)
target_link_libraries(espresso_core PUBLIC espresso_hdf5)
endif()
48 changes: 39 additions & 9 deletions src/core/io/writer/h5md_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "hdf5_patches.hpp" // must appear first

#include "h5md_core.hpp"
#include "h5md_dataset.hpp"
#include "h5md_specification.hpp"

#include "BoxGeometry.hpp"
#include "Particle.hpp"
#include "h5md_specification.hpp"
#include "lees_edwards/LeesEdwardsBC.hpp"

#include "config/version.hpp"
Expand All @@ -35,6 +38,8 @@
#include <boost/mpi/collectives.hpp>
#include <boost/multi_array.hpp>

#include <h5xx/h5xx.hpp>

#include <mpi.h>

#include <algorithm>
Expand All @@ -46,6 +51,11 @@
#include <string>
#include <vector>

namespace h5xx {
template <typename T, std::size_t size>
struct is_array<Utils::Vector<T, size>> : std::true_type {};
} // namespace h5xx

namespace Writer {
namespace H5md {

Expand Down Expand Up @@ -141,12 +151,12 @@ void File::load_datasets() {
for (auto const &d : m_h5md_specification.get_datasets()) {
if (d.is_link)
continue;
datasets[d.path()] = h5xx::dataset(m_h5md_file, d.path());
datasets[d.path()] = h5xx::dataset(*m_h5md_file, d.path());
}
}

void File::create_groups() {
h5xx::group group(m_h5md_file);
h5xx::group group(*m_h5md_file);
for (auto const &d : m_h5md_specification.get_datasets()) {
h5xx::group new_group(group, d.group);
}
Expand Down Expand Up @@ -190,13 +200,13 @@ void File::create_datasets() {
auto dataspace = h5xx::dataspace(create_dims(d.rank, d.data_dim), maxdims);
auto storage = hps::chunked(create_chunk_dims(d.rank, d.data_dim))
.set(hps::fill_value(-10));
datasets[d.path()] = h5xx::dataset(m_h5md_file, d.path(), d.type, dataspace,
datasets[d.path()] = h5xx::dataset(*m_h5md_file, d.path(), d.type, dataspace,
storage, H5P_DEFAULT, H5P_DEFAULT);
}
}

void File::load_file(const std::string &file_path) {
m_h5md_file = h5xx::file(file_path, m_comm, MPI_INFO_NULL, h5xx::file::out);
*m_h5md_file = h5xx::file(file_path, m_comm, MPI_INFO_NULL, h5xx::file::out);
load_datasets();
}

Expand Down Expand Up @@ -259,7 +269,7 @@ void File::create_hard_links() {
from = path_time.c_str();
}
assert(from != nullptr);
if (H5Lcreate_hard(m_h5md_file.hid(), from, m_h5md_file.hid(),
if (H5Lcreate_hard(m_h5md_file->hid(), from, m_h5md_file->hid(),
ds.path().c_str(), H5P_DEFAULT, H5P_DEFAULT) < 0) {
throw std::runtime_error("Error creating hard link for " + ds.path());
}
Expand All @@ -271,10 +281,10 @@ void File::create_file(const std::string &file_path) {
if (m_comm.rank() == 0)
write_script(file_path, m_absolute_script_path);
m_comm.barrier();
m_h5md_file = h5xx::file(file_path, m_comm, MPI_INFO_NULL, h5xx::file::out);
m_h5md_file = std::make_unique<h5xx::file>(file_path, m_comm, MPI_INFO_NULL, h5xx::file::out);
create_groups();
create_datasets();
write_attributes(m_h5md_file);
write_attributes(*m_h5md_file);
write_units();
create_hard_links();
}
Expand Down Expand Up @@ -478,7 +488,27 @@ void File::write_connectivity(const ParticleRange &particles) {
offset_bonds, count_bonds);
}

void File::flush() { m_h5md_file.flush(); }
void File::flush() { m_h5md_file->flush(); }

std::string File::file_path() const { return m_h5md_file->name(); }

File::File(std::string file_path, std::string script_path,
std::vector<std::string> const &output_fields, std::string mass_unit,
std::string length_unit, std::string time_unit, std::string force_unit,
std::string velocity_unit, std::string charge_unit)
: m_script_path(std::move(script_path)),
m_mass_unit(std::move(mass_unit)),
m_length_unit(std::move(length_unit)),
m_time_unit(std::move(time_unit)), m_force_unit(std::move(force_unit)),
m_velocity_unit(std::move(velocity_unit)),
m_charge_unit(std::move(charge_unit)),
m_comm(boost::mpi::communicator()),
m_fields(fields_list_to_bitfield(output_fields)),
m_h5md_specification(m_fields) {
init_file(file_path);
}

File::~File() = default;

} /* namespace H5md */
} /* namespace Writer */
30 changes: 8 additions & 22 deletions src/core/io/writer/h5md_core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
#include <boost/filesystem.hpp>
#include <boost/mpi/communicator.hpp>

#include <h5xx/h5xx.hpp>

#include <cstddef>
#include <stdexcept>
#include <string>
Expand All @@ -40,8 +38,8 @@
#include <utility>

namespace h5xx {
template <typename T, std::size_t size>
struct is_array<Utils::Vector<T, size>> : std::true_type {};
class file;
class dataset;
} // namespace h5xx

namespace Writer {
Expand Down Expand Up @@ -88,7 +86,7 @@ static std::unordered_map<std::string, H5MDOutputFields> const fields_map = {
inline auto fields_list_to_bitfield(std::vector<std::string> const &fields) {
unsigned int bitfield = H5MD_OUT_NONE;
for (auto const &field_name : fields) {
if (fields_map.count(field_name) == 0) {
if (not fields_map.contains(field_name)) {
throw std::invalid_argument("Unknown field '" + field_name + "'");
}
bitfield |= fields_map.at(field_name);
Expand All @@ -112,24 +110,12 @@ class File {
* @param force_unit The unit for force.
* @param velocity_unit The unit for velocity.
* @param charge_unit The unit for charge.
* @param comm The MPI communicator.
*/
File(std::string file_path, std::string script_path,
std::vector<std::string> const &output_fields, std::string mass_unit,
std::string length_unit, std::string time_unit, std::string force_unit,
std::string velocity_unit, std::string charge_unit,
boost::mpi::communicator comm = boost::mpi::communicator())
: m_script_path(std::move(script_path)),
m_mass_unit(std::move(mass_unit)),
m_length_unit(std::move(length_unit)),
m_time_unit(std::move(time_unit)), m_force_unit(std::move(force_unit)),
m_velocity_unit(std::move(velocity_unit)),
m_charge_unit(std::move(charge_unit)), m_comm(std::move(comm)),
m_fields(fields_list_to_bitfield(output_fields)),
m_h5md_specification(m_fields) {
init_file(file_path);
}
~File() = default;
std::string velocity_unit, std::string charge_unit);
~File();

/**
* @brief Method to perform the renaming of the temporary file from
Expand All @@ -151,7 +137,7 @@ class File {
* @brief Retrieve the path to the hdf5 file.
* @return The path as a string.
*/
auto file_path() const { return m_h5md_file.name(); }
std::string file_path() const;

/**
* @brief Retrieve the path to the simulation script.
Expand Down Expand Up @@ -272,9 +258,9 @@ class File {
unsigned int m_fields;
std::string m_backup_filename;
boost::filesystem::path m_absolute_script_path;
h5xx::file m_h5md_file;
std::unique_ptr<h5xx::file> m_h5md_file;
std::unordered_map<std::string, h5xx::dataset> datasets;
H5MD_Specification m_h5md_specification;
Specification m_h5md_specification;
};

struct incompatible_h5mdfile : public std::exception {
Expand Down
45 changes: 45 additions & 0 deletions src/core/io/writer/h5md_dataset.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2010-2025 The ESPResSo project
* Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
* Max-Planck-Institute for Polymer Research, Theory Group
*
* This file is part of ESPResSo.
*
* ESPResSo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ESPResSo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "hdf5_patches.hpp"

#include <hdf5.h>

#include <string>

namespace Writer {
namespace H5md {

struct Dataset {
std::string path() const { return group + "/" + name; }

std::string group;
std::string name;
hsize_t rank;
hid_t type;
hsize_t data_dim;
bool is_link;
};

} // namespace H5md
} // namespace Writer
27 changes: 24 additions & 3 deletions src/core/io/writer/h5md_specification.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2022 The ESPResSo project
* Copyright (C) 2010-2025 The ESPResSo project
* Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
* Max-Planck-Institute for Polymer Research, Theory Group
*
Expand All @@ -19,17 +19,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "h5md_specification.hpp"
#include "hdf5_patches.hpp" // must appear first

#include "h5md_core.hpp"
#include "h5md_dataset.hpp"
#include "h5md_specification.hpp"

#include <h5xx/h5xx.hpp>

#include <hdf5.h>

#include <algorithm>
#include <string>
#include <utility>

namespace Writer {
namespace H5md {

H5MD_Specification::H5MD_Specification(unsigned int fields) {
Specification::Specification(unsigned int fields) {
auto const add_time_series = [this](Dataset &&dataset, bool link = true) {
auto const group = dataset.group;
m_datasets.push_back(std::move(dataset));
Expand Down Expand Up @@ -89,5 +96,19 @@ H5MD_Specification::H5MD_Specification(unsigned int fields) {
}
}

bool Specification::is_compliant(std::string const &filename) const {
h5xx::file h5md_file(filename, h5xx::file::in);

auto const all_groups_exist =
std::ranges::all_of(m_datasets, [&h5md_file](auto const &d) {
return h5xx::exists_group(h5md_file, d.group);
});
auto const all_datasets_exist =
std::ranges::all_of(m_datasets, [&h5md_file](auto const &d) {
return h5xx::exists_dataset(h5md_file, d.path());
});
return all_groups_exist and all_datasets_exist;
}

} // namespace H5md
} // namespace Writer
48 changes: 6 additions & 42 deletions src/core/io/writer/h5md_specification.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2022 The ESPResSo project
* Copyright (C) 2010-2025 The ESPResSo project
* Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010
* Max-Planck-Institute for Polymer Research, Theory Group
*
Expand All @@ -21,62 +21,26 @@

#pragma once

// guard for hdf5.h
#if not defined(_H5public_H)
#ifdef OMPI_SKIP_MPICXX
#undef OMPI_SKIP_MPICXX
#endif
#ifdef MPICH_SKIP_MPICXX
#undef MPICH_SKIP_MPICXX
#endif
#endif // not defined(_H5public_H)
#include <H5public.h>

#include <h5xx/h5xx.hpp>

#include <algorithm>
#include <string>
#include <vector>

namespace Writer {
namespace H5md {

struct Dataset;

/**
* @brief Layout information for H5MD files.
* In order to add a new particle property you have to add an entry to the
* H5MD_Specification::DATASETS member and extend the File::write() and the
* File::write_units() functions accordingly.
*/
struct H5MD_Specification {

struct Dataset {
std::string path() const { return group + "/" + name; }

std::string group;
std::string name;
hsize_t rank;
hid_t type;
hsize_t data_dim;
bool is_link;
};

H5MD_Specification(unsigned int fields);
struct Specification {
Specification(unsigned int fields);

auto const &get_datasets() const { return m_datasets; }

bool is_compliant(std::string const &filename) const {
h5xx::file h5md_file(filename, h5xx::file::in);

auto const all_groups_exist = std::all_of(
m_datasets.begin(), m_datasets.end(), [&h5md_file](auto const &d) {
return h5xx::exists_group(h5md_file, d.group);
});
auto const all_datasets_exist = std::all_of(
m_datasets.begin(), m_datasets.end(), [&h5md_file](auto const &d) {
return h5xx::exists_dataset(h5md_file, d.path());
});
return all_groups_exist and all_datasets_exist;
}
bool is_compliant(std::string const &filename) const;

private:
std::vector<Dataset> m_datasets;
Expand Down
Loading

0 comments on commit e419967

Please sign in to comment.