GridFormat 0.6.0
I/O-Library for grid-like data structures
Loading...
Searching...
No Matches
hdf_image_grid_reader.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2// SPDX-License-Identifier: MIT
8#ifndef GRIDFORMAT_VTK_HDF_IMAGE_GRID_READER_HPP_
9#define GRIDFORMAT_VTK_HDF_IMAGE_GRID_READER_HPP_
10#if GRIDFORMAT_HAVE_HIGH_FIVE
11
12#include <iterator>
13#include <optional>
14#include <algorithm>
15#include <functional>
16#include <concepts>
17#include <numeric>
18#include <utility>
19#include <ranges>
20
22#include <gridformat/common/range_field.hpp>
23#include <gridformat/common/field_transformations.hpp>
24#include <gridformat/common/exceptions.hpp>
25#include <gridformat/common/lazy_field.hpp>
26#include <gridformat/common/ranges.hpp>
27
29#include <gridformat/parallel/concepts.hpp>
32
33namespace GridFormat {
34
39class VTKHDFImageGridReader : public GridReader {
40 using Communicator = NullCommunicator;
41 using HDF5File = HDF5::File<Communicator>;
42 static constexpr std::size_t vtk_space_dim = 3;
43
44 public:
45 explicit VTKHDFImageGridReader() = default;
46
47 template<Concepts::Communicator C>
48 explicit VTKHDFImageGridReader(C&&) {
49 static_assert(
50 std::is_same_v<std::remove_cvref_t<C>, NullCommunicator>,
51 "Cannot read vtk-hdf image grid files in parallel"
52 );
53 }
54
55 private:
56 std::string _name() const override {
57 if (_is_transient())
58 return "VTKHDFImageGridReader (transient)";
59 return "VTKHDFImageGridReader";
60 }
61
62 void _open(const std::string& filename, typename GridReader::FieldNames& field_names) override {
63 _file = HDF5File{filename, _comm, HDF5File::read_only};
64
65 const auto type = VTKHDF::get_file_type(_file.value());
66 if (type != "ImageData")
67 throw ValueError("Incompatible VTK-HDF type: '" + type + "', expected 'ImageData'.");
68
69 VTKHDF::check_version_compatibility(_file.value(), {2, 0});
70
71 if (_file->exists("/VTKHDF/Steps"))
72 _file->visit_attribute("/VTKHDF/Steps/NSteps", [&] (auto&& field) {
73 _num_steps.emplace();
74 field.export_to(_num_steps.value());
75 _step_index.emplace(0);
76 });
77
78 const auto copy_names_in = [&] (const std::string& group, auto& storage) {
79 if (_file->exists(group))
80 std::ranges::copy(_file->dataset_names_in(group), std::back_inserter(storage));
81 };
82 copy_names_in("VTKHDF/CellData", field_names.cell_fields);
83 copy_names_in("VTKHDF/PointData", field_names.point_fields);
84 copy_names_in("VTKHDF/FieldData", field_names.meta_data_fields);
85
86 auto spacing = _file.value().template read_attribute_to<std::vector<double>>("/VTKHDF/Spacing");
87 if (spacing.size() != vtk_space_dim)
88 throw SizeError("Unexpected spacing vector read (size = " + as_string(spacing.size()) + ")");
89 _cell_spacing = Ranges::to_array<vtk_space_dim>(spacing);
90
91 auto direction = std::vector<double>{1., 0., 0., 0., 1., 0., 0., 0., 1.};
92 if (_file->has_attribute_at("/VTKHDF/Direction"))
93 direction = _file->template read_attribute_to<std::vector<double>>("/VTKHDF/Direction");
94 if (direction.size() != 9)
95 throw SizeError("Unexpected direction vector read (size = " + as_string(direction.size()) + ")");
96 _direction = Ranges::to_array<9>(direction);
97
98 auto origin = std::vector<double>{0., 0., 0.};
99 if (_file->has_attribute_at("/VTKHDF/Origin"))
100 origin = _file->template read_attribute_to<std::vector<double>>("/VTKHDF/Origin");
101 if (origin.size() != vtk_space_dim)
102 throw SizeError("Unexpected origin read (size = " + as_string(origin.size()) + ")");
103 _point_origin = Ranges::to_array<vtk_space_dim>(origin);
104
105 auto extents = _file.value().template read_attribute_to<std::vector<std::size_t>>("/VTKHDF/WholeExtent");
106 if (extents.size() != 6)
107 throw SizeError("Unexpected 'WholeExtents' attribute (size = " + as_string(extents.size()) + ").");
108 _piece_location.lower_left = {extents[0], extents[2], extents[4]};
109 _piece_location.upper_right = {extents[1], extents[3], extents[5]};
110 }
111
112 void _close() override {
113 _file = {};
114 _piece_location = {};
115 _cell_spacing = {};
116 _point_origin = {};
117 _direction = {};
118 }
119
120 typename GridReader::PieceLocation _location() const override {
121 return _piece_location;
122 }
123
124 typename GridReader::Vector _spacing() const override {
125 return _cell_spacing;
126 }
127
128 typename GridReader::Vector _origin() const override {
129 return _point_origin;
130 }
131
132 std::size_t _number_of_cells() const override {
133 const auto counts = _get_extents();
134 auto actives = counts | std::views::filter([] (auto e) { return e != 0; });
135 return std::accumulate(
136 std::ranges::begin(actives),
137 std::ranges::end(actives),
138 std::size_t{1},
139 std::multiplies{}
140 );
141 }
142
143 std::size_t _number_of_points() const override {
144 auto counts = Ranges::incremented(_get_extents(), 1);
145 return std::accumulate(
146 std::ranges::begin(counts),
147 std::ranges::end(counts),
148 std::size_t{1},
149 std::multiplies{}
150 );
151 }
152
153 std::size_t _number_of_pieces() const override {
154 return 1;
155 }
156
157 void _visit_cells(const typename GridReader::CellVisitor& visitor) const override {
158 VTK::CommonDetail::visit_structured_cells(visitor, _make_vtk_extents_array());
159 }
160
161 FieldPtr _points() const override {
162 std::array<std::size_t, 6> extents = _make_vtk_extents_array();
163 extents[1] += 1;
164 extents[3] += 1;
165 extents[5] += 1;
166 return make_field_ptr(LazyField{
167 int{}, // dummy source
168 MDLayout{{VTK::CommonDetail::number_of_entities(extents), std::size_t{vtk_space_dim}}},
169 Precision<double>{},
170 [ex=extents, o=_point_origin, s=_cell_spacing, d=_direction] (const int&) {
171 return VTK::CommonDetail::serialize_structured_points(ex, o, s, d);
172 }
173 });
174 }
175
176 bool _is_sequence() const override {
177 return _is_transient();
178 }
179
180 std::size_t _number_of_steps() const override {
181 if (!_num_steps.has_value())
182 throw ValueError("Read file is not a sequence");
183 return _num_steps.value();
184 }
185
186 double _time_at_step(std::size_t step_idx) const override {
187 if (step_idx >= _number_of_steps())
188 throw ValueError("Only " + as_string(_number_of_steps()) + " available");
189 return _file.value().template read_dataset_to<double>("/VTKHDF/Steps/Values", HDF5::Slice{
190 .offset = {step_idx},
191 .count = {1}
192 });
193 }
194
195 void _set_step(std::size_t step_idx, typename GridReader::FieldNames&) override {
196 if (step_idx >= _number_of_steps())
197 throw ValueError("Only " + as_string(_number_of_steps()) + " available");
198 _step_index = step_idx;
199 }
200
201 auto _serialization_callback(std::string path, std::size_t target_size) const {
202 return [&, size=target_size, _step=_step_index, _p=std::move(path)] (const HDF5File& file) {
203 auto count = file.get_dimensions(_p).value();
204 auto offset = count;
205 std::ranges::fill(offset, 0);
206 if (_step) {
207 count.at(0) = 1;
208 offset.at(0) = _step.value();
209 }
210 return file.visit_dataset(_p, [&] <typename F> (F&& field) {
211 const std::size_t step_offset = _step.has_value() ? 1 : 0;
212 const auto layout = field.layout();
213 if (layout.dimension() == _grid_dimension() + step_offset)
214 return transform(make_field_ptr(std::move(field)), FieldTransformation::reshape_to(
215 MDLayout{{size}}
216 ))->serialized();
217 else if (layout.dimension() == _grid_dimension() + step_offset + 1)
218 return transform(make_field_ptr(std::move(field)), FieldTransformation::reshape_to(
219 MDLayout{{size, layout.extent(_grid_dimension() + step_offset)}}
220 ))->serialized();
221 throw InvalidState("Unexpected field layout: " + as_string(layout));
222 }, HDF5::Slice{.offset = offset, .count = count});
223 };
224 }
225
226 FieldPtr _cell_field(std::string_view name) const override {
227 const std::string path = "VTKHDF/CellData/" + std::string{name};
228 const auto num_components = _get_number_of_components(path);
229 const auto layout = num_components ? MDLayout{{_number_of_cells(), num_components.value()}}
230 : MDLayout{{_number_of_cells()}};
232 _file.value(),
233 std::move(layout),
234 _file.value().get_precision(path).value(),
235 _serialization_callback(path, _number_of_cells())
236 });
237 }
238
239 FieldPtr _point_field(std::string_view name) const override {
240 const std::string path = "VTKHDF/PointData/" + std::string{name};
241 const auto num_components = _get_number_of_components(path);
242 const auto layout = num_components ? MDLayout{{_number_of_points(), num_components.value()}}
243 : MDLayout{{_number_of_points()}};
245 _file.value(),
246 std::move(layout),
247 _file.value().get_precision(path).value(),
248 _serialization_callback(path, _number_of_points())
249 });
250 }
251
252 FieldPtr _meta_data_field(std::string_view name) const override {
254 _file.value(),
255 std::string{name},
256 _is_transient() ? std::optional{_step_index.value()} : std::nullopt
257 );
258 }
259
260 std::array<std::size_t, 6> _make_vtk_extents_array() const {
261 std::array<std::size_t, 6> extents;
262 extents[0] = _piece_location.lower_left[0];
263 extents[2] = _piece_location.lower_left[1];
264 extents[4] = _piece_location.lower_left[2];
265 extents[1] = _piece_location.upper_right[0];
266 extents[3] = _piece_location.upper_right[1];
267 extents[5] = _piece_location.upper_right[2];
268 return extents;
269 }
270
271 std::optional<std::size_t> _get_number_of_components(std::string_view path) const {
272 const auto layout = _file.value().get_dimensions(std::string{path}).value();
273 const std::size_t step_offset = _is_transient() ? 1 : 0;
274 if (layout.size() == _grid_dimension() + step_offset + 1)
275 return layout.back();
276 return {};
277 }
278
279 std::size_t _grid_dimension() const {
280 return VTK::CommonDetail::structured_grid_dimension(_get_extents());
281 }
282
283 std::array<std::size_t, 3> _get_extents() const {
284 return {
285 _piece_location.upper_right[0] - _piece_location.lower_left[0],
286 _piece_location.upper_right[1] - _piece_location.lower_left[1],
287 _piece_location.upper_right[2] - _piece_location.lower_left[2]
288 };
289 }
290
291 bool _is_transient() const {
292 return static_cast<bool>(_num_steps);
293 }
294
295 Communicator _comm;
296 std::optional<HDF5File> _file;
297 typename GridReader::PieceLocation _piece_location;
298 std::array<double, 9> _direction;
299 std::array<double, 3> _cell_spacing;
300 std::array<double, 3> _point_origin;
301 std::optional<std::size_t> _num_steps;
302 std::optional<std::size_t> _step_index;
303};
304
305} // namespace GridFormat
306
307#endif // GRIDFORMAT_HAVE_HIGH_FIVE
308#endif // GRIDFORMAT_VTK_HDF_IMAGE_GRID_READER_HPP_
std::array< std::size_t, 3 > extents() const
Return the extents of the grid (only available for structured grid formats).
Definition reader.hpp:113
const std::string & filename() const
Return the name of the opened grid file (empty string until open() is called).
Definition reader.hpp:92
std::string name() const
Return the name of this reader.
Definition reader.hpp:73
Vector origin() const
Return the origin of the grid (only available for image grid formats).
Definition reader.hpp:140
Vector spacing() const
Return the spacing of the grid (only available for image grid formats).
Definition reader.hpp:135
Field implementation that draws values from an open HDF5 file upon request.
Definition hdf_common.hpp:135
Abstract interface for fields of values that is used by writers/readers to store fields.
Base class for grid data readers.
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
Common functionality for writing VTK HDF files.
void check_version_compatibility(const HDF5::File< C > &file, const std::array< std::size_t, 2 > &supported)
Definition hdf_common.hpp:174
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
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
Definition reader.hpp:266
Describes the location of a piece within a distributed structured grid.
Definition reader.hpp:57
Common functionality for VTK writers.