9#ifndef GRIDFORMAT_VTK_HDF_COMMON_HPP_
10#define GRIDFORMAT_VTK_HDF_COMMON_HPP_
21#include <gridformat/common/hdf5.hpp>
23#include <gridformat/common/precision.hpp>
24#include <gridformat/common/lazy_field.hpp>
25#include <gridformat/common/range_field.hpp>
26#include <gridformat/common/field_transformations.hpp>
27#include <gridformat/common/multi_string.hpp>
28#include <gridformat/common/string_conversion.hpp>
29#include <gridformat/common/exceptions.hpp>
30#include <gridformat/parallel/communication.hpp>
51 const bool is_parallel;
52 const std::vector<std::size_t> rank_cells;
53 const std::vector<std::size_t> rank_points;
54 const std::size_t num_cells_total;
55 const std::size_t num_points_total;
56 const std::size_t my_cell_offset;
57 const std::size_t my_point_offset;
59 IOContext(
int _my_rank,
61 std::vector<std::size_t> _rank_cells,
62 std::vector<std::size_t> _rank_points)
64 , num_ranks{_num_ranks}
65 , is_parallel{_num_ranks > 1}
66 , rank_cells{std::move(_rank_cells)}
67 , rank_points{std::move(_rank_points)}
68 , num_cells_total{_accumulate(rank_cells)}
69 , num_points_total{_accumulate(rank_points)}
70 , my_cell_offset{_accumulate_rank_offset(rank_cells)}
71 , my_point_offset{_accumulate_rank_offset(rank_points)} {
72 if (my_rank >= num_ranks)
73 throw ValueError(as_error(
"Given rank is not within communicator size"));
74 if (num_ranks !=
static_cast<int>(rank_cells.size()))
75 throw ValueError(as_error(
"Cells vector does not match communicator size"));
76 if (num_ranks !=
static_cast<int>(rank_points.size()))
77 throw ValueError(as_error(
"Points vector does not match communicator size"));
80 template<Concepts::Gr
id Gr
id, Concepts::Communicator Communicator>
81 static IOContext from(
const Grid& grid,
const Communicator& comm,
int root_rank = 0) {
82 const int size = Parallel::size(comm);
83 const int rank = Parallel::rank(comm);
84 const std::size_t num_points = number_of_points(grid);
85 const std::size_t num_cells = number_of_cells(grid);
87 return IOContext{rank, size, std::vector{num_cells}, std::vector{num_points}};
89 const auto all_num_points = Parallel::gather(comm, num_points, root_rank);
90 const auto all_num_cells = Parallel::gather(comm, num_cells, root_rank);
91 const auto my_all_num_points = Parallel::broadcast(comm, all_num_points, root_rank);
92 const auto my_all_num_cells = Parallel::broadcast(comm, all_num_cells, root_rank);
93 return IOContext{rank, size, my_all_num_cells, my_all_num_points};
97 std::size_t _accumulate(
const std::vector<std::size_t>& in)
const {
98 return std::accumulate(in.begin(), in.end(), std::size_t{0});
101 std::size_t _accumulate_rank_offset(
const std::vector<std::size_t>& in)
const {
102 if (in.size() <=
static_cast<std::size_t
>(my_rank))
103 throw ValueError(
"Rank-vector length must be equal to number of ranks");
104 return std::accumulate(in.begin(), std::next(in.begin(), my_rank), std::size_t{0});
111 throw ValueError(
"VTKHDF array names must not be empty");
112 if (name.find_first_of(
"/.") != std::string::npos)
113 throw ValueError(
"VTKHDF array names must not contain '/' or '.' (received '" + name +
"')");
118 return field.
precision().template is<char>() && field.
layout().dimension() == 1;
123 const auto strings = field.template export_to<MultiString>();
124 const auto slices = strings.slices();
125 return {slices.begin(), slices.end()};
128#if GRIDFORMAT_HAVE_HIGH_FIVE
135class DataSetField :
public LazyField<const HDF5::File<C>&> {
136 using ParentType = LazyField<const HDF5::File<C>&>;
139 using ParentType::ParentType;
141 explicit DataSetField(
const HDF5::File<C>& file, std::string path)
144 MDLayout{file.get_dimensions(path).value()},
145 DynamicPrecision{file.get_precision(path).value()},
146 [_p=std::move(path)] (
const HDF5::File<C>& file) {
147 return file.visit_dataset(_p, [&] <
typename F> (F&& field) {
148 return field.serialized();
157template<
typename C,
typename CB>
163 if (!file.exists(
"/VTKHDF"))
164 throw IOError(
"Given file is not a VTK-HDF file");
165 if (!file.has_attribute_at(
"/VTKHDF/Type"))
166 throw IOError(
"VTKHDF-Type attribute missing");
167 return file.template read_attribute_to<std::string>(
"/VTKHDF/Type");
175 if (file.has_attribute_at(
"/VTKHDF/Version"))
176 file.visit_attribute(
"/VTKHDF/Version", [&] (
auto&& field) {
177 const auto version = field.template export_to<std::vector<std::size_t>>();
178 if (version.size() > 0 && version.at(0) > supported[0])
180 "File version is higher than supported by the reader (" + as_string(supported,
".") +
")"
187 const auto layout = field->layout();
188 if (layout.dimension() == 0)
189 return make_field_ptr(ReshapedField{field, MDLayout{{std::size_t{1}}}});
190 if (layout.dimension() > 2)
191 return make_field_ptr(ReshapedField{field, MDLayout{{layout.extent(0), layout.number_of_entries(1)}}});
197 const std::string path =
"/VTKHDF/FieldData/" + name;
211 const std::string path =
"/VTKHDF/FieldData/" + name;
212 const auto offset = file.get_dimensions(path).value_or(std::vector<std::size_t>{0}).at(0);
213 std::array<std::size_t, 2> sizes;
216 sizes = {1, strings.size()};
217 file.write_strings(strings, path);
220 const auto layout = tuples->layout();
221 sizes = {layout.dimension() > 1 ? layout.extent(1) : 1, layout.extent(0)};
222 file.write(*tuples, path);
224 file.write(std::array{offset},
"/VTKHDF/Steps/FieldDataOffsets/" + name);
225 file.write(std::vector{sizes},
"/VTKHDF/Steps/FieldDataSizes/" + name);
230 const std::string offsets_path =
"/VTKHDF/Steps/FieldDataOffsets/" + name;
231 const std::string sizes_path =
"/VTKHDF/Steps/FieldDataSizes/" + name;
232 const auto offset = file.read_dataset_to<std::size_t>(
233 offsets_path, HDF5::Slice{.offset = {step_index}, .count = {1}}
235 const auto sizes = file.read_dataset_to<std::vector<std::size_t>>(
236 sizes_path, HDF5::Slice{.offset = {step_index, 0}, .count = {1, 2}}
238 file.write(std::array{offset}, offsets_path);
239 file.write(std::vector{std::array{sizes.at(0), sizes.at(1)}}, sizes_path);
245 const std::string path =
"VTKHDF/FieldData/" + name;
246 const auto precision = file.get_precision(path).value();
247 const bool is_string = precision.template is<char>();
249 return is_string ?
make_field_ptr(RangeField{file.template read_dataset_to<MultiString>(path)})
253 const auto offset = file.template read_dataset_to<std::size_t>(
254 "VTKHDF/Steps/FieldDataOffsets/" + name, HDF5::Slice{.offset = {*step}, .count = {1}}
256 const std::string sizes_path =
"VTKHDF/Steps/FieldDataSizes/" + name;
257 const auto sizes = file.has_dataset_at(sizes_path)
258 ? std::optional{file.template read_dataset_to<std::vector<std::size_t>>(
259 sizes_path, HDF5::Slice{.offset = {*step, 0}, .count = {1, 2}}
263 const std::size_t count = sizes ? sizes->at(0)*sizes->at(1) : 1;
264 return make_field_ptr(RangeField{file.template read_dataset_to<MultiString>(
265 path, HDF5::Slice{.offset = {offset}, .count = {count}}
269 const auto dims = file.get_dimensions(path).value();
270 std::vector<std::size_t> slice_offset(dims.size(), 0);
271 slice_offset.at(0) = offset;
273 count.at(0) = sizes ? sizes->at(1) : 1;
274 if (sizes && MDLayout{count}.number_of_entries() != sizes->at(0)*sizes->at(1))
275 throw SizeError(
"Field data '" + name +
"' does not match its stated number of components");
276 MDLayout layout = sizes ? MDLayout{count} : MDLayout{count | std::views::drop(1)};
281 [p=path, o=std::move(slice_offset), c=std::move(count)] (
const HDF5::File<C>& f) {
282 return f.visit_dataset(p, [&] <
typename F> (F&& field) {
283 return FlattenedField{
make_field_ptr(std::move(field))}.serialized();
284 }, HDF5::Slice{.offset = o, .count = c});
294template<Concepts::Communicator C, std::invocable<HDF5::File<>&> Write>
295void write_on_root(
const std::string& filename,
const C& comm,
int root, Write&& write) {
296 Parallel::barrier(comm);
297 std::exception_ptr error;
298 if (Parallel::rank(comm) == root) {
300 HDF5::File file{filename, HDF5::File<>::append};
303 error = std::current_exception();
307 if (Parallel::broadcast(comm,
static_cast<int>(error !=
nullptr), root)) {
309 std::rethrow_exception(error);
310 throw IOError(
"Writing to '" + filename +
"' failed on rank " + std::to_string(root));
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:210
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:229
void check_version_compatibility(const HDF5::File< C > &file, const std::array< std::size_t, 2 > &supported)
Definition hdf_common.hpp:174
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:196
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:109
std::vector< std::string > extract_strings(const Field &field)
Return the strings stored in the given character field (see MultiString).
Definition hdf_common.hpp:122
std::string get_file_type(const HDF5::File< C > &file)
Read the vtk-hdf file type from an hdf5 file.
Definition hdf_common.hpp:162
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:295
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:244
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:117
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:186