9#ifndef GRIDFORMAT_VTK_HDF_COMMON_HPP_
10#define GRIDFORMAT_VTK_HDF_COMMON_HPP_
20#include <gridformat/common/hdf5.hpp>
22#include <gridformat/common/precision.hpp>
23#include <gridformat/common/lazy_field.hpp>
24#include <gridformat/common/range_field.hpp>
25#include <gridformat/common/field_transformations.hpp>
26#include <gridformat/common/multi_string.hpp>
27#include <gridformat/common/string_conversion.hpp>
28#include <gridformat/common/exceptions.hpp>
29#include <gridformat/common/logging.hpp>
31#include <gridformat/grid/grid.hpp>
32#include <gridformat/parallel/communication.hpp>
33#include <gridformat/parallel/helpers.hpp>
54 const bool is_parallel;
55 const std::vector<std::size_t> rank_cells;
56 const std::vector<std::size_t> rank_points;
57 const std::size_t num_cells_total;
58 const std::size_t num_points_total;
59 const std::size_t my_cell_offset;
60 const std::size_t my_point_offset;
62 IOContext(
int _my_rank,
64 std::vector<std::size_t> _rank_cells,
65 std::vector<std::size_t> _rank_points)
67 , num_ranks{_num_ranks}
68 , is_parallel{_num_ranks > 1}
69 , rank_cells{std::move(_rank_cells)}
70 , rank_points{std::move(_rank_points)}
71 , num_cells_total{_accumulate(rank_cells)}
72 , num_points_total{_accumulate(rank_points)}
73 , my_cell_offset{_accumulate_rank_offset(rank_cells)}
74 , my_point_offset{_accumulate_rank_offset(rank_points)} {
75 if (my_rank >= num_ranks)
76 throw ValueError(as_error(
"Given rank is not within communicator size"));
77 if (num_ranks !=
static_cast<int>(rank_cells.size()))
78 throw ValueError(as_error(
"Cells vector does not match communicator size"));
79 if (num_ranks !=
static_cast<int>(rank_points.size()))
80 throw ValueError(as_error(
"Points vector does not match communicator size"));
83 template<Concepts::Gr
id Gr
id, Concepts::Communicator Communicator>
84 static IOContext from(
const Grid& grid,
const Communicator& comm,
int root_rank = 0) {
85 const int size = Parallel::size(comm);
86 const int rank = Parallel::rank(comm);
87 const std::size_t num_points = number_of_points(grid);
88 const std::size_t num_cells = number_of_cells(grid);
90 return IOContext{rank, size, std::vector{num_cells}, std::vector{num_points}};
92 const auto all_num_points = Parallel::gather(comm, num_points, root_rank);
93 const auto all_num_cells = Parallel::gather(comm, num_cells, root_rank);
94 const auto my_all_num_points = Parallel::broadcast(comm, all_num_points, root_rank);
95 const auto my_all_num_cells = Parallel::broadcast(comm, all_num_cells, root_rank);
96 return IOContext{rank, size, my_all_num_cells, my_all_num_points};
100 std::size_t _accumulate(
const std::vector<std::size_t>& in)
const {
101 return std::accumulate(in.begin(), in.end(), std::size_t{0});
104 std::size_t _accumulate_rank_offset(
const std::vector<std::size_t>& in)
const {
105 if (in.size() <=
static_cast<std::size_t
>(my_rank))
106 throw ValueError(
"Rank-vector length must be equal to number of ranks");
107 return std::accumulate(in.begin(), std::next(in.begin(), my_rank), std::size_t{0});
114 throw ValueError(
"VTKHDF array names must not be empty");
115 if (name.find_first_of(
"/.") != std::string::npos)
116 throw ValueError(
"VTKHDF array names must not contain '/' or '.' (received '" + name +
"')");
121 return field.
precision().template is<char>() && field.
layout().dimension() == 1;
126 const auto strings = field.template export_to<MultiString>();
127 const auto slices = strings.slices();
128 return {slices.begin(), slices.end()};
131#if GRIDFORMAT_HAVE_HIGH_FIVE
138class DataSetField :
public LazyField<const HDF5::File<C>&> {
139 using ParentType = LazyField<const HDF5::File<C>&>;
142 using ParentType::ParentType;
144 explicit DataSetField(
const HDF5::File<C>& file, std::string path)
147 MDLayout{file.get_dimensions(path).value()},
148 DynamicPrecision{file.get_precision(path).value()},
149 [_p=std::move(path)] (
const HDF5::File<C>& file) {
150 return file.visit_dataset(_p, [&] <
typename F> (F&& field) {
151 return field.serialized();
160template<
typename C,
typename CB>
166 if (!file.exists(
"/VTKHDF"))
167 throw IOError(
"Given file is not a VTK-HDF file");
168 if (!file.has_attribute_at(
"/VTKHDF/Type"))
169 throw IOError(
"VTKHDF-Type attribute missing");
170 return file.template read_attribute_to<std::string>(
"/VTKHDF/Type");
178 if (file.has_attribute_at(
"/VTKHDF/Version"))
179 file.visit_attribute(
"/VTKHDF/Version", [&] (
auto&& field) {
180 const auto version = field.template export_to<std::vector<std::size_t>>();
181 if (version.size() > 0 && version.at(0) > supported[0])
183 "File version is higher than supported by the reader (" + as_string(supported,
".") +
")"
190 const auto layout = field->layout();
191 if (layout.dimension() == 0)
192 return make_field_ptr(ReshapedField{field, MDLayout{{std::size_t{1}}}});
193 if (layout.dimension() > 2)
194 return make_field_ptr(ReshapedField{field, MDLayout{{layout.extent(0), layout.number_of_entries(1)}}});
200 const std::string path =
"/VTKHDF/FieldData/" + name;
214 const std::string path =
"/VTKHDF/FieldData/" + name;
215 const auto offset = file.get_dimensions(path).value_or(std::vector<std::size_t>{0}).at(0);
216 std::array<std::size_t, 2> sizes;
219 sizes = {1, strings.size()};
220 file.write_strings(strings, path);
223 const auto layout = tuples->layout();
224 sizes = {layout.dimension() > 1 ? layout.extent(1) : 1, layout.extent(0)};
225 file.write(*tuples, path);
227 file.write(std::array{offset},
"/VTKHDF/Steps/FieldDataOffsets/" + name);
228 file.write(std::vector{sizes},
"/VTKHDF/Steps/FieldDataSizes/" + name);
233 const std::string offsets_path =
"/VTKHDF/Steps/FieldDataOffsets/" + name;
234 const std::string sizes_path =
"/VTKHDF/Steps/FieldDataSizes/" + name;
235 const auto offset = file.read_dataset_to<std::size_t>(
236 offsets_path, HDF5::Slice{.offset = {step_index}, .count = {1}}
238 const auto sizes = file.read_dataset_to<std::vector<std::size_t>>(
239 sizes_path, HDF5::Slice{.offset = {step_index, 0}, .count = {1, 2}}
241 file.write(std::array{offset}, offsets_path);
242 file.write(std::vector{std::array{sizes.at(0), sizes.at(1)}}, sizes_path);
248 const std::string path =
"VTKHDF/FieldData/" + name;
249 const auto precision = file.get_precision(path).value();
250 const bool is_string = precision.template is<char>();
252 return is_string ?
make_field_ptr(RangeField{file.template read_dataset_to<MultiString>(path)})
256 const auto offset = file.template read_dataset_to<std::size_t>(
257 "VTKHDF/Steps/FieldDataOffsets/" + name, HDF5::Slice{.offset = {*step}, .count = {1}}
259 const std::string sizes_path =
"VTKHDF/Steps/FieldDataSizes/" + name;
260 const auto sizes = file.has_dataset_at(sizes_path)
261 ? std::optional{file.template read_dataset_to<std::vector<std::size_t>>(
262 sizes_path, HDF5::Slice{.offset = {*step, 0}, .count = {1, 2}}
266 const std::size_t count = sizes ? sizes->at(0)*sizes->at(1) : 1;
267 return make_field_ptr(RangeField{file.template read_dataset_to<MultiString>(
268 path, HDF5::Slice{.offset = {offset}, .count = {count}}
272 const auto dims = file.get_dimensions(path).value();
273 std::vector<std::size_t> slice_offset(dims.size(), 0);
274 slice_offset.at(0) = offset;
276 count.at(0) = sizes ? sizes->at(1) : 1;
277 if (sizes && MDLayout{count}.number_of_entries() != sizes->at(0)*sizes->at(1))
278 throw SizeError(
"Field data '" + name +
"' does not match its stated number of components");
279 MDLayout layout = sizes ? MDLayout{count} : MDLayout{count | std::views::drop(1)};
284 [p=path, o=std::move(slice_offset), c=std::move(count)] (
const HDF5::File<C>& f) {
285 return f.visit_dataset(p, [&] <
typename F> (F&& field) {
286 return FlattenedField{
make_field_ptr(std::move(field))}.serialized();
287 }, HDF5::Slice{.offset = o, .count = c});
297template<Concepts::Communicator C, std::invocable<HDF5::File<>&> Write>
298void write_on_root(
const std::string& filename,
const C& comm,
int root, Write&& write) {
299 Parallel::barrier(comm);
301 Parallel::invoke_collectively(comm, [&] () {
302 if (Parallel::rank(comm) == root) {
303 HDF5::File file{filename, HDF5::File<>::append};
306 },
"Writing to '" + filename +
"'");
Abstract interface for fields of values that is used by writers/readers to store fields.
FieldPtr make_field_ptr(F &&f)
Factory function for field pointers.
Definition field.hpp:211
std::shared_ptr< const Field > FieldPtr
Pointer type used by writers/readers for fields.
Definition field.hpp:205
void write_field_data_step(HDF5::File<> &file, const std::string &name, FieldPtr field)
Append a time step to a field data array of a transient file.
Definition hdf_common.hpp:213
void write_field_data_step_referencing(std::size_t step_index, HDF5::File<> &file, const std::string &name)
Append a time step to a field data array of a transient file, referring to the data of an earlier ste...
Definition hdf_common.hpp:232
void check_version_compatibility(const HDF5::File< C > &file, const std::array< std::size_t, 2 > &supported)
Definition hdf_common.hpp:177
void write_field_data(HDF5::File<> &file, const std::string &name, FieldPtr field)
Write a field data array into a file that is not transient.
Definition hdf_common.hpp:199
void check_array_name(const std::string &name)
Throw if the given name cannot be used as a vtk-hdf array name.
Definition hdf_common.hpp:112
std::vector< std::string > extract_strings(const Field &field)
Return the strings stored in the given character field (see MultiString).
Definition hdf_common.hpp:125
std::string get_file_type(const HDF5::File< C > &file)
Read the vtk-hdf file type from an hdf5 file.
Definition hdf_common.hpp:165
void write_on_root(const std::string &filename, const C &comm, int root, Write &&write)
Open the given file with the default (non-MPI) driver on the root rank and pass it to write.
Definition hdf_common.hpp:298
FieldPtr read_field_data(const HDF5::File< C > &file, const std::string &name, std::optional< std::size_t > step)
Read the field data array with the given name, at the given step for transient files.
Definition hdf_common.hpp:247
bool is_string_field(const Field &field)
Return true if the given field holds strings (i.e. is a flat array of characters, see MultiString).
Definition hdf_common.hpp:120
FieldPtr as_field_data_tuples(FieldPtr field)
Return the given field data in the shape of tuples, i.e. (tuples) or (tuples, components).
Definition hdf_common.hpp:189