GridFormat 0.4.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/field_transformations.hpp>
23#include <gridformat/common/exceptions.hpp>
24#include <gridformat/common/lazy_field.hpp>
25#include <gridformat/common/ranges.hpp>
26
28#include <gridformat/parallel/concepts.hpp>
30
31namespace GridFormat {
32
38 using Communicator = NullCommunicator;
39 using HDF5File = HDF5::File<Communicator>;
40 static constexpr std::size_t vtk_space_dim = 3;
41
42 public:
43 explicit VTKHDFImageGridReader() = default;
44
45 template<Concepts::Communicator C>
46 explicit VTKHDFImageGridReader(C&&) {
47 static_assert(
48 std::is_same_v<std::remove_cvref_t<C>, NullCommunicator>,
49 "Cannot read vtk-hdf image grid files in parallel"
50 );
51 }
52
53 private:
54 std::string _name() const override {
55 if (_is_transient())
56 return "VTKHDFImageGridReader (transient)";
57 return "VTKHDFImageGridReader";
58 }
59
60 void _open(const std::string& filename, typename GridReader::FieldNames& field_names) override {
61 _file = HDF5File{filename, _comm, HDF5File::read_only};
62
63 const auto type = VTKHDF::get_file_type(_file.value());
64 if (type != "ImageData")
65 throw ValueError("Incompatible VTK-HDF type: '" + type + "', expected 'ImageData'.");
66
67 VTKHDF::check_version_compatibility(_file.value(), {2, 0});
68
69 if (_file->exists("/VTKHDF/Steps"))
70 _file->visit_attribute("/VTKHDF/Steps/NSteps", [&] (auto&& field) {
71 _num_steps.emplace();
72 field.export_to(_num_steps.value());
73 _step_index.emplace(0);
74 });
75
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));
79 };
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);
83
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);
88
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);
95
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);
102
103 auto extents = _file.value().template read_attribute_to<std::vector<std::size_t>>("/VTKHDF/WholeExtent");
104 if (extents.size() != 6)
105 throw SizeError("Unexpected 'WholeExtents' attribute (size = " + as_string(extents.size()) + ").");
106 _piece_location.lower_left = {extents[0], extents[2], extents[4]};
107 _piece_location.upper_right = {extents[1], extents[3], extents[5]};
108 }
109
110 void _close() override {
111 _file = {};
112 _piece_location = {};
113 _cell_spacing = {};
114 _point_origin = {};
115 _direction = {};
116 }
117
118 typename GridReader::PieceLocation _location() const override {
119 return _piece_location;
120 }
121
122 typename GridReader::Vector _spacing() const override {
123 return _cell_spacing;
124 }
125
126 typename GridReader::Vector _origin() const override {
127 return _point_origin;
128 }
129
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),
136 std::size_t{1},
137 std::multiplies{}
138 );
139 }
140
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),
146 std::size_t{1},
147 std::multiplies{}
148 );
149 }
150
151 std::size_t _number_of_pieces() const override {
152 return 1;
153 }
154
155 void _visit_cells(const typename GridReader::CellVisitor& visitor) const override {
156 VTK::CommonDetail::visit_structured_cells(visitor, _make_vtk_extents_array());
157 }
158
159 FieldPtr _points() const override {
160 std::array<std::size_t, 6> extents = _make_vtk_extents_array();
161 extents[1] += 1;
162 extents[3] += 1;
163 extents[5] += 1;
164 return make_field_ptr(LazyField{
165 int{}, // dummy source
166 MDLayout{{VTK::CommonDetail::number_of_entities(extents), std::size_t{vtk_space_dim}}},
167 Precision<double>{},
168 [ex=extents, o=_point_origin, s=_cell_spacing, d=_direction] (const int&) {
169 return VTK::CommonDetail::serialize_structured_points(ex, o, s, d);
170 }
171 });
172 }
173
174 bool _is_sequence() const override {
175 return _is_transient();
176 }
177
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();
182 }
183
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},
189 .count = {1}
190 });
191 }
192
193 void _set_step(std::size_t step_idx, typename GridReader::FieldNames&) override {
194 if (step_idx >= _number_of_steps())
195 throw ValueError("Only " + as_string(_number_of_steps()) + " available");
196 _step_index = step_idx;
197 }
198
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();
202 auto offset = count;
203 std::ranges::fill(offset, 0);
204 if (_step) {
205 count.at(0) = 1;
206 offset.at(0) = _step.value();
207 }
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(
213 MDLayout{{size}}
214 ))->serialized();
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)}}
218 ))->serialized();
219 throw InvalidState("Unexpected field layout: " + as_string(layout));
220 }, HDF5::Slice{.offset = offset, .count = count});
221 };
222 }
223
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()}};
230 _file.value(),
231 std::move(layout),
232 _file.value().get_precision(path).value(),
233 _serialization_callback(path, _number_of_cells())
234 });
235 }
236
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()}};
243 _file.value(),
244 std::move(layout),
245 _file.value().get_precision(path).value(),
246 _serialization_callback(path, _number_of_points())
247 });
248 }
249
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)
254 return make_field_ptr(VTKHDF::DataSetField{_file.value(), path});
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");
259
260 auto offset = dims;
261 auto count = offset;
262 std::ranges::fill(offset, 0);
263 count.at(0) = 1;
264 offset.at(0) = _is_transient() ? _step_index.value() : 0;
266 _file.value(),
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});
273 }
274 });
275 }
276
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];
285 return extents;
286 }
287
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();
293 return {};
294 }
295
296 std::size_t _grid_dimension() const {
297 return VTK::CommonDetail::structured_grid_dimension(_get_extents());
298 }
299
300 std::array<std::size_t, 3> _get_extents() const {
301 return {
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]
305 };
306 }
307
308 bool _is_transient() const {
309 return static_cast<bool>(_num_steps);
310 }
311
312 Communicator _comm;
313 std::optional<HDF5File> _file;
314 typename GridReader::PieceLocation _piece_location;
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;
320};
321
322} // namespace GridFormat
323
324#endif // GRIDFORMAT_HAVE_HIGH_FIVE
325#endif // GRIDFORMAT_VTK_HDF_IMAGE_GRID_READER_HPP_
Abstract base class for all readers, defines the common interface.
Definition: reader.hpp:51
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
Reader for the VTK-HDF file format for image grids.
Definition: hdf_image_grid_reader.hpp:37
Field implementation that draws values from an open HDF5 file upon request.
Definition: hdf_common.hpp:104
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.
Definition: reader.hpp:266
Describes the location of a piece within a distributed structured grid.
Definition: reader.hpp:57