8#ifndef GRIDFORMAT_VTK_HDF_IMAGE_GRID_READER_HPP_
9#define GRIDFORMAT_VTK_HDF_IMAGE_GRID_READER_HPP_
10#if GRIDFORMAT_HAVE_HIGH_FIVE
22#include <gridformat/common/field_transformations.hpp>
23#include <gridformat/common/exceptions.hpp>
24#include <gridformat/common/lazy_field.hpp>
25#include <gridformat/common/ranges.hpp>
28#include <gridformat/parallel/concepts.hpp>
38 using Communicator = NullCommunicator;
39 using HDF5File = HDF5::File<Communicator>;
40 static constexpr std::size_t vtk_space_dim = 3;
45 template<Concepts::Communicator C>
48 std::is_same_v<std::remove_cvref_t<C>, NullCommunicator>,
49 "Cannot read vtk-hdf image grid files in parallel"
54 std::string _name()
const override {
56 return "VTKHDFImageGridReader (transient)";
57 return "VTKHDFImageGridReader";
61 _file = HDF5File{
filename, _comm, HDF5File::read_only};
63 const auto type = VTKHDF::get_file_type(_file.value());
64 if (type !=
"ImageData")
65 throw ValueError(
"Incompatible VTK-HDF type: '" + type +
"', expected 'ImageData'.");
67 VTKHDF::check_version_compatibility(_file.value(), {2, 0});
69 if (_file->exists(
"/VTKHDF/Steps"))
70 _file->visit_attribute(
"/VTKHDF/Steps/NSteps", [&] (
auto&& field) {
72 field.export_to(_num_steps.value());
73 _step_index.emplace(0);
76 const auto copy_names_in = [&] (
const std::string& group,
auto& storage) {
77 if (_file->exists(group))
78 std::ranges::copy(_file->dataset_names_in(group), std::back_inserter(storage));
80 copy_names_in(
"VTKHDF/CellData", field_names.cell_fields);
81 copy_names_in(
"VTKHDF/PointData", field_names.point_fields);
82 copy_names_in(
"VTKHDF/FieldData", field_names.meta_data_fields);
84 auto spacing = _file.value().template read_attribute_to<std::vector<double>>(
"/VTKHDF/Spacing");
85 if (
spacing.size() != vtk_space_dim)
86 throw SizeError(
"Unexpected spacing vector read (size = " + as_string(
spacing.size()) +
")");
87 _cell_spacing = Ranges::to_array<vtk_space_dim>(
spacing);
89 auto direction = std::vector<double>{1., 0., 0., 0., 1., 0., 0., 0., 1.};
90 if (_file->has_attribute_at(
"/VTKHDF/Direction"))
91 direction = _file->template read_attribute_to<std::vector<double>>(
"/VTKHDF/Direction");
92 if (direction.size() != 9)
93 throw SizeError(
"Unexpected direction vector read (size = " + as_string(direction.size()) +
")");
94 _direction = Ranges::to_array<9>(direction);
96 auto origin = std::vector<double>{0., 0., 0.};
97 if (_file->has_attribute_at(
"/VTKHDF/Origin"))
98 origin = _file->template read_attribute_to<std::vector<double>>(
"/VTKHDF/Origin");
99 if (
origin.size() != vtk_space_dim)
100 throw SizeError(
"Unexpected origin read (size = " + as_string(
origin.size()) +
")");
101 _point_origin = Ranges::to_array<vtk_space_dim>(
origin);
103 auto extents = _file.value().template read_attribute_to<std::vector<std::size_t>>(
"/VTKHDF/WholeExtent");
105 throw SizeError(
"Unexpected 'WholeExtents' attribute (size = " + as_string(
extents.size()) +
").");
110 void _close()
override {
112 _piece_location = {};
119 return _piece_location;
122 typename GridReader::Vector _spacing()
const override {
123 return _cell_spacing;
126 typename GridReader::Vector _origin()
const override {
127 return _point_origin;
130 std::size_t _number_of_cells()
const override {
131 const auto counts = _get_extents();
132 auto actives = counts | std::views::filter([] (
auto e) {
return e != 0; });
133 return std::accumulate(
134 std::ranges::begin(actives),
135 std::ranges::end(actives),
141 std::size_t _number_of_points()
const override {
142 auto counts = Ranges::incremented(_get_extents(), 1);
143 return std::accumulate(
144 std::ranges::begin(counts),
145 std::ranges::end(counts),
151 std::size_t _number_of_pieces()
const override {
155 void _visit_cells(
const typename GridReader::CellVisitor& visitor)
const override {
156 VTK::CommonDetail::visit_structured_cells(visitor, _make_vtk_extents_array());
160 std::array<std::size_t, 6>
extents = _make_vtk_extents_array();
166 MDLayout{{VTK::CommonDetail::number_of_entities(
extents), std::size_t{vtk_space_dim}}},
168 [ex=
extents, o=_point_origin, s=_cell_spacing, d=_direction] (
const int&) {
169 return VTK::CommonDetail::serialize_structured_points(ex, o, s, d);
174 bool _is_sequence()
const override {
175 return _is_transient();
178 std::size_t _number_of_steps()
const override {
179 if (!_num_steps.has_value())
180 throw ValueError(
"Read file is not a sequence");
181 return _num_steps.value();
184 double _time_at_step(std::size_t step_idx)
const override {
185 if (step_idx >= _number_of_steps())
186 throw ValueError(
"Only " + as_string(_number_of_steps()) +
" available");
187 return _file.value().template read_dataset_to<double>(
"/VTKHDF/Steps/Values", HDF5::Slice{
188 .offset = {step_idx},
194 if (step_idx >= _number_of_steps())
195 throw ValueError(
"Only " + as_string(_number_of_steps()) +
" available");
196 _step_index = step_idx;
199 auto _serialization_callback(std::string path, std::size_t target_size)
const {
200 return [&, size=target_size, _step=_step_index, _p=std::move(path)] (
const HDF5File& file) {
201 auto count = file.get_dimensions(_p).value();
203 std::ranges::fill(offset, 0);
206 offset.at(0) = _step.value();
208 return file.visit_dataset(_p, [&] <
typename F> (F&& field) {
209 const std::size_t step_offset = _step.has_value() ? 1 : 0;
210 const auto layout = field.layout();
211 if (layout.dimension() == _grid_dimension() + step_offset)
212 return transform(
make_field_ptr(std::move(field)), FieldTransformation::reshape_to(
215 else if (layout.dimension() == _grid_dimension() + step_offset + 1)
216 return transform(
make_field_ptr(std::move(field)), FieldTransformation::reshape_to(
217 MDLayout{{size, layout.extent(_grid_dimension() + step_offset)}}
219 throw InvalidState(
"Unexpected field layout: " + as_string(layout));
220 }, HDF5::Slice{.offset = offset, .count = count});
224 FieldPtr _cell_field(std::string_view
name)
const override {
225 const std::string path =
"VTKHDF/CellData/" + std::string{
name};
226 const auto num_components = _get_number_of_components(path);
227 const auto layout = num_components ? MDLayout{{_number_of_cells(), num_components.value()}}
228 : MDLayout{{_number_of_cells()}};
232 _file.value().get_precision(path).value(),
233 _serialization_callback(path, _number_of_cells())
237 FieldPtr _point_field(std::string_view
name)
const override {
238 const std::string path =
"VTKHDF/PointData/" + std::string{
name};
239 const auto num_components = _get_number_of_components(path);
240 const auto layout = num_components ? MDLayout{{_number_of_points(), num_components.value()}}
241 : MDLayout{{_number_of_points()}};
245 _file.value().get_precision(path).value(),
246 _serialization_callback(path, _number_of_points())
250 FieldPtr _meta_data_field(std::string_view
name)
const override {
251 const auto path =
"VTKHDF/FieldData/" + std::string{
name};
252 const auto dims = _file.value().get_dimensions(path).value();
253 if (dims.size() == 1)
255 if (dims.size() > 3 || (!_is_transient() && dims.size() != 2))
256 throw SizeError(
"Unexpected field data array size");
257 if (dims.at(dims.size() - 2) != 1)
258 throw SizeError(
"Cannot only read one-dimensional field data");
262 std::ranges::fill(offset, 0);
264 offset.at(0) = _is_transient() ? _step_index.value() : 0;
267 MDLayout{count | std::views::drop(1)},
268 _file.value().get_precision(path).value(),
269 [p=path, o=offset, c=count] (
const HDF5File& f) {
270 return f.visit_dataset(p, [&] <
typename F> (F&& field) {
271 return FlattenedField{
make_field_ptr(std::move(field))}.serialized();
272 }, HDF5::Slice{.offset = o, .count = c});
277 std::array<std::size_t, 6> _make_vtk_extents_array()
const {
278 std::array<std::size_t, 6>
extents;
279 extents[0] = _piece_location.lower_left[0];
280 extents[2] = _piece_location.lower_left[1];
281 extents[4] = _piece_location.lower_left[2];
282 extents[1] = _piece_location.upper_right[0];
283 extents[3] = _piece_location.upper_right[1];
284 extents[5] = _piece_location.upper_right[2];
288 std::optional<std::size_t> _get_number_of_components(std::string_view path)
const {
289 const auto layout = _file.value().get_dimensions(std::string{path}).value();
290 const std::size_t step_offset = _is_transient() ? 1 : 0;
291 if (layout.size() == _grid_dimension() + step_offset + 1)
292 return layout.back();
296 std::size_t _grid_dimension()
const {
297 return VTK::CommonDetail::structured_grid_dimension(_get_extents());
300 std::array<std::size_t, 3> _get_extents()
const {
302 _piece_location.upper_right[0] - _piece_location.lower_left[0],
303 _piece_location.upper_right[1] - _piece_location.lower_left[1],
304 _piece_location.upper_right[2] - _piece_location.lower_left[2]
308 bool _is_transient()
const {
309 return static_cast<bool>(_num_steps);
313 std::optional<HDF5File> _file;
315 std::array<double, 9> _direction;
316 std::array<double, 3> _cell_spacing;
317 std::array<double, 3> _point_origin;
318 std::optional<std::size_t> _num_steps;
319 std::optional<std::size_t> _step_index;
Base class for grid data readers.
std::shared_ptr< const Field > FieldPtr
Pointer type used by writers/readers for fields.
Definition: field.hpp:186
FieldPtr make_field_ptr(F &&f)
Factory function for field pointers.
Definition: field.hpp:192
Common functionality for writing VTK HDF files.