39class VTKHDFImageGridReader :
public GridReader {
40 using Communicator = NullCommunicator;
41 using HDF5File = HDF5::File<Communicator>;
42 static constexpr std::size_t vtk_space_dim = 3;
45 explicit VTKHDFImageGridReader() =
default;
47 template<Concepts::Communicator C>
48 explicit VTKHDFImageGridReader(C&&) {
50 std::is_same_v<std::remove_cvref_t<C>, NullCommunicator>,
51 "Cannot read vtk-hdf image grid files in parallel"
56 std::string _name()
const override {
58 return "VTKHDFImageGridReader (transient)";
59 return "VTKHDFImageGridReader";
63 _file = HDF5File{
filename, _comm, HDF5File::read_only};
66 if (type !=
"ImageData")
67 throw ValueError(
"Incompatible VTK-HDF type: '" + type +
"', expected 'ImageData'.");
71 if (_file->exists(
"/VTKHDF/Steps"))
72 _file->visit_attribute(
"/VTKHDF/Steps/NSteps", [&] (
auto&& field) {
74 field.export_to(_num_steps.value());
75 _step_index.emplace(0);
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));
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);
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);
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);
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);
105 auto extents = _file.value().template read_attribute_to<std::vector<std::size_t>>(
"/VTKHDF/WholeExtent");
107 throw SizeError(
"Unexpected 'WholeExtents' attribute (size = " + as_string(
extents.size()) +
").");
112 void _close()
override {
114 _piece_location = {};
121 return _piece_location;
124 typename GridReader::Vector _spacing()
const override {
125 return _cell_spacing;
128 typename GridReader::Vector _origin()
const override {
129 return _point_origin;
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),
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),
153 std::size_t _number_of_pieces()
const override {
157 void _visit_cells(
const typename GridReader::CellVisitor& visitor)
const override {
158 VTK::CommonDetail::visit_structured_cells(visitor, _make_vtk_extents_array());
162 std::array<std::size_t, 6>
extents = _make_vtk_extents_array();
168 MDLayout{{VTK::CommonDetail::number_of_entities(
extents), std::size_t{vtk_space_dim}}},
170 [ex=
extents, o=_point_origin, s=_cell_spacing, d=_direction] (
const int&) {
171 return VTK::CommonDetail::serialize_structured_points(ex, o, s, d);
176 bool _is_sequence()
const override {
177 return _is_transient();
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();
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},
196 if (step_idx >= _number_of_steps())
197 throw ValueError(
"Only " + as_string(_number_of_steps()) +
" available");
198 _step_index = step_idx;
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();
205 std::ranges::fill(offset, 0);
208 offset.at(0) = _step.value();
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(
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)}}
221 throw InvalidState(
"Unexpected field layout: " + as_string(layout));
222 }, HDF5::Slice{.offset = offset, .count = count});
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()}};
234 _file.value().get_precision(path).value(),
235 _serialization_callback(path, _number_of_cells())
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()}};
247 _file.value().get_precision(path).value(),
248 _serialization_callback(path, _number_of_points())
252 FieldPtr _meta_data_field(std::string_view
name)
const override {
256 _is_transient() ? std::optional{_step_index.value()} : std::nullopt
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];
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();
279 std::size_t _grid_dimension()
const {
280 return VTK::CommonDetail::structured_grid_dimension(_get_extents());
283 std::array<std::size_t, 3> _get_extents()
const {
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]
291 bool _is_transient()
const {
292 return static_cast<bool>(_num_steps);
296 std::optional<HDF5File> _file;
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;