GCC Code Coverage Report


Directory: gridformat/
File: gridformat/vtk/hdf_image_grid_writer.hpp
Date: 2026-10-12 17:50:57
Exec Total Coverage
Lines: 187 192 97.4%
Functions: 309 335 92.2%
Branches: 315 612 51.5%

Line Branch Exec Source
1 // SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2 // SPDX-License-Identifier: MIT
3 /*!
4 * \file
5 * \ingroup VTK
6 * \brief Writer for the VTK HDF file format for image grids.
7 */
8 #ifndef GRIDFORMAT_VTK_HDF_IMAGE_GRID_WRITER_HPP_
9 #define GRIDFORMAT_VTK_HDF_IMAGE_GRID_WRITER_HPP_
10 #if GRIDFORMAT_HAVE_HIGH_FIVE
11
12 #include <ranges>
13 #include <iterator>
14 #include <algorithm>
15 #include <utility>
16 #include <tuple>
17
18 #include <gridformat/common/exceptions.hpp>
19 #include <gridformat/common/md_layout.hpp>
20 #include <gridformat/common/concepts.hpp>
21 #include <gridformat/common/matrix.hpp>
22 #include <gridformat/common/ranges.hpp>
23 #include <gridformat/common/type_traits.hpp>
24 #include <gridformat/common/lvalue_reference.hpp>
25 #include <gridformat/common/field_transformations.hpp>
26
27 #include <gridformat/parallel/communication.hpp>
28 #include <gridformat/parallel/concepts.hpp>
29
30 #include <gridformat/grid/concepts.hpp>
31 #include <gridformat/grid/writer.hpp>
32
33 #include <gridformat/vtk/common.hpp>
34 #include <gridformat/vtk/parallel.hpp>
35 #include <gridformat/vtk/hdf_common.hpp>
36
37 namespace GridFormat {
38
39 template<bool is_transient, Concepts::ImageGrid Grid, Concepts::Communicator Communicator = NullCommunicator>
40 class VTKHDFImageGridWriterImpl : public GridDetail::WriterBase<is_transient, Grid>::type {
41 static constexpr int root_rank = 0;
42 static constexpr std::size_t dim = dimension<Grid>;
43 static constexpr std::size_t vtk_space_dim = 3;
44 static constexpr std::array<std::size_t, 2> version{1, 0};
45
46 using CT = CoordinateType<Grid>;
47 using IOContext = VTKHDF::IOContext;
48 using HDF5File = HDF5::File<Communicator>;
49
50 struct ImageSpecs {
51 const std::array<CT, dim> origin;
52 const std::array<CT, dim> spacing;
53 const std::array<std::size_t, dim> extents;
54
55 template<typename O, typename S, typename E>
56 638 ImageSpecs(O&& origin, S&& spacing, E&& extents)
57 638 : origin{Ranges::to_array<dim, CT>(std::forward<O>(origin))}
58 638 , spacing{Ranges::to_array<dim, CT>(std::forward<S>(spacing))}
59 638 , extents{Ranges::to_array<dim, std::size_t>(std::forward<E>(extents))}
60 638 {}
61 };
62
63 static constexpr WriterOptions writer_opts{
64 .use_structured_grid_ordering = true,
65 .append_null_terminator_to_strings = true
66 };
67
68 public:
69 24 explicit VTKHDFImageGridWriterImpl(LValueReferenceOf<const Grid> grid)
70 requires(std::is_same_v<Communicator, NullCommunicator>)
71
3/6
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 11 not taken.
120 : GridWriter<Grid>(grid.get(), ".hdf", writer_opts)
72 24 {}
73
74 151 explicit VTKHDFImageGridWriterImpl(LValueReferenceOf<const Grid> grid, const Communicator& comm)
75 requires(std::is_copy_constructible_v<Communicator>)
76 : GridWriter<Grid>(grid.get(), ".hdf", writer_opts)
77
3/6
✓ Branch 2 taken 79 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 79 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 79 times.
✗ Branch 11 not taken.
755 , _comm{comm}
78 151 {}
79
80 11 explicit VTKHDFImageGridWriterImpl(LValueReferenceOf<const Grid> grid,
81 std::string filename_without_extension,
82 VTK::HDFTransientOptions opts = {
83 .static_grid = true,
84 .static_meta_data = false
85 })
86 requires(is_transient && std::is_same_v<Communicator, NullCommunicator>)
87
2/4
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 8 not taken.
11 : VTKHDFImageGridWriterImpl(grid.get(), NullCommunicator{}, filename_without_extension, std::move(opts))
88 11 {}
89
90 26 explicit VTKHDFImageGridWriterImpl(LValueReferenceOf<const Grid> grid,
91 const Communicator& comm,
92 std::string filename_without_extension,
93 VTK::HDFTransientOptions opts = {
94 .static_grid = true,
95 .static_meta_data = false
96 })
97 requires(is_transient && std::is_copy_constructible_v<Communicator>)
98 : TimeSeriesGridWriter<Grid>(grid.get(), writer_opts)
99 14 , _comm{comm}
100
1/2
✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
26 , _timeseries_filename{std::move(filename_without_extension) + ".hdf"}
101
1/2
✓ Branch 3 taken 25 times.
✗ Branch 4 not taken.
52 , _transient_opts{std::move(opts)} {
102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if (!_transient_opts.static_grid)
103 ✗ throw ValueError("Transient VTK-HDF ImageData files do not support evolving grids");
104 26 }
105
106 const Communicator& communicator() const {
107 return _comm;
108 }
109
110 private:
111 ✗ void _write(std::ostream&) const {
112 ✗ throw InvalidState("VTKHDFImageGridWriter does not support export into stream");
113 }
114
115 96 std::string _write([[maybe_unused]] double t) {
116 if constexpr (!is_transient)
117 throw InvalidState("This overload only works for transient output");
118
119
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 70 times.
96 if (this->_step_count == 0)
120 26 HDF5File::clear(_timeseries_filename, _comm);
121
122 { // scope to close the file before writing meta data on rank 0 with serial I/O
123
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 HDF5File file{_timeseries_filename, _comm, HDF5File::Mode::append};
124
1/2
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
96 _write_to(file);
125
2/4
✓ Branch 1 taken 96 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 96 times.
✗ Branch 5 not taken.
96 file.write_attribute(this->_step_count+1, "/VTKHDF/Steps/NSteps");
126
2/4
✓ Branch 2 taken 96 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 96 times.
✗ Branch 6 not taken.
288 file.write(std::array{t}, "/VTKHDF/Steps/Values");
127 96 }
128 96 _write_meta_data(_timeseries_filename);
129 96 return _timeseries_filename;
130 }
131
132 177 void _write(const std::string& filename_with_ext) const {
133 if constexpr (is_transient)
134 throw InvalidState("This overload only works for non-transient output");
135 { // scope to close the file before writing meta data on rank 0 with serial I/O
136
1/2
✓ Branch 1 taken 95 times.
✗ Branch 2 not taken.
177 HDF5File file{filename_with_ext, _comm, HDF5File::Mode::overwrite};
137
1/2
✓ Branch 1 taken 95 times.
✗ Branch 2 not taken.
177 _write_to(file);
138 177 }
139 177 _write_meta_data(filename_with_ext);
140 177 }
141
142 // hdf5 cannot write variable-length strings into files opened for parallel I/O, not even from
143 // a single rank. Therefore, all meta data is written by rank 0 once the (parallel) file is closed,
144 // reopened with the default driver. Meta data is assumed to be the same on all ranks.
145 316 void _write_meta_data(const std::string& filename) const {
146
1/2
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
458 VTKHDF::write_on_root(filename, _comm, root_rank, [&] (HDF5::File<>& file) {
147 115 _write_meta_data(file);
148 });
149 316 }
150
151 186 void _write_meta_data(HDF5::File<>& file) const {
152
2/4
✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 115 times.
✗ Branch 5 not taken.
727 std::ranges::for_each(this->_meta_data_field_names(), [&] (const std::string& name) {
153 405 VTKHDF::check_array_name(name);
154 if constexpr (is_transient) {
155
4/4
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 63 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 164 times.
237 if (this->_step_count > 0 && _transient_opts.static_meta_data)
156 10 VTKHDF::write_field_data_step_referencing(0, file, name);
157 else
158
2/4
✓ Branch 1 taken 227 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 227 times.
✗ Branch 5 not taken.
227 VTKHDF::write_field_data_step(file, name, this->_get_meta_data_field_ptr(name));
159 } else {
160
8/16
✓ Branch 1 taken 64 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 64 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 80 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 80 times.
✗ Branch 13 not taken.
✓ Branch 17 taken 12 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 12 times.
✗ Branch 21 not taken.
✓ Branch 25 taken 12 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 12 times.
✗ Branch 29 not taken.
168 VTKHDF::write_field_data(file, name, this->_get_meta_data_field_ptr(name));
161 }
162 });
163 186 }
164
165 316 void _write_to(HDF5File& file) const {
166
6/10
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 46 times.
✓ Branch 7 taken 145 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 45 times.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
316 const ImageSpecs my_specs{origin(this->grid()), spacing(this->grid()), extents(this->grid())};
167
1/2
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
316 const auto [overall_specs, my_offset] = _get_image_specs(my_specs);
168 316 const auto cell_slice_base = _make_slice(
169
1/2
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
316 overall_specs.extents,
170 my_specs.extents,
171 my_offset
172 );
173
174 // in order to avoid overlapping hyperslabs for point data in parallel I/O,
175 // we only write the last entries of the slab (per direction) when our portion
176 // of the image is the last portion of the overall image in that direction.
177
1/2
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
316 const auto my_point_extents = Ranges::to_array<dim>(
178
1/2
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
316 std::views::iota(std::size_t{0}, dim)
179
2/4
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
968 | std::views::transform([&] (std::size_t dir) {
180 468 const auto overall_end = overall_specs.extents[dir];
181 468 const auto my_end = my_specs.extents[dir] + my_offset[dir];
182
8/8
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 184 times.
✓ Branch 4 taken 45 times.
✓ Branch 5 taken 156 times.
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 24 times.
✓ Branch 12 taken 5 times.
✓ Branch 13 taken 16 times.
468 return my_end < overall_end ? my_specs.extents[dir] : my_specs.extents[dir] + 1;
183 }));
184 316 const auto point_slice_base = _make_slice<true>(
185
1/2
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
316 overall_specs.extents,
186 my_point_extents,
187 my_offset
188 );
189
190
2/4
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
632 file.write_attribute(std::array<std::size_t, 2>{(is_transient ? 2 : 1), 0}, "/VTKHDF/Version");
191
3/6
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 191 times.
✗ Branch 8 not taken.
632 file.write_attribute(Ranges::to_array<vtk_space_dim>(overall_specs.origin), "/VTKHDF/Origin");
192
3/6
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 191 times.
✗ Branch 8 not taken.
632 file.write_attribute(Ranges::to_array<vtk_space_dim>(overall_specs.spacing), "/VTKHDF/Spacing");
193
3/6
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 191 times.
✗ Branch 8 not taken.
632 file.write_attribute(VTK::CommonDetail::get_extents(overall_specs.extents), "/VTKHDF/WholeExtent");
194
3/6
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 191 times.
✗ Branch 8 not taken.
632 file.write_attribute(_get_direction(), "/VTKHDF/Direction");
195
2/4
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
316 file.write_attribute("ImageData", "/VTKHDF/Type");
196
197 316 std::vector<std::size_t> non_zero_extents;
198
2/4
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
632 std::ranges::copy(
199
2/4
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
784 my_specs.extents | std::views::filter([] (auto e) { return e != 0; }),
200 std::back_inserter(non_zero_extents)
201 );
202
203
2/4
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
1165 std::ranges::for_each(this->_point_field_names(), [&] (const std::string& name) {
204
4/8
✓ Branch 1 taken 303 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 432 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 72 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 42 times.
✗ Branch 11 not taken.
849 VTKHDF::check_array_name(name);
205
4/8
✓ Branch 1 taken 303 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 432 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 72 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 42 times.
✗ Branch 11 not taken.
849 auto field_ptr = _reshape(
206
8/16
✓ Branch 1 taken 303 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 303 times.
✗ Branch 5 not taken.
✓ Branch 11 taken 432 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 432 times.
✗ Branch 15 not taken.
✓ Branch 21 taken 72 times.
✗ Branch 22 not taken.
✓ Branch 24 taken 72 times.
✗ Branch 25 not taken.
✓ Branch 31 taken 42 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 42 times.
✗ Branch 35 not taken.
1698 VTK::make_vtk_field(this->_get_point_field_ptr(name)),
207
4/8
✓ Branch 1 taken 303 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 432 times.
✗ Branch 9 not taken.
✓ Branch 15 taken 72 times.
✗ Branch 16 not taken.
✓ Branch 22 taken 42 times.
✗ Branch 23 not taken.
1698 Ranges::incremented(non_zero_extents, 1) | std::views::reverse,
208
4/8
✓ Branch 1 taken 303 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 432 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 72 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 42 times.
✗ Branch 11 not taken.
849 point_slice_base.count
209 );
210
8/16
✓ Branch 1 taken 303 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 303 times.
✗ Branch 6 not taken.
✓ Branch 12 taken 432 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 432 times.
✗ Branch 17 not taken.
✓ Branch 23 taken 72 times.
✗ Branch 24 not taken.
✓ Branch 27 taken 72 times.
✗ Branch 28 not taken.
✓ Branch 34 taken 42 times.
✗ Branch 35 not taken.
✓ Branch 38 taken 42 times.
✗ Branch 39 not taken.
849 _write_field(file, field_ptr, "/VTKHDF/PointData/" + name, point_slice_base);
211 849 });
212
213
2/4
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
1123 std::ranges::for_each(this->_cell_field_names(), [&] (const std::string& name) {
214
4/8
✓ Branch 1 taken 297 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 426 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 42 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 42 times.
✗ Branch 11 not taken.
807 VTKHDF::check_array_name(name);
215
4/8
✓ Branch 1 taken 297 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 426 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 42 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 42 times.
✗ Branch 11 not taken.
807 auto field_ptr = _reshape(
216
8/16
✓ Branch 1 taken 297 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 297 times.
✗ Branch 5 not taken.
✓ Branch 11 taken 426 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 426 times.
✗ Branch 15 not taken.
✓ Branch 21 taken 42 times.
✗ Branch 22 not taken.
✓ Branch 24 taken 42 times.
✗ Branch 25 not taken.
✓ Branch 31 taken 42 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 42 times.
✗ Branch 35 not taken.
1614 VTK::make_vtk_field(this->_get_cell_field_ptr(name)),
217 ✗ non_zero_extents | std::views::reverse,
218
4/8
✓ Branch 1 taken 297 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 426 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 42 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 42 times.
✗ Branch 11 not taken.
807 cell_slice_base.count
219 );
220
8/16
✓ Branch 1 taken 297 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 297 times.
✗ Branch 6 not taken.
✓ Branch 12 taken 426 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 426 times.
✗ Branch 17 not taken.
✓ Branch 23 taken 42 times.
✗ Branch 24 not taken.
✓ Branch 27 taken 42 times.
✗ Branch 28 not taken.
✓ Branch 34 taken 42 times.
✗ Branch 35 not taken.
✓ Branch 38 taken 42 times.
✗ Branch 39 not taken.
807 _write_field(file, field_ptr, "/VTKHDF/CellData/" + name, cell_slice_base);
221 807 });
222 316 }
223
224 template<std::ranges::range E, std::ranges::range S>
225 3312 FieldPtr _reshape(FieldPtr f, const E& row_major_extents, S&& _slice_end) const {
226
1/2
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
3312 auto flat = _flatten(f);
227
1/2
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
3312 auto structured = _make_structured(flat, row_major_extents);
228
1/2
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
3312 const auto structured_layout = structured->layout();
229
230 3312 std::vector<std::size_t> slice_end;
231
2/4
✓ Branch 1 taken 1656 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1656 times.
✗ Branch 5 not taken.
3312 std::ranges::copy(_slice_end, std::back_inserter(slice_end));
232
3/4
✓ Branch 2 taken 2700 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1044 times.
✓ Branch 5 taken 1656 times.
5400 for (std::size_t i = slice_end.size(); i < structured_layout.dimension(); ++i)
233
2/4
✓ Branch 1 taken 1044 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1044 times.
✗ Branch 5 not taken.
2088 slice_end.push_back(structured_layout.extent(i));
234 6624 return transform(structured, FieldTransformation::take_slice({
235 3312 .from = std::vector<std::size_t>(slice_end.size(), 0),
236 .to = slice_end
237
1/2
✓ Branch 1 taken 1656 times.
✗ Branch 2 not taken.
6624 }));
238
2/6
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1656 times.
✗ Branch 6 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
6624 }
239
240 3042 FieldPtr _flatten(FieldPtr f) const {
241
1/2
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
3042 const auto layout = f->layout();
242
3/4
✓ Branch 1 taken 1656 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1144 times.
✓ Branch 4 taken 512 times.
3042 if (layout.dimension() <= 2)
243 2078 return f;
244 // vtk requires tensors to be made flat
245
3/6
✓ Branch 1 taken 512 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 512 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 512 times.
✗ Branch 8 not taken.
964 auto nl = MDLayout{{layout.extent(0), layout.number_of_entries(1)}};
246
3/6
✓ Branch 2 taken 512 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 512 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 512 times.
✗ Branch 10 not taken.
964 return transform(f, FieldTransformation::reshape_to(std::move(nl)));
247 3042 }
248
249 template<std::ranges::range E>
250 3312 FieldPtr _make_structured(FieldPtr f, const E& row_major_extents) const {
251
1/2
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
3312 const auto layout = f->layout();
252
1/2
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
3312 std::vector<std::size_t> target_layout(Ranges::size(row_major_extents));
253
1/2
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
3312 std::ranges::copy(row_major_extents, target_layout.begin());
254
3/4
✓ Branch 1 taken 1656 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1044 times.
✓ Branch 4 taken 612 times.
3312 if (layout.dimension() > 1)
255
2/4
✓ Branch 1 taken 1044 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1044 times.
✗ Branch 5 not taken.
2088 target_layout.push_back(layout.extent(1));
256
3/6
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1656 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1656 times.
✗ Branch 10 not taken.
6624 return transform(f, FieldTransformation::reshape_to(MDLayout{std::move(target_layout)}));
257 3312 }
258
259 316 auto _get_image_specs(const ImageSpecs& piece_specs) const {
260 using OffsetType = std::ranges::range_value_t<decltype(piece_specs.extents)>;
261
2/2
✓ Branch 1 taken 128 times.
✓ Branch 2 taken 63 times.
316 if (Parallel::size(_comm) > 1) {
262 212 PVTK::StructuredParallelGridHelper helper{_comm};
263
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
212 const auto all_origins = Parallel::gather(_comm, piece_specs.origin, root_rank);
264
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
212 const auto all_extents = Parallel::gather(_comm, piece_specs.extents, root_rank);
265
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
212 const auto is_negative_axis = VTK::CommonDetail::structured_grid_axis_orientation(piece_specs.spacing);
266
2/3
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
400 const auto [exts_begin, exts_end, whole_extent, origin] = helper.compute_extents_and_origin(
267 all_origins,
268 all_extents,
269 is_negative_axis,
270
1/2
✓ Branch 2 taken 116 times.
✗ Branch 3 not taken.
400 basis(this->grid())
271 );
272
273
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
212 const auto my_whole_extent = Parallel::broadcast(_comm, whole_extent, root_rank);
274
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
212 const auto my_whole_origin = Parallel::broadcast(_comm, origin, root_rank);
275
2/4
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 128 times.
✗ Branch 5 not taken.
212 const auto my_extent_offset = Parallel::scatter(_comm, Ranges::flat(exts_begin), root_rank);
276 ✗ return std::make_tuple(
277
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
212 ImageSpecs{my_whole_origin, piece_specs.spacing, my_whole_extent},
278 my_extent_offset
279
1/2
✓ Branch 1 taken 128 times.
✗ Branch 2 not taken.
212 );
280 212 } else {
281
1/2
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
104 return std::make_tuple(piece_specs, std::vector<OffsetType>(dim, 0));
282 }
283 }
284
285 template<bool increment = false, typename TotalExtents, typename Extents, typename Offsets>
286 764 HDF5::Slice _make_slice(const TotalExtents& total_extents,
287 const Extents& extents,
288 const Offsets& offsets) const {
289 764 HDF5::Slice result;
290 764 result.total_size.emplace();
291
292 // use only those dimensions that are not zero
293 764 std::vector<bool> is_nonzero;
294
2/4
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 382 times.
✗ Branch 5 not taken.
1528 std::ranges::copy(
295
2/4
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 382 times.
✗ Branch 5 not taken.
1700 total_extents | std::views::transform([] (const auto& v) { return v != 0; }),
296 std::back_inserter(is_nonzero)
297 );
298
1/2
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
1700 std::ranges::for_each(total_extents, [&, i=0] (const auto& value) mutable {
299
16/32
✓ Branch 1 taken 217 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 217 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 217 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 217 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 201 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 201 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 201 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 201 times.
✗ Branch 26 not taken.
✓ Branch 29 taken 29 times.
✗ Branch 30 not taken.
✓ Branch 32 taken 29 times.
✗ Branch 33 not taken.
✓ Branch 36 taken 29 times.
✗ Branch 37 not taken.
✓ Branch 39 taken 29 times.
✗ Branch 40 not taken.
✓ Branch 43 taken 21 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 21 times.
✗ Branch 47 not taken.
✓ Branch 50 taken 21 times.
✗ Branch 51 not taken.
✓ Branch 53 taken 21 times.
✗ Branch 54 not taken.
936 if (is_nonzero[i++])
300
8/16
✓ Branch 2 taken 217 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 217 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 201 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 201 times.
✗ Branch 15 not taken.
✓ Branch 18 taken 29 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 29 times.
✗ Branch 23 not taken.
✓ Branch 26 taken 21 times.
✗ Branch 27 not taken.
✓ Branch 30 taken 21 times.
✗ Branch 31 not taken.
936 result.total_size.value().push_back(value + (increment ? 1 : 0));
301 });
302
1/2
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
1700 std::ranges::for_each(extents, [&, i=0] (const auto& value) mutable {
303
16/32
✓ Branch 1 taken 217 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 217 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 217 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 217 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 201 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 201 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 201 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 201 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 29 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 29 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 29 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 29 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 21 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 21 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 21 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 21 times.
✗ Branch 47 not taken.
936 if (is_nonzero[i++])
304 936 result.count.push_back(value);
305 });
306
1/2
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
1700 std::ranges::for_each(offsets, [&, i=0] (const auto& value) mutable {
307
16/32
✓ Branch 1 taken 217 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 217 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 217 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 217 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 201 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 201 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 201 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 201 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 29 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 29 times.
✗ Branch 29 not taken.
✓ Branch 31 taken 29 times.
✗ Branch 32 not taken.
✓ Branch 34 taken 29 times.
✗ Branch 35 not taken.
✓ Branch 37 taken 21 times.
✗ Branch 38 not taken.
✓ Branch 40 taken 21 times.
✗ Branch 41 not taken.
✓ Branch 43 taken 21 times.
✗ Branch 44 not taken.
✓ Branch 46 taken 21 times.
✗ Branch 47 not taken.
936 if (is_nonzero[i++])
308 936 result.offset.push_back(value);
309 });
310
311 // slices in VTK are accessed with the last coordinate first (i.e. values[z][y][x])
312
2/4
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 382 times.
✗ Branch 5 not taken.
764 std::ranges::reverse(result.total_size.value());
313
1/2
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
764 std::ranges::reverse(result.count);
314
1/2
✓ Branch 1 taken 382 times.
✗ Branch 2 not taken.
764 std::ranges::reverse(result.offset);
315
316 764 return result;
317 764 }
318
319 316 auto _get_direction() const {
320 using T = MDRangeScalar<decltype(basis(this->grid()))>;
321 std::array<T, vtk_space_dim*vtk_space_dim> coefficients;
322
1/2
✓ Branch 1 taken 148 times.
✗ Branch 2 not taken.
316 std::ranges::fill(coefficients, T{0});
323
2/4
✓ Branch 1 taken 191 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 191 times.
✗ Branch 5 not taken.
316 std::ranges::for_each(
324
3/5
✓ Branch 1 taken 177 times.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 177 times.
✗ Branch 5 not taken.
316 Matrix{basis(this->grid())}.transposed(),
325 988 [it = coefficients.begin()] (const std::ranges::range auto& row) mutable {
326 468 std::ranges::copy(row, it);
327 468 std::advance(it, vtk_space_dim);
328 }
329 );
330 316 return coefficients;
331 }
332
333 3042 void _write_field(HDF5File& file,
334 FieldPtr field,
335 const std::string& path,
336 const HDF5::Slice& slice) const {
337 3042 const std::size_t dimension_offset = is_transient ? 1 : 0;
338
2/4
✓ Branch 1 taken 1656 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1656 times.
✗ Branch 6 not taken.
6084 std::vector<std::size_t> size(slice.total_size.value().size() + dimension_offset);
339
1/2
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
6084 std::vector<std::size_t> count(slice.count.size() + dimension_offset);
340
1/2
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
3042 std::vector<std::size_t> offset(slice.offset.size() + dimension_offset);
341
5/10
✓ Branch 1 taken 1656 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1656 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1656 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1656 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1656 times.
✗ Branch 14 not taken.
3042 std::ranges::copy(slice.total_size.value(), std::ranges::begin(size | std::views::drop(dimension_offset)));
342
4/8
✓ Branch 1 taken 1656 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1656 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1656 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1656 times.
✗ Branch 11 not taken.
3042 std::ranges::copy(slice.count, std::ranges::begin(count | std::views::drop(dimension_offset)));
343
4/8
✓ Branch 1 taken 1656 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1656 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1656 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1656 times.
✗ Branch 11 not taken.
3042 std::ranges::copy(slice.offset, std::ranges::begin(offset | std::views::drop(dimension_offset)));
344
345
1/2
✓ Branch 2 taken 1656 times.
✗ Branch 3 not taken.
3042 const auto layout = field->layout();
346
1/2
✓ Branch 1 taken 1656 times.
✗ Branch 2 not taken.
3042 const bool is_vector_field = layout.dimension() > size.size() - dimension_offset;
347
2/2
✓ Branch 0 taken 1044 times.
✓ Branch 1 taken 612 times.
3042 if (is_vector_field)
348
1/2
✓ Branch 1 taken 1044 times.
✗ Branch 2 not taken.
1968 std::ranges::for_each(
349
2/4
✓ Branch 1 taken 1044 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1044 times.
✗ Branch 6 not taken.
3936 std::views::iota(size.size() - dimension_offset, layout.dimension()),
350 2088 [&] (const std::size_t codim) {
351
8/16
✓ Branch 1 taken 340 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 340 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 572 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 572 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 76 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 76 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 56 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 56 times.
✗ Branch 26 not taken.
1044 size.push_back(layout.extent(codim));
352
8/16
✓ Branch 1 taken 340 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 340 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 572 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 572 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 76 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 76 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 56 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 56 times.
✗ Branch 26 not taken.
1044 count.push_back(layout.extent(codim));
353
4/8
✓ Branch 1 taken 340 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 572 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 76 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 56 times.
✗ Branch 11 not taken.
1044 offset.push_back(0);
354 }
355 );
356
357 if constexpr (is_transient) {
358
1/2
✓ Branch 1 taken 588 times.
✗ Branch 2 not taken.
918 size.at(0) = 1;
359
1/2
✓ Branch 1 taken 588 times.
✗ Branch 2 not taken.
918 count.at(0) = 1;
360
1/2
✓ Branch 1 taken 588 times.
✗ Branch 2 not taken.
918 offset.at(0) = 0;
361
1/2
✓ Branch 2 taken 588 times.
✗ Branch 3 not taken.
918 FieldPtr sub_field = transform(field, FieldTransformation::as_sub_field);
362
1/2
✓ Branch 2 taken 588 times.
✗ Branch 3 not taken.
918 file.write(*sub_field, path, HDF5::Slice{
363 918 .offset = std::move(offset),
364 918 .count = std::move(count),
365 918 .total_size = std::move(size)
366 });
367 918 } else {
368
1/2
✓ Branch 2 taken 1068 times.
✗ Branch 3 not taken.
2124 file.write(*field, path, HDF5::Slice{
369 2124 .offset = std::move(offset),
370 2124 .count = std::move(count),
371 2124 .total_size = std::move(size)
372 });
373 }
374
0/4
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
6084 }
375
376 Communicator _comm;
377 std::string _timeseries_filename = "";
378 VTK::HDFTransientOptions _transient_opts;
379 };
380
381 /*!
382 * \ingroup VTK
383 * \brief Writer for the VTK HDF file format for image grids.
384 */
385 template<Concepts::ImageGrid G, Concepts::Communicator C = NullCommunicator>
386 class VTKHDFImageGridWriter : public VTKHDFImageGridWriterImpl<false, G, C> {
387 using ParentType = VTKHDFImageGridWriterImpl<false, G, C>;
388 public:
389 using ParentType::ParentType;
390 };
391
392 /*!
393 * \ingroup VTK
394 * \brief Writer for the transient VTK HDF file format for image grids.
395 */
396 template<Concepts::ImageGrid G, Concepts::Communicator C = NullCommunicator>
397 class VTKHDFImageGridTimeSeriesWriter : public VTKHDFImageGridWriterImpl<true, G, C> {
398 using ParentType = VTKHDFImageGridWriterImpl<true, G, C>;
399 public:
400 using ParentType::ParentType;
401 };
402
403 template<Concepts::ImageGrid G>
404 VTKHDFImageGridWriter(const G&) -> VTKHDFImageGridWriter<G, NullCommunicator>;
405 template<Concepts::ImageGrid G, Concepts::Communicator C>
406 VTKHDFImageGridWriter(const G&, const C&) -> VTKHDFImageGridWriter<G, C>;
407
408 template<Concepts::ImageGrid G>
409 VTKHDFImageGridTimeSeriesWriter(const G&, std::string, VTK::HDFTransientOptions = {}) -> VTKHDFImageGridTimeSeriesWriter<G, NullCommunicator>;
410 template<Concepts::ImageGrid G, Concepts::Communicator C>
411 VTKHDFImageGridTimeSeriesWriter(const G&, const C&, std::string, VTK::HDFTransientOptions = {}) -> VTKHDFImageGridTimeSeriesWriter<G, C>;
412
413
414 namespace Traits {
415
416 template<typename... Args>
417 struct WritesConnectivity<VTKHDFImageGridWriter<Args...>> : public std::false_type {};
418
419 template<typename... Args>
420 struct WritesConnectivity<VTKHDFImageGridTimeSeriesWriter<Args...>> : public std::false_type {};
421
422 } // namespace Traits
423 } // namespace GridFormat
424
425 #endif // GRIDFORMAT_HAVE_HIGH_FIVE
426 #endif // GRIDFORMAT_VTK_HDF_IMAGE_GRID_WRITER_HPP_
427