File fileloaderhdf5.h
File List > code_source > templat > include > TempLat > lattice > IO > HDF5 > fileloaderhdf5.h
Go to the documentation of this file
#ifndef TEMPLAT_LATTICE_IO_HDF5_FILELOADERHDF5_H
#define TEMPLAT_LATTICE_IO_HDF5_FILELOADERHDF5_H
/* This file is part of TempLat, available at https://cosmolattice.github.io/templat .
Copyright 2021-2026 The TempLat authors, see AUTHORS.md.
Released under the MIT license, see LICENSE.md. */
// File info: Main contributor(s): Adrien Florio, Year: 2020
#ifdef HAVE_HDF5
#include <algorithm>
#include <cstring>
#include <iomanip>
#include <sstream>
#include <vector>
#include "TempLat/lattice/IO/HDF5/helpers/blockgeometry.h"
#include "TempLat/lattice/IO/HDF5/helpers/hdf5file.h"
#include "TempLat/lattice/algebra/helpers/getstring.h"
#include "TempLat/lattice/algebra/helpers/getgetreturntype.h"
#include "TempLat/parameters/parameterparser.h"
#include "TempLat/parallel/device.h"
#include "TempLat/parallel/device_iteration.h"
#include "TempLat/parallel/device_memory.h"
namespace TempLat
{
class FileLoaderHDF5
{
public:
// Put public methods here. These should change very little over time.
FileLoaderHDF5() = default;
void open(std::string fn, FileMode flag = ReadOnly) { mFile.open(fn, flag); }
#ifdef HAVE_MPI
void setComm(MPI_Comm comm) { mFile.setComm(comm); }
#endif
void close() { mFile.close(); }
void reset() { this->close(); }
void load(ParameterParser &par)
{
mDataset = mFile.openDataset("Parameters");
std::vector<std::string> parStr;
char tmp[HDF5TypeConstant::FixedSizeStringLength];
// get number of parameters stored.
hid_t dspace = H5Dget_space(mDataset);
const int ndims = H5Sget_simple_extent_ndims(dspace);
std::vector<hsize_t> dims(ndims);
H5Sget_simple_extent_dims(dspace, dims.data(), NULL);
auto nElements = dims[0];
for (size_t i = 0; i < nElements; ++i) {
mDataset.readElement(tmp, std::vector<hsize_t>(1, i));
parStr.emplace_back(tmp);
}
H5Sclose(dspace);
par.addFromVector(parStr);
mDataset.close();
}
template <typename R> void load(R &t, std::string name)
{ // used to store a number. The name is the one of the dataset which contains this number.
mDataset = mFile.openDataset(name);
mDataset.readElement(&t, std::vector<hsize_t>(1, 0));
mDataset.close();
}
void load(double &value, const std::string &name)
{
// Use "/" prefix for root group (matching openDataset pattern)
std::string fullName = "/" + name;
auto dataset = H5Dopen2(mFile.getHandle(), fullName.c_str(), H5P_DEFAULT);
H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
H5Dclose(dataset);
}
void loadScalarU64(uint64_t &value, const std::string &name)
{
std::string fullName = "/" + name;
auto dataset = H5Dopen2(mFile.getHandle(), fullName.c_str(), H5P_DEFAULT);
H5Dread(dataset, H5T_NATIVE_UINT64, H5S_ALL, H5S_ALL, H5P_DEFAULT, &value);
H5Dclose(dataset);
}
bool datasetExists(const std::string &name)
{
std::string fullName = "/" + name;
return H5Lexists(mFile.getHandle(), fullName.c_str(), H5P_DEFAULT) > 0;
}
std::vector<hsize_t> getDatasetDims(const std::string &name)
{
std::string fullName = "/" + name;
auto dataset = H5Dopen2(mFile.getHandle(), fullName.c_str(), H5P_DEFAULT);
auto filespace = H5Dget_space(dataset);
const int ndims = H5Sget_simple_extent_ndims(filespace);
std::vector<hsize_t> dims(ndims > 0 ? ndims : 0);
if (ndims > 0) H5Sget_simple_extent_dims(filespace, dims.data(), nullptr);
H5Sclose(filespace);
H5Dclose(dataset);
return dims;
}
void loadWholeArray(std::vector<double> &values, const std::string &name)
{
std::string fullName = "/" + name;
auto dataset = H5Dopen2(mFile.getHandle(), fullName.c_str(), H5P_DEFAULT);
auto filespace = H5Dget_space(dataset);
hsize_t dims[1];
H5Sget_simple_extent_dims(filespace, dims, nullptr);
values.assign(static_cast<size_t>(dims[0]), 0.0);
auto plist = H5Pcreate(H5P_DATASET_XFER);
#ifdef HAVE_MPI
H5Pset_dxpl_mpio(plist, H5FD_MPIO_INDEPENDENT);
#endif
H5Dread(dataset, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, plist, values.data());
H5Pclose(plist);
H5Sclose(filespace);
H5Dclose(dataset);
}
void load(std::string &str, const std::string &name)
{
constexpr size_t LargeStringLength = 16384; // 16KB for combined RNG states
std::vector<char> buffer(LargeStringLength, 0);
// Use "/" prefix for root group (matching openDataset pattern)
std::string fullName = "/" + name;
auto dataset = H5Dopen2(mFile.getHandle(), fullName.c_str(), H5P_DEFAULT);
auto dtype = H5Dget_type(dataset);
size_t typeSize = H5Tget_size(dtype);
// Create memory type matching the file type size
auto memtype = H5Tcopy(H5T_C_S1);
H5Tset_size(memtype, typeSize);
H5Dread(dataset, memtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, buffer.data());
H5Tclose(memtype);
H5Tclose(dtype);
H5Dclose(dataset);
str = std::string(buffer.data());
}
void loadPerRank(std::string &str, const std::string &name, int mpiRank)
{
constexpr size_t LargeStringLength = 16384;
std::vector<char> buffer(LargeStringLength, 0);
std::string fullName = "/" + name;
auto dataset = H5Dopen2(mFile.getHandle(), fullName.c_str(), H5P_DEFAULT);
auto dtype = H5Dget_type(dataset);
size_t typeSize = H5Tget_size(dtype);
// Create memory type matching file type size
auto memtype = H5Tcopy(H5T_C_S1);
H5Tset_size(memtype, typeSize);
// Select hyperslab for this rank's element
auto filespace = H5Dget_space(dataset);
hsize_t start[1] = {static_cast<hsize_t>(mpiRank)};
hsize_t count[1] = {1};
H5Sselect_hyperslab(filespace, H5S_SELECT_SET, start, nullptr, count, nullptr);
// Memory space for single element
hsize_t memDims[1] = {1};
auto memspace = H5Screate_simple(1, memDims, nullptr);
// Read with independent I/O
auto plist = H5Pcreate(H5P_DATASET_XFER);
#ifdef HAVE_MPI
H5Pset_dxpl_mpio(plist, H5FD_MPIO_INDEPENDENT);
#endif
H5Dread(dataset, memtype, memspace, filespace, plist, buffer.data());
H5Pclose(plist);
H5Sclose(memspace);
H5Sclose(filespace);
H5Tclose(memtype);
H5Tclose(dtype);
H5Dclose(dataset);
str = std::string(buffer.data());
}
void loadPerRank(double &value, const std::string &name, int mpiRank)
{
std::string fullName = "/" + name;
auto dataset = H5Dopen2(mFile.getHandle(), fullName.c_str(), H5P_DEFAULT);
// Select hyperslab for this rank's element
auto filespace = H5Dget_space(dataset);
hsize_t start[1] = {static_cast<hsize_t>(mpiRank)};
hsize_t count[1] = {1};
H5Sselect_hyperslab(filespace, H5S_SELECT_SET, start, nullptr, count, nullptr);
// Memory space for single element
hsize_t memDims[1] = {1};
auto memspace = H5Screate_simple(1, memDims, nullptr);
// Read with independent I/O
auto plist = H5Pcreate(H5P_DATASET_XFER);
#ifdef HAVE_MPI
H5Pset_dxpl_mpio(plist, H5FD_MPIO_INDEPENDENT);
#endif
H5Dread(dataset, H5T_NATIVE_DOUBLE, memspace, filespace, plist, &value);
H5Pclose(plist);
H5Sclose(memspace);
H5Sclose(filespace);
H5Dclose(dataset);
}
void loadRNGStateBinary(std::string &textState, const std::string &name, int mpiRank)
{
std::string fullName = "/" + name;
auto dataset = H5Dopen2(mFile.getHandle(), fullName.c_str(), H5P_DEFAULT);
auto filespace = H5Dget_space(dataset);
hsize_t dims[2];
H5Sget_simple_extent_dims(filespace, dims, nullptr);
size_t stateSize = dims[1];
hsize_t start[2] = {static_cast<hsize_t>(mpiRank), 0};
hsize_t count[2] = {1, stateSize};
H5Sselect_hyperslab(filespace, H5S_SELECT_SET, start, nullptr, count, nullptr);
hsize_t memDims[1] = {stateSize};
auto memspace = H5Screate_simple(1, memDims, nullptr);
std::vector<uint64_t> binaryState(stateSize);
auto plist = H5Pcreate(H5P_DATASET_XFER);
#ifdef HAVE_MPI
H5Pset_dxpl_mpio(plist, H5FD_MPIO_INDEPENDENT);
#endif
H5Dread(dataset, H5T_NATIVE_UINT64, memspace, filespace, plist, binaryState.data());
H5Pclose(plist);
H5Sclose(memspace);
H5Dclose(dataset);
H5Sclose(filespace);
std::ostringstream oss;
if (stateSize == 313) {
for (size_t i = 0; i < stateSize; ++i) {
if (i > 0) oss << " ";
oss << binaryState[i];
}
} else if (stateSize == 316) {
for (size_t i = 0; i < 313; ++i) {
if (i > 0) oss << " ";
oss << binaryState[i];
}
oss << "\n";
oss << binaryState[313] << " " << binaryState[314] << " ";
if (binaryState[314]) {
double cachedValue;
std::memcpy(&cachedValue, &binaryState[315], sizeof(double));
oss << std::setprecision(17) << cachedValue;
}
} else {
for (size_t i = 0; i < stateSize; ++i) {
if (i > 0) oss << " ";
oss << binaryState[i];
}
}
textState = oss.str();
}
template <typename R> void load(R r)
{
mDataset = mFile.openDataset(GetString::get(r));
loadBlock(r);
mDataset.close();
}
template <typename R> void loadBlock(R r)
{
auto toolBox = r.getToolBox();
constexpr size_t NDim = std::decay_t<decltype(*toolBox)>::NDim;
using vType = typename GetGetReturnType<R>::type;
const auto geo = computeDenseBlockGeometry<NDim>(toolBox->mLayouts.getConfigSpaceStarts(), // Local mpi offset.
toolBox->mLayouts.getConfigSpaceSizes(), // Local mpi sizes.
toolBox->mNGridPointsVec);
if (geo.isEmpty()) return;
const auto mLayout = toolBox->mLayouts.getConfigSpaceLayout();
device::IdxArray<NDim> memFirst = geo.first;
device::apply([&](auto... idx) { mLayout.putMemoryIndexFromSpatialLocationInto(memFirst, idx...); }, memFirst);
// Row-major strides of the staging buffer, matching H5Screate_simple's C ordering.
device::IdxArray<NDim> bstride{};
bstride[NDim - 1] = 1;
for (size_t d = NDim - 1; d > 0; --d)
bstride[d - 1] = bstride[d] * geo.count[d];
const device::Idx sliceElems = (NDim > 1) ? bstride[0] : 1;
const device::Idx maxSlices =
std::max<device::Idx>(1, (device::Idx)(mStagingBudgetBytes / std::max<size_t>(1, sliceElems * sizeof(vType))));
const device::IdxArray<NDim> starts{}; // foreach's third argument is an EXTENT, not a stop.
const device::IdxArray<NDim> stepMem = geo.step;
auto view = r.getView();
for (device::Idx c0 = 0; c0 < geo.count[0]; c0 += maxSlices) {
const device::Idx n0 = std::min(maxSlices, geo.count[0] - c0);
device::IdxArray<NDim> extents = geo.count;
device::IdxArray<NDim> offsets = geo.offset;
device::IdxArray<NDim> memBase = memFirst;
extents[0] = n0;
offsets[0] += c0;
memBase[0] += c0 * geo.step[0];
const device::Idx nElems = n0 * sliceElems;
std::vector<vType> host(nElems);
mDataset.readSlices(host, extents, offsets);
// Rank 1 keeps the staging view contiguous, so copyHostToDevice takes its fast path.
device::memory::NDView<vType, 1> device_buf("LoadBlockBuffer", nElems);
device::memory::copyHostToDevice(host.data(), device_buf);
auto functor = DEVICE_LAMBDA(device::IdxArray<NDim> idx)
{
device::IdxArray<NDim> mem{};
device::Idx linear = 0;
for (size_t d = 0; d < NDim; ++d) {
mem[d] = memBase[d] + idx[d] * stepMem[d];
linear += idx[d] * bstride[d];
}
device::apply([&](const auto &...i) { view(i...) = device_buf(linear); }, mem);
};
device::iteration::foreach<NDim>("LoadBlockBufferScattering", starts, extents, functor);
}
}
private:
/* Put all member variables and private methods here. These may change arbitrarily. */
HDF5File mFile;
HDF5Dataset mDataset;
size_t mStagingBudgetBytes = 256ull << 20;
};
} // namespace TempLat
#endif
#endif