GCC Code Coverage Report


Directory: gridformat/
File: gridformat/vtk/hdf_image_grid_writer.hpp
Date: 2024-11-20 14:41:59
Exec Total Coverage
Lines: 188 195 96.4%
Functions: 267 313 85.3%
Branches: 305 650 46.9%

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 526 ImageSpecs(O&& origin, S&& spacing, E&& extents)
57 526 : origin{Ranges::to_array<dim, CT>(std::forward<O>(origin))}
58 526 , spacing{Ranges::to_array<dim, CT>(std::forward<S>(spacing))}
59 526 , extents{Ranges::to_array<dim, std::size_t>(std::forward<E>(extents))}
60 526 {}
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 23 explicit VTKHDFImageGridWriterImpl(LValueReferenceOf<const Grid> grid)
70 requires(std::is_same_v<Communicator, NullCommunicator>)
71
3/6
✓ Branch 2 taken 13 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 13 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 13 times.
✗ Branch 11 not taken.
115 : GridWriter<Grid>(grid.get(), ".hdf", writer_opts)
72 23 {}
73
74 149 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 77 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 77 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 77 times.
✗ Branch 11 not taken.
745 , _comm{comm}
78 149 {}
79
80 6 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 6 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
6 : VTKHDFImageGridWriterImpl(grid.get(), NullCommunicator{}, filename_without_extension, std::move(opts))
88 6 {}
89
90 15 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 8 , _comm{comm}
100
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 , _timeseries_filename{std::move(filename_without_extension) + ".hdf"}
101
1/2
✓ Branch 3 taken 14 times.
✗ Branch 4 not taken.
30 , _transient_opts{std::move(opts)} {
102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 if (!_transient_opts.static_grid)
103 throw ValueError("Transient VTK-HDF ImageData files do not support evolving grids");
104 15 }
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 63 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 15 times.
✓ Branch 1 taken 48 times.
63 if (this->_step_count == 0)
120
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 HDF5File::clear(_timeseries_filename, _comm);
121
122
1/2
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
63 HDF5File file{_timeseries_filename, _comm, HDF5File::Mode::append};
123
1/2
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
63 _write_to(file);
124
2/4
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 63 times.
✗ Branch 5 not taken.
63 file.write_attribute(this->_step_count+1, "/VTKHDF/Steps/NSteps");
125
2/4
✓ Branch 2 taken 63 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 63 times.
✗ Branch 6 not taken.
189 file.write(std::array{t}, "/VTKHDF/Steps/Values");
126
1/2
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
126 return _timeseries_filename;
127 63 }
128
129 174 void _write(const std::string& filename_with_ext) const {
130 if constexpr (is_transient)
131 throw InvalidState("This overload only works for non-transient output");
132
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
174 HDF5File file{filename_with_ext, _comm, HDF5File::Mode::overwrite};
133
1/2
✓ Branch 1 taken 92 times.
✗ Branch 2 not taken.
174 _write_to(file);
134 174 }
135
136 274 void _write_to(HDF5File& file) const {
137
6/10
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 22 times.
✓ Branch 7 taken 133 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 21 times.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
274 const ImageSpecs my_specs{origin(this->grid()), spacing(this->grid()), extents(this->grid())};
138
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
274 const auto [overall_specs, my_offset] = _get_image_specs(my_specs);
139 274 const auto cell_slice_base = _make_slice(
140
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
274 overall_specs.extents,
141 my_specs.extents,
142 my_offset
143 );
144
145 // in order to avoid overlapping hyperslabs for point data in parallel I/O,
146 // we only write the last entries of the slab (per direction) when our portion
147 // of the image is the last portion of the overall image in that direction.
148
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
274 const auto my_point_extents = Ranges::to_array<dim>(
149
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
274 std::views::iota(std::size_t{0}, dim)
150
2/4
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
866 | std::views::transform([&] (std::size_t dir) {
151 390 const auto overall_end = overall_specs.extents[dir];
152 390 const auto my_end = my_specs.extents[dir] + my_offset[dir];
153
8/8
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 98 times.
✓ Branch 4 taken 45 times.
✓ Branch 5 taken 174 times.
✓ Branch 8 taken 5 times.
✓ Branch 9 taken 24 times.
✓ Branch 12 taken 5 times.
✓ Branch 13 taken 16 times.
390 return my_end < overall_end ? my_specs.extents[dir] : my_specs.extents[dir] + 1;
154 }));
155 274 const auto point_slice_base = _make_slice<true>(
156
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
274 overall_specs.extents,
157 my_point_extents,
158 my_offset
159 );
160
161
2/4
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
548 file.write_attribute(std::array<std::size_t, 2>{(is_transient ? 2 : 1), 0}, "/VTKHDF/Version");
162
3/6
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 155 times.
✗ Branch 8 not taken.
548 file.write_attribute(Ranges::to_array<vtk_space_dim>(overall_specs.origin), "/VTKHDF/Origin");
163
3/6
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 155 times.
✗ Branch 8 not taken.
548 file.write_attribute(Ranges::to_array<vtk_space_dim>(overall_specs.spacing), "/VTKHDF/Spacing");
164
3/6
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 155 times.
✗ Branch 8 not taken.
548 file.write_attribute(VTK::CommonDetail::get_extents(overall_specs.extents), "/VTKHDF/WholeExtent");
165
3/6
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 155 times.
✗ Branch 8 not taken.
548 file.write_attribute(_get_direction(), "/VTKHDF/Direction");
166
2/4
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
274 file.write_attribute("ImageData", "/VTKHDF/Type");
167
168
2/4
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
367 std::ranges::for_each(this->_meta_data_field_names(), [&] (const std::string& name) {
169 if constexpr (is_transient) {
170
3/4
✓ Branch 0 taken 108 times.
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 108 times.
144 if (this->_step_count > 0 && _transient_opts.static_meta_data) {
171 file.write(std::array{0}, "/VTKHDF/Steps/FieldDataOffsets/" + name);
172 return;
173 } else {
174
2/4
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 144 times.
✗ Branch 6 not taken.
144 file.write(std::array{this->_step_count}, "/VTKHDF/Steps/FieldDataOffsets/" + name);
175 }
176
1/2
✓ Branch 1 taken 144 times.
✗ Branch 2 not taken.
144 auto field_ptr = this->_get_meta_data_field_ptr(name);
177
2/4
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 144 times.
✗ Branch 6 not taken.
144 auto sub = make_field_ptr(TransformedField{field_ptr, FieldTransformation::as_sub_field});
178
3/6
✓ Branch 2 taken 144 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 144 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 144 times.
✗ Branch 10 not taken.
144 _write_field(file, sub, "/VTKHDF/FieldData/" + name, _slice_from(sub));
179 144 } else {
180
1/8
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
18 auto field_ptr = this->_get_meta_data_field_ptr(name);
181
3/24
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 18 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 18 times.
✗ Branch 10 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 62 not taken.
✗ Branch 63 not taken.
✗ Branch 66 not taken.
✗ Branch 67 not taken.
18 _write_field(file, field_ptr, "/VTKHDF/FieldData/" + name, _slice_from(field_ptr));
182 18 }
183
184 });
185
186 274 std::vector<std::size_t> non_zero_extents;
187
2/4
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
548 std::ranges::copy(
188
2/4
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
664 my_specs.extents | std::views::filter([] (auto e) { return e != 0; }),
189 std::back_inserter(non_zero_extents)
190 );
191
192
2/4
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
1099 std::ranges::for_each(this->_point_field_names(), [&] (const std::string& name) {
193
4/8
✓ Branch 1 taken 243 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 468 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 72 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 42 times.
✗ Branch 11 not taken.
825 auto field_ptr = _reshape(
194
8/16
✓ Branch 1 taken 243 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 243 times.
✗ Branch 5 not taken.
✓ Branch 11 taken 468 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 468 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.
1650 VTK::make_vtk_field(this->_get_point_field_ptr(name)),
195
4/8
✓ Branch 1 taken 243 times.
✗ Branch 2 not taken.
✓ Branch 8 taken 468 times.
✗ Branch 9 not taken.
✓ Branch 15 taken 72 times.
✗ Branch 16 not taken.
✓ Branch 22 taken 42 times.
✗ Branch 23 not taken.
1650 Ranges::incremented(non_zero_extents, 1) | std::views::reverse,
196
4/8
✓ Branch 1 taken 243 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 468 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 72 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 42 times.
✗ Branch 11 not taken.
825 point_slice_base.count
197 );
198
8/16
✓ Branch 1 taken 243 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 243 times.
✗ Branch 6 not taken.
✓ Branch 12 taken 468 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 468 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.
825 _write_field(file, field_ptr, "/VTKHDF/PointData/" + name, point_slice_base);
199 825 });
200
201
2/4
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
553 std::ranges::for_each(this->_cell_field_names(), [&] (const std::string& name) {
202
2/8
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
279 auto field_ptr = _reshape(
203
4/16
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 129 times.
✗ Branch 5 not taken.
✓ Branch 11 taken 150 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 150 times.
✗ Branch 15 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
558 VTK::make_vtk_field(this->_get_cell_field_ptr(name)),
204 non_zero_extents | std::views::reverse,
205
2/8
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 150 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
279 cell_slice_base.count
206 );
207
4/16
✓ Branch 1 taken 129 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 129 times.
✗ Branch 6 not taken.
✓ Branch 12 taken 150 times.
✗ Branch 13 not taken.
✓ Branch 16 taken 150 times.
✗ Branch 17 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
279 _write_field(file, field_ptr, "/VTKHDF/CellData/" + name, cell_slice_base);
208 279 });
209 274 }
210
211 template<std::ranges::range E, std::ranges::range S>
212 2208 FieldPtr _reshape(FieldPtr f, const E& row_major_extents, S&& _slice_end) const {
213
1/2
✓ Branch 2 taken 1104 times.
✗ Branch 3 not taken.
2208 auto flat = _flatten(f);
214
1/2
✓ Branch 2 taken 1104 times.
✗ Branch 3 not taken.
2208 auto structured = _make_structured(flat, row_major_extents);
215
1/2
✓ Branch 2 taken 1104 times.
✗ Branch 3 not taken.
2208 const auto structured_layout = structured->layout();
216
217 2208 std::vector<std::size_t> slice_end;
218
2/4
✓ Branch 1 taken 1104 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1104 times.
✗ Branch 5 not taken.
2208 std::ranges::copy(_slice_end, std::back_inserter(slice_end));
219
3/4
✓ Branch 2 taken 1812 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 708 times.
✓ Branch 5 taken 1104 times.
3624 for (std::size_t i = slice_end.size(); i < structured_layout.dimension(); ++i)
220
2/4
✓ Branch 1 taken 708 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 708 times.
✗ Branch 5 not taken.
1416 slice_end.push_back(structured_layout.extent(i));
221 4416 return transform(structured, FieldTransformation::take_slice({
222 2208 .from = std::vector<std::size_t>(slice_end.size(), 0),
223 .to = slice_end
224
1/2
✓ Branch 1 taken 1104 times.
✗ Branch 2 not taken.
4416 }));
225
2/6
✓ Branch 2 taken 1104 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1104 times.
✗ Branch 6 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
4416 }
226
227 1986 FieldPtr _flatten(FieldPtr f) const {
228
1/2
✓ Branch 2 taken 1104 times.
✗ Branch 3 not taken.
1986 const auto layout = f->layout();
229
3/4
✓ Branch 1 taken 1104 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 756 times.
✓ Branch 4 taken 348 times.
1986 if (layout.dimension() <= 2)
230 1350 return f;
231 // vtk requires tensors to be made flat
232
3/6
✓ Branch 1 taken 348 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 348 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 348 times.
✗ Branch 8 not taken.
636 auto nl = MDLayout{{layout.extent(0), layout.number_of_entries(1)}};
233
3/6
✓ Branch 2 taken 348 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 348 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 348 times.
✗ Branch 10 not taken.
636 return transform(f, FieldTransformation::reshape_to(std::move(nl)));
234 1986 }
235
236 template<std::ranges::range E>
237 2208 FieldPtr _make_structured(FieldPtr f, const E& row_major_extents) const {
238
1/2
✓ Branch 2 taken 1104 times.
✗ Branch 3 not taken.
2208 const auto layout = f->layout();
239
1/2
✓ Branch 2 taken 1104 times.
✗ Branch 3 not taken.
2208 std::vector<std::size_t> target_layout(Ranges::size(row_major_extents));
240
1/2
✓ Branch 2 taken 1104 times.
✗ Branch 3 not taken.
2208 std::ranges::copy(row_major_extents, target_layout.begin());
241
3/4
✓ Branch 1 taken 1104 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 708 times.
✓ Branch 4 taken 396 times.
2208 if (layout.dimension() > 1)
242
2/4
✓ Branch 1 taken 708 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 708 times.
✗ Branch 5 not taken.
1416 target_layout.push_back(layout.extent(1));
243
3/6
✓ Branch 2 taken 1104 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1104 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1104 times.
✗ Branch 10 not taken.
4416 return transform(f, FieldTransformation::reshape_to(MDLayout{std::move(target_layout)}));
244 2208 }
245
246 274 auto _get_image_specs(const ImageSpecs& piece_specs) const {
247 using OffsetType = std::ranges::range_value_t<decltype(piece_specs.extents)>;
248
2/2
✓ Branch 1 taken 108 times.
✓ Branch 2 taken 47 times.
274 if (Parallel::size(_comm) > 1) {
249 192 PVTK::StructuredParallelGridHelper helper{_comm};
250
1/2
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
192 const auto all_origins = Parallel::gather(_comm, piece_specs.origin, root_rank);
251
1/2
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
192 const auto all_extents = Parallel::gather(_comm, piece_specs.extents, root_rank);
252
1/2
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
192 const auto is_negative_axis = VTK::CommonDetail::structured_grid_axis_orientation(piece_specs.spacing);
253
2/3
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
360 const auto [exts_begin, exts_end, whole_extent, origin] = helper.compute_extents_and_origin(
254 all_origins,
255 all_extents,
256 is_negative_axis,
257
1/2
✓ Branch 2 taken 96 times.
✗ Branch 3 not taken.
360 basis(this->grid())
258 );
259
260
1/2
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
192 const auto my_whole_extent = Parallel::broadcast(_comm, whole_extent, root_rank);
261
1/2
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
192 const auto my_whole_origin = Parallel::broadcast(_comm, origin, root_rank);
262
2/4
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 108 times.
✗ Branch 5 not taken.
192 const auto my_extent_offset = Parallel::scatter(_comm, Ranges::flat(exts_begin), root_rank);
263 return std::make_tuple(
264
1/2
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
192 ImageSpecs{my_whole_origin, piece_specs.spacing, my_whole_extent},
265 my_extent_offset
266
1/2
✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
192 );
267 192 } else {
268
1/2
✓ Branch 1 taken 47 times.
✗ Branch 2 not taken.
82 return std::make_tuple(piece_specs, std::vector<OffsetType>(dim, 0));
269 }
270 }
271
272 template<bool increment = false, typename TotalExtents, typename Extents, typename Offsets>
273 620 HDF5::Slice _make_slice(const TotalExtents& total_extents,
274 const Extents& extents,
275 const Offsets& offsets) const {
276 620 HDF5::Slice result;
277 620 result.total_size.emplace();
278
279 // use only those dimensions that are not zero
280 620 std::vector<bool> is_nonzero;
281
2/4
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 310 times.
✗ Branch 5 not taken.
1240 std::ranges::copy(
282
2/4
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 310 times.
✗ Branch 5 not taken.
1400 total_extents | std::views::transform([] (const auto& v) { return v != 0; }),
283 std::back_inserter(is_nonzero)
284 );
285
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
1400 std::ranges::for_each(total_extents, [&, i=0] (const auto& value) mutable {
286
16/32
✓ Branch 1 taken 121 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 121 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 121 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 121 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 219 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 219 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 219 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 219 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.
780 if (is_nonzero[i++])
287
8/16
✓ Branch 2 taken 121 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 121 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 219 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 219 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.
780 result.total_size.value().push_back(value + (increment ? 1 : 0));
288 });
289
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
1400 std::ranges::for_each(extents, [&, i=0] (const auto& value) mutable {
290
16/32
✓ Branch 1 taken 121 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 121 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 121 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 121 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 219 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 219 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 219 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 219 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.
780 if (is_nonzero[i++])
291 780 result.count.push_back(value);
292 });
293
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
1400 std::ranges::for_each(offsets, [&, i=0] (const auto& value) mutable {
294
16/32
✓ Branch 1 taken 121 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 121 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 121 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 121 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 219 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 219 times.
✗ Branch 17 not taken.
✓ Branch 19 taken 219 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 219 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.
780 if (is_nonzero[i++])
295 780 result.offset.push_back(value);
296 });
297
298 // slices in VTK are accessed with the last coordinate first (i.e. values[z][y][x])
299
2/4
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 310 times.
✗ Branch 5 not taken.
620 std::ranges::reverse(result.total_size.value());
300
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
620 std::ranges::reverse(result.count);
301
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
620 std::ranges::reverse(result.offset);
302
303 620 return result;
304 620 }
305
306 274 auto _get_direction() const {
307 using T = MDRangeScalar<decltype(basis(this->grid()))>;
308 std::array<T, vtk_space_dim*vtk_space_dim> coefficients;
309
1/2
✓ Branch 1 taken 134 times.
✗ Branch 2 not taken.
274 std::ranges::fill(coefficients, T{0});
310
2/4
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
274 std::ranges::for_each(
311
3/5
✓ Branch 1 taken 141 times.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 141 times.
✗ Branch 5 not taken.
274 Matrix{basis(this->grid())}.transposed(),
312 910 [it = coefficients.begin()] (const std::ranges::range auto& row) mutable {
313 390 std::ranges::copy(row, it);
314 390 std::advance(it, vtk_space_dim);
315 }
316 );
317 274 return coefficients;
318 }
319
320 2232 void _write_field(HDF5File& file,
321 FieldPtr field,
322 const std::string& path,
323 const HDF5::Slice& slice) const {
324 2232 const std::size_t dimension_offset = is_transient ? 1 : 0;
325
2/4
✓ Branch 1 taken 1266 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1266 times.
✗ Branch 6 not taken.
4464 std::vector<std::size_t> size(slice.total_size.value().size() + dimension_offset);
326
1/2
✓ Branch 2 taken 1266 times.
✗ Branch 3 not taken.
4464 std::vector<std::size_t> count(slice.count.size() + dimension_offset);
327
1/2
✓ Branch 2 taken 1266 times.
✗ Branch 3 not taken.
2232 std::vector<std::size_t> offset(slice.offset.size() + dimension_offset);
328
5/10
✓ Branch 1 taken 1266 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1266 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1266 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1266 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 1266 times.
✗ Branch 14 not taken.
2232 std::ranges::copy(slice.total_size.value(), std::ranges::begin(size | std::views::drop(dimension_offset)));
329
4/8
✓ Branch 1 taken 1266 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1266 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1266 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1266 times.
✗ Branch 11 not taken.
2232 std::ranges::copy(slice.count, std::ranges::begin(count | std::views::drop(dimension_offset)));
330
4/8
✓ Branch 1 taken 1266 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1266 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1266 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 1266 times.
✗ Branch 11 not taken.
2232 std::ranges::copy(slice.offset, std::ranges::begin(offset | std::views::drop(dimension_offset)));
331
332
1/2
✓ Branch 2 taken 1266 times.
✗ Branch 3 not taken.
2232 const auto layout = field->layout();
333
1/2
✓ Branch 1 taken 1266 times.
✗ Branch 2 not taken.
2232 const bool is_vector_field = layout.dimension() > size.size() - dimension_offset;
334
2/2
✓ Branch 0 taken 708 times.
✓ Branch 1 taken 558 times.
2232 if (is_vector_field)
335
1/2
✓ Branch 1 taken 708 times.
✗ Branch 2 not taken.
1296 std::ranges::for_each(
336
2/4
✓ Branch 1 taken 708 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 708 times.
✗ Branch 6 not taken.
2592 std::views::iota(size.size() - dimension_offset, layout.dimension()),
337 1416 [&] (const std::size_t codim) {
338
8/16
✓ Branch 1 taken 220 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 220 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 412 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 412 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 48 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 48 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 28 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 28 times.
✗ Branch 26 not taken.
708 size.push_back(layout.extent(codim));
339
8/16
✓ Branch 1 taken 220 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 220 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 412 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 412 times.
✗ Branch 12 not taken.
✓ Branch 15 taken 48 times.
✗ Branch 16 not taken.
✓ Branch 18 taken 48 times.
✗ Branch 19 not taken.
✓ Branch 22 taken 28 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 28 times.
✗ Branch 26 not taken.
708 count.push_back(layout.extent(codim));
340
4/8
✓ Branch 1 taken 220 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 412 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 48 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 28 times.
✗ Branch 11 not taken.
708 offset.push_back(0);
341 }
342 );
343
344 if constexpr (is_transient) {
345
1/2
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
1095 size.at(0) = 1;
346
1/2
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
1095 count.at(0) = 1;
347
1/2
✓ Branch 1 taken 690 times.
✗ Branch 2 not taken.
1095 offset.at(0) = 0;
348
1/2
✓ Branch 2 taken 690 times.
✗ Branch 3 not taken.
1095 FieldPtr sub_field = transform(field, FieldTransformation::as_sub_field);
349
1/2
✓ Branch 2 taken 690 times.
✗ Branch 3 not taken.
1095 file.write(*sub_field, path, HDF5::Slice{
350 1095 .offset = std::move(offset),
351 1095 .count = std::move(count),
352 1095 .total_size = std::move(size)
353 });
354 1095 } else {
355
1/2
✓ Branch 2 taken 576 times.
✗ Branch 3 not taken.
1137 file.write(*field, path, HDF5::Slice{
356 1137 .offset = std::move(offset),
357 1137 .count = std::move(count),
358 1137 .total_size = std::move(size)
359 });
360 }
361
0/4
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
4464 }
362
363 246 HDF5::Slice _slice_from(FieldPtr field) const {
364
1/2
✓ Branch 2 taken 162 times.
✗ Branch 3 not taken.
246 const auto layout = field->layout();
365
2/4
✓ Branch 1 taken 162 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 162 times.
✗ Branch 5 not taken.
492 std::vector<std::size_t> dims(layout.dimension());
366
2/4
✓ Branch 1 taken 162 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 162 times.
✗ Branch 5 not taken.
246 std::vector<std::size_t> offset(layout.dimension(), 0);
367
1/2
✓ Branch 1 taken 162 times.
✗ Branch 2 not taken.
246 layout.export_to(dims);
368 return {
369 246 .offset = std::move(offset),
370 .count = dims,
371 .total_size = dims
372 246 };
373
2/8
✓ Branch 3 taken 162 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 162 times.
✗ Branch 7 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
246 }
374
375 Communicator _comm;
376 std::string _timeseries_filename = "";
377 VTK::HDFTransientOptions _transient_opts;
378 };
379
380 /*!
381 * \ingroup VTK
382 * \brief Writer for the VTK HDF file format for image grids.
383 */
384 template<Concepts::ImageGrid G, Concepts::Communicator C = NullCommunicator>
385 class VTKHDFImageGridWriter : public VTKHDFImageGridWriterImpl<false, G, C> {
386 using ParentType = VTKHDFImageGridWriterImpl<false, G, C>;
387 public:
388 using ParentType::ParentType;
389 };
390
391 /*!
392 * \ingroup VTK
393 * \brief Writer for the transient VTK HDF file format for image grids.
394 */
395 template<Concepts::ImageGrid G, Concepts::Communicator C = NullCommunicator>
396 class VTKHDFImageGridTimeSeriesWriter : public VTKHDFImageGridWriterImpl<true, G, C> {
397 using ParentType = VTKHDFImageGridWriterImpl<true, G, C>;
398 public:
399 using ParentType::ParentType;
400 };
401
402 template<Concepts::ImageGrid G>
403 VTKHDFImageGridWriter(const G&) -> VTKHDFImageGridWriter<G, NullCommunicator>;
404 template<Concepts::ImageGrid G, Concepts::Communicator C>
405 VTKHDFImageGridWriter(const G&, const C&) -> VTKHDFImageGridWriter<G, C>;
406
407 template<Concepts::ImageGrid G>
408 VTKHDFImageGridTimeSeriesWriter(const G&, std::string, VTK::HDFTransientOptions = {}) -> VTKHDFImageGridTimeSeriesWriter<G, NullCommunicator>;
409 template<Concepts::ImageGrid G, Concepts::Communicator C>
410 VTKHDFImageGridTimeSeriesWriter(const G&, const C&, std::string, VTK::HDFTransientOptions = {}) -> VTKHDFImageGridTimeSeriesWriter<G, C>;
411
412
413 namespace Traits {
414
415 template<typename... Args>
416 struct WritesConnectivity<VTKHDFImageGridWriter<Args...>> : public std::false_type {};
417
418 template<typename... Args>
419 struct WritesConnectivity<VTKHDFImageGridTimeSeriesWriter<Args...>> : public std::false_type {};
420
421 } // namespace Traits
422 } // namespace GridFormat
423
424 #endif // GRIDFORMAT_HAVE_HIGH_FIVE
425 #endif // GRIDFORMAT_VTK_HDF_IMAGE_GRID_WRITER_HPP_
426