GCC Code Coverage Report


Directory: gridformat/
File: gridformat/vtk/hdf_unstructured_grid_writer.hpp
Date: 2026-10-12 17:50:57
Exec Total Coverage
Lines: 206 210 98.1%
Functions: 377 447 84.3%
Branches: 266 510 52.2%

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 unstructured grids.
7 */
8 #ifndef GRIDFORMAT_VTK_HDF_UNSTRUCTURED_GRID_WRITER_HPP_
9 #define GRIDFORMAT_VTK_HDF_UNSTRUCTURED_GRID_WRITER_HPP_
10 #if GRIDFORMAT_HAVE_HIGH_FIVE
11
12 #include <type_traits>
13 #include <ostream>
14
15 #include <gridformat/common/exceptions.hpp>
16 #include <gridformat/common/md_layout.hpp>
17 #include <gridformat/common/concepts.hpp>
18 #include <gridformat/common/lvalue_reference.hpp>
19
20 #include <gridformat/parallel/communication.hpp>
21 #include <gridformat/parallel/concepts.hpp>
22
23 #include <gridformat/grid/concepts.hpp>
24 #include <gridformat/grid/writer.hpp>
25
26 #include <gridformat/vtk/common.hpp>
27 #include <gridformat/vtk/hdf_common.hpp>
28
29 namespace GridFormat {
30
31 template<bool is_transient,
32 Concepts::UnstructuredGrid G,
33 Concepts::Communicator Communicator = NullCommunicator>
34 class VTKHDFUnstructuredGridWriterImpl : public GridDetail::WriterBase<is_transient, G>::type {
35 static constexpr int root_rank = 0;
36 static constexpr std::size_t vtk_space_dim = 3;
37
38 using CT = CoordinateType<G>;
39 template<typename T> using Vector = std::array<T, vtk_space_dim>;
40 template<typename T> using Tensor = std::array<T, vtk_space_dim*vtk_space_dim>;
41
42 using IOContext = VTKHDF::IOContext;
43 using HDF5File = HDF5::File<Communicator>;
44
45 static constexpr WriterOptions writer_opts{
46 .use_structured_grid_ordering = false,
47 .append_null_terminator_to_strings = true
48 };
49
50 struct TimeSeriesOffsets {
51 std::size_t cell_offset;
52 std::size_t connectivity_offset;
53 std::size_t point_offset;
54 };
55
56 public:
57 using Grid = G;
58
59 17 explicit VTKHDFUnstructuredGridWriterImpl(LValueReferenceOf<const Grid> grid)
60 requires(!is_transient && std::is_same_v<Communicator, NullCommunicator>)
61
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.
85 : GridWriter<Grid>(grid.get(), ".hdf", writer_opts)
62 17 {}
63
64 39 explicit VTKHDFUnstructuredGridWriterImpl(LValueReferenceOf<const Grid> grid, const Communicator& comm)
65 requires(!is_transient && std::is_copy_constructible_v<Communicator>)
66 : GridWriter<Grid>(grid.get(), ".hdf", writer_opts)
67
3/6
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 23 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 23 times.
✗ Branch 11 not taken.
195 , _comm{comm}
68 39 {}
69
70 25 explicit VTKHDFUnstructuredGridWriterImpl(LValueReferenceOf<const Grid> grid,
71 std::string filename_without_extension,
72 VTK::HDFTransientOptions opts = {})
73 requires(is_transient && std::is_same_v<Communicator, NullCommunicator>)
74 : TimeSeriesGridWriter<Grid>(grid.get(), writer_opts)
75 , _comm{}
76
1/2
✓ Branch 1 taken 20 times.
✗ Branch 2 not taken.
25 , _timeseries_filename{std::move(filename_without_extension) + ".hdf"}
77
1/2
✓ Branch 3 taken 20 times.
✗ Branch 4 not taken.
50 , _transient_opts{std::move(opts)}
78 25 {}
79
80 31 explicit VTKHDFUnstructuredGridWriterImpl(LValueReferenceOf<const Grid> grid,
81 const Communicator& comm,
82 std::string filename_without_extension,
83 VTK::HDFTransientOptions opts = {})
84 requires(is_transient && std::is_copy_constructible_v<Communicator>)
85 : TimeSeriesGridWriter<Grid>(grid.get(), writer_opts)
86 30 , _comm{comm}
87
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
31 , _timeseries_filename{std::move(filename_without_extension) + ".hdf"}
88
1/2
✓ Branch 3 taken 23 times.
✗ Branch 4 not taken.
62 , _transient_opts{std::move(opts)}
89 31 {}
90
91 const Communicator& communicator() const {
92 return _comm;
93 }
94
95 private:
96 ✗ void _write(std::ostream&) const {
97 ✗ throw InvalidState("VTKHDFUnstructuredGridWriter does not support export into stream");
98 }
99
100 65 void _write(const std::string& filename_with_ext) const {
101 if constexpr (is_transient)
102 throw InvalidState("This overload only works for non-transient output");
103 { // scope to close the file before writing meta data on rank 0 with serial I/O
104
1/2
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
65 HDF5File file{filename_with_ext, _comm, HDF5File::overwrite};
105
2/2
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 2 times.
65 _write_to(file);
106 65 }
107 63 _write_meta_data(filename_with_ext);
108 61 }
109
110 196 std::string _write(double t) {
111 if constexpr (!is_transient)
112 throw InvalidState("This overload only works for transient output");
113
114
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 121 times.
196 if (this->_step_count == 0)
115 56 HDF5File::clear(_timeseries_filename, _comm);
116
117 { // scope to close the file before writing meta data on rank 0 with serial I/O
118
1/2
✓ Branch 1 taken 164 times.
✗ Branch 2 not taken.
196 HDF5File file{_timeseries_filename, _comm, HDF5File::append};
119
1/2
✓ Branch 1 taken 164 times.
✗ Branch 2 not taken.
196 const auto offsets = _write_to(file);
120
121
2/4
✓ Branch 1 taken 164 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 164 times.
✗ Branch 5 not taken.
196 file.write_attribute(this->_step_count+1, "/VTKHDF/Steps/NSteps");
122
2/4
✓ Branch 2 taken 164 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 164 times.
✗ Branch 6 not taken.
588 file.write(std::array{t}, "VTKHDF/Steps/Values");
123
3/6
✓ Branch 2 taken 164 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 164 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 164 times.
✗ Branch 9 not taken.
980 file.write(std::vector{offsets.point_offset}, "VTKHDF/Steps/PointOffsets");
124
3/6
✓ Branch 2 taken 164 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 164 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 164 times.
✗ Branch 9 not taken.
980 file.write(std::vector{std::array{offsets.cell_offset}}, "/VTKHDF/Steps/CellOffsets");
125
3/6
✓ Branch 2 taken 164 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 164 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 164 times.
✗ Branch 9 not taken.
980 file.write(std::vector{std::array{offsets.connectivity_offset}}, "VTKHDF/Steps/ConnectivityIdOffsets");
126
127
6/10
✓ Branch 2 taken 164 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 86 times.
✓ Branch 6 taken 78 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 86 times.
✓ Branch 9 taken 78 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 86 times.
✗ Branch 12 not taken.
980 file.write(std::vector{Parallel::size(_comm)}, "/VTKHDF/Steps/NumberOfParts");
128
4/4
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 99 times.
196 if (this->_step_count > 0 && _transient_opts.static_grid) {
129
3/6
✓ Branch 2 taken 22 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 22 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 22 times.
✗ Branch 9 not taken.
88 file.write(
130
2/4
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
66 std::vector{_get_last_step_data(file, "PartOffsets")},
131 "/VTKHDF/Steps/PartOffsets"
132 );
133 } else {
134 348 const std::size_t offset = this->_step_count == 0
135
2/2
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 43 times.
174 ? 0
136
9/16
✓ Branch 1 taken 99 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 99 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 99 times.
✓ Branch 8 taken 21 times.
✓ Branch 9 taken 60 times.
✓ Branch 10 taken 61 times.
✓ Branch 11 taken 21 times.
✓ Branch 12 taken 60 times.
✓ Branch 13 taken 22 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
174 : _get_last_step_data(file, "PartOffsets") + Parallel::size(_comm);
137
3/6
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 142 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 142 times.
✗ Branch 9 not taken.
870 file.write(std::vector{offset}, "/VTKHDF/Steps/PartOffsets");
138 }
139 196 }
140 196 _write_meta_data(_timeseries_filename);
141 196 return _timeseries_filename;
142 }
143
144 379 TimeSeriesOffsets _write_to(HDF5File& file) const {
145
2/4
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 209 times.
✗ Branch 5 not taken.
758 file.write_attribute(std::array<std::size_t, 2>{(is_transient ? 2 : 1), 0}, "/VTKHDF/Version");
146
2/4
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 209 times.
✗ Branch 5 not taken.
379 file.write_attribute("UnstructuredGrid", "/VTKHDF/Type");
147
148 TimeSeriesOffsets offsets;
149
1/2
✓ Branch 2 taken 209 times.
✗ Branch 3 not taken.
379 const auto context = IOContext::from(this->grid(), _comm, root_rank);
150
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
379 _write_num_cells_and_points(file, context);
151
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
379 offsets.point_offset = _write_coordinates(file, context);
152
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
379 offsets.connectivity_offset = _write_connectivity(file, context);
153
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
379 offsets.cell_offset = _write_types(file, context);
154
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
379 _write_offsets(file, context);
155
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
379 _write_point_fields(file, context);
156
2/2
✓ Branch 1 taken 207 times.
✓ Branch 2 taken 2 times.
379 _write_cell_fields(file, context);
157
158 750 return offsets;
159 379 }
160
161 379 void _write_num_cells_and_points(HDF5File& file, const IOContext& context) const {
162
7/11
✓ Branch 2 taken 27 times.
✓ Branch 3 taken 182 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 27 times.
✓ Branch 6 taken 182 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 27 times.
✓ Branch 9 taken 182 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 27 times.
✗ Branch 12 not taken.
1516 _write_values(file, "/VTKHDF/NumberOfPoints", std::vector{number_of_points(this->grid())}, context);
163
7/11
✓ Branch 2 taken 27 times.
✓ Branch 3 taken 182 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 27 times.
✓ Branch 6 taken 182 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 27 times.
✓ Branch 9 taken 182 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 27 times.
✗ Branch 12 not taken.
1516 _write_values(file, "/VTKHDF/NumberOfCells", std::vector{number_of_cells(this->grid())}, context);
164 379 }
165
166 379 std::size_t _write_coordinates(HDF5File& file, const IOContext& context) const {
167 if constexpr (is_transient) {
168
4/4
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 99 times.
292 if (this->_step_count > 0 && _transient_opts.static_grid)
169
2/4
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
72 return _get_last_step_data(file, "PointOffsets");
170 }
171
1/2
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
343 const auto coords_field = VTK::make_coordinates_field<CT>(this->grid(), false);
172
2/4
✓ Branch 1 taken 187 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 187 times.
✗ Branch 5 not taken.
343 const auto offset = _get_current_offset(file, "/VTKHDF/Points");
173
2/4
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 187 times.
✗ Branch 6 not taken.
686 _write_point_field(file, "/VTKHDF/Points", *coords_field, context);
174 343 return offset;
175 343 }
176
177 379 std::size_t _write_connectivity(HDF5File& file, const IOContext& context) const {
178 if constexpr (is_transient) {
179
4/4
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 99 times.
292 if (this->_step_count > 0 && _transient_opts.static_grid)
180
2/4
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
72 return _get_last_step_data(file, "ConnectivityIdOffsets");
181 }
182
1/2
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
343 const auto point_id_map = make_point_id_map(this->grid());
183
1/2
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
343 const auto connectivity_field = VTK::make_connectivity_field(this->grid(), point_id_map);
184
2/4
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 187 times.
✗ Branch 6 not taken.
343 const auto num_entries = connectivity_field->layout().number_of_entries();
185
1/2
✓ Branch 1 taken 187 times.
✗ Branch 2 not taken.
1029 const auto my_num_ids = std::vector{static_cast<long>(num_entries)};
186
1/2
✓ Branch 1 taken 187 times.
✗ Branch 2 not taken.
343 std::vector<long> connectivity(num_entries);
187
1/2
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
343 connectivity_field->export_to(connectivity);
188
2/4
✓ Branch 1 taken 187 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 187 times.
✗ Branch 5 not taken.
686 const auto offset = _get_current_offset(file, "/VTKHDF/Connectivity");
189
2/4
✓ Branch 1 taken 187 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 187 times.
✗ Branch 5 not taken.
686 _write_values(file, "/VTKHDF/Connectivity", connectivity, context);
190
2/4
✓ Branch 1 taken 187 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 187 times.
✗ Branch 5 not taken.
343 _write_values(file, "/VTKHDF/NumberOfConnectivityIds", my_num_ids, context);
191 343 return offset;
192 343 }
193
194 379 std::size_t _write_types(HDF5File& file, const IOContext& context) const {
195 if constexpr (is_transient) {
196
4/4
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 99 times.
292 if (this->_step_count > 0 && _transient_opts.static_grid)
197
2/4
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
72 return _get_last_step_data(file, "CellOffsets");
198 }
199
1/2
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
343 const auto types_field = VTK::make_cell_types_field(this->grid());
200
3/6
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 187 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 187 times.
✗ Branch 9 not taken.
343 std::vector<std::uint8_t> types(types_field->layout().number_of_entries());
201
1/2
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
343 types_field->export_to(types);
202
2/4
✓ Branch 1 taken 187 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 187 times.
✗ Branch 5 not taken.
686 const auto offset = _get_current_offset(file, "VTKHDF/Types");
203
2/4
✓ Branch 1 taken 187 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 187 times.
✗ Branch 5 not taken.
343 _write_values(file, "VTKHDF/Types", types, context);
204 343 return offset;
205 343 }
206
207 379 std::size_t _write_offsets(HDF5File& file, const IOContext& context) const {
208 if constexpr (is_transient) {
209
4/4
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 99 times.
292 if (this->_step_count > 0 && _transient_opts.static_grid)
210
2/4
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
72 return _get_last_step_data(file, "CellOffsets");
211 }
212
1/2
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
343 const auto offsets_field = VTK::make_offsets_field(this->grid());
213
2/4
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 187 times.
✗ Branch 6 not taken.
343 const auto num_offset_entries = offsets_field->layout().number_of_entries() + 1;
214
1/2
✓ Branch 1 taken 187 times.
✗ Branch 2 not taken.
343 std::vector<long> offsets(num_offset_entries);
215
1/2
✓ Branch 5 taken 187 times.
✗ Branch 6 not taken.
686 offsets_field->export_to(std::ranges::subrange(std::next(offsets.begin()), offsets.end()));
216 343 offsets[0] = long{0};
217
2/4
✓ Branch 1 taken 187 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 187 times.
✗ Branch 5 not taken.
686 const auto offset = _get_current_offset(file, "/VTKHDF/Offsets");
218
2/4
✓ Branch 1 taken 187 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 187 times.
✗ Branch 5 not taken.
343 _write_values(file, "/VTKHDF/Offsets", offsets, context);
219 343 return offset;
220 343 }
221
222 // hdf5 cannot write variable-length strings into files opened for parallel I/O, not even from
223 // a single rank. Therefore, all meta data is written by rank 0 once the (parallel) file is closed,
224 // reopened with the default driver. Meta data is assumed to be the same on all ranks.
225 375 void _write_meta_data(const std::string& filename) const {
226
2/2
✓ Branch 1 taken 205 times.
✓ Branch 2 taken 2 times.
609 VTKHDF::write_on_root(filename, _comm, root_rank, [&] (HDF5::File<>& file) {
227 148 _write_meta_data(file);
228 });
229 371 }
230
231 266 void _write_meta_data(HDF5::File<>& file) const {
232
3/4
✓ Branch 1 taken 148 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 146 times.
✓ Branch 5 taken 2 times.
782 std::ranges::for_each(this->_meta_data_field_names(), [&] (const std::string& name) {
233 452 VTKHDF::check_array_name(name);
234 if constexpr (is_transient) {
235
4/8
✓ Branch 0 taken 292 times.
✓ Branch 1 taken 86 times.
✓ Branch 2 taken 264 times.
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
378 if (this->_step_count > 0 && _transient_opts.static_meta_data)
236 264 VTKHDF::write_field_data_step_referencing(0, file, name);
237 else
238
2/8
✓ Branch 1 taken 114 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 114 times.
✗ Branch 5 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
114 VTKHDF::write_field_data_step(file, name, this->_get_meta_data_field_ptr(name));
239 } else {
240
4/8
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 60 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 10 not taken.
✓ Branch 12 taken 12 times.
✗ Branch 13 not taken.
72 VTKHDF::write_field_data(file, name, this->_get_meta_data_field_ptr(name));
241 }
242 });
243 262 }
244
245 379 void _write_point_fields(HDF5File& file, const IOContext& context) const {
246
2/4
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 209 times.
✗ Branch 5 not taken.
1229 std::ranges::for_each(this->_point_field_names(), [&] (const std::string& name) {
247
3/6
✓ Branch 1 taken 341 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 506 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
850 VTKHDF::check_array_name(name);
248 if constexpr (is_transient)
249
4/8
✓ Branch 1 taken 648 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 648 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 14 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 11 not taken.
662 _write_step_offset(
250 file,
251
2/4
✓ Branch 1 taken 648 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
1324 _get_current_offset(file, "/VTKHDF/PointData/" + name),
252
2/4
✓ Branch 1 taken 648 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
1324 "/VTKHDF/Steps/PointDataOffsets/" + name
253 );
254
9/18
✓ Branch 1 taken 341 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 341 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 341 times.
✗ Branch 8 not taken.
✓ Branch 14 taken 506 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 506 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 506 times.
✗ Branch 21 not taken.
✓ Branch 27 taken 3 times.
✗ Branch 28 not taken.
✓ Branch 30 taken 3 times.
✗ Branch 31 not taken.
✓ Branch 33 taken 3 times.
✗ Branch 34 not taken.
850 auto reshaped = _reshape(VTK::make_vtk_field(this->_get_point_field_ptr(name)));
255
6/12
✓ Branch 2 taken 341 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 341 times.
✗ Branch 6 not taken.
✓ Branch 11 taken 506 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 506 times.
✗ Branch 15 not taken.
✓ Branch 20 taken 3 times.
✗ Branch 21 not taken.
✓ Branch 23 taken 3 times.
✗ Branch 24 not taken.
850 _write_point_field(file, "/VTKHDF/PointData/" + name, *reshaped, context);
256 850 });
257 379 }
258
259 379 void _write_cell_fields(HDF5File& file, const IOContext& context) const {
260
3/4
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 207 times.
✓ Branch 5 taken 2 times.
1219 std::ranges::for_each(this->_cell_field_names(), [&] (const std::string& name) {
261
3/6
✓ Branch 1 taken 334 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 506 times.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
842 VTKHDF::check_array_name(name);
262 if constexpr (is_transient)
263
4/8
✓ Branch 1 taken 648 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 648 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 14 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 14 times.
✗ Branch 11 not taken.
662 _write_step_offset(
264 file,
265
2/4
✓ Branch 1 taken 648 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
1324 _get_current_offset(file, "VTKHDF/CellData/" + name),
266
2/4
✓ Branch 1 taken 648 times.
✗ Branch 2 not taken.
✓ Branch 6 taken 14 times.
✗ Branch 7 not taken.
1324 "/VTKHDF/Steps/CellDataOffsets/" + name
267 );
268
6/18
✓ Branch 1 taken 334 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 334 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 334 times.
✗ Branch 8 not taken.
✓ Branch 14 taken 506 times.
✗ Branch 15 not taken.
✓ Branch 17 taken 506 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 506 times.
✗ Branch 21 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
840 auto reshaped = _reshape(VTK::make_vtk_field(this->_get_cell_field_ptr(name)));
269
4/12
✓ Branch 2 taken 334 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 334 times.
✗ Branch 6 not taken.
✓ Branch 11 taken 506 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 506 times.
✗ Branch 15 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
840 _write_cell_field(file, "/VTKHDF/CellData/" + name, *reshaped, context);
270 840 });
271 375 }
272
273 3010 FieldPtr _reshape(FieldPtr field_ptr) const {
274 // VTK requires tensors to be written as flat fields
275
1/2
✓ Branch 2 taken 1690 times.
✗ Branch 3 not taken.
3010 const auto layout = field_ptr->layout();
276
3/4
✓ Branch 1 taken 1690 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 352 times.
✓ Branch 4 taken 1338 times.
3010 if (layout.dimension() > 2)
277
1/2
✓ Branch 2 taken 352 times.
✗ Branch 3 not taken.
2496 return make_field_ptr(ReshapedField{
278 field_ptr,
279
3/6
✓ Branch 1 taken 352 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 352 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 352 times.
✗ Branch 8 not taken.
1248 MDLayout{{layout.extent(0), layout.number_of_entries(1)}}
280
1/2
✓ Branch 1 taken 352 times.
✗ Branch 2 not taken.
624 });
281 2386 return field_ptr;
282 3010 }
283
284 template<Concepts::Scalar T>
285 2332 void _write_values(HDF5File& file,
286 const std::string& path,
287 const std::vector<T>& values,
288 const IOContext& context) const {
289
2/2
✓ Branch 0 taken 668 times.
✓ Branch 1 taken 498 times.
2332 if (context.is_parallel) {
290 1336 const auto num_values = values.size();
291
1/2
✓ Branch 1 taken 668 times.
✗ Branch 2 not taken.
1336 const auto total_num_values = Parallel::sum(_comm, num_values, root_rank);
292
1/2
✓ Branch 1 taken 668 times.
✗ Branch 2 not taken.
1336 const auto my_total_num_values = Parallel::broadcast(_comm, total_num_values, root_rank);
293
1/2
✓ Branch 1 taken 668 times.
✗ Branch 2 not taken.
1336 const auto my_offset = _accumulate_rank_offset(num_values);
294
3/10
✓ Branch 1 taken 668 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 668 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 668 times.
✗ Branch 8 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
6680 HDF5::Slice slice{
295 .offset = std::vector{my_offset},
296 .count = std::vector{num_values},
297 .total_size = std::vector{my_total_num_values}
298 };
299
2/4
✓ Branch 1 taken 668 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 668 times.
✗ Branch 5 not taken.
1336 file.write(values, path, slice);
300 1336 } else {
301
1/2
✓ Branch 2 taken 498 times.
✗ Branch 3 not taken.
996 file.write(values, path);
302 }
303 2332 }
304
305 1855 void _write_point_field(HDF5File& file,
306 const std::string& path,
307 const Field& field,
308 const IOContext& context) const {
309 1855 _write_field(file, path, field, context.is_parallel, context.my_point_offset, context.num_points_total);
310 1855 }
311
312 1498 void _write_cell_field(HDF5File& file,
313 const std::string& path,
314 const Field& field,
315 const IOContext& context) const {
316 1498 _write_field(file, path, field, context.is_parallel, context.my_cell_offset, context.num_cells_total);
317 1498 }
318
319 3353 void _write_field(HDF5File& file,
320 const std::string& path,
321 const Field& field,
322 bool is_parallel,
323 std::size_t main_offset,
324 std::size_t main_size) const {
325
2/2
✓ Branch 0 taken 1142 times.
✓ Branch 1 taken 735 times.
3353 if (is_parallel) {
326
1/2
✓ Branch 1 taken 1142 times.
✗ Branch 2 not taken.
2146 const auto layout = field.layout();
327
2/4
✓ Branch 1 taken 1142 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1142 times.
✗ Branch 5 not taken.
2146 std::vector<std::size_t> count(layout.dimension());
328
1/2
✓ Branch 1 taken 1142 times.
✗ Branch 2 not taken.
2146 layout.export_to(count);
329
330
1/2
✓ Branch 1 taken 1142 times.
✗ Branch 2 not taken.
2146 std::vector<std::size_t> size = count;
331
2/4
✓ Branch 1 taken 1142 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1142 times.
✗ Branch 5 not taken.
2146 std::vector<std::size_t> offset(layout.dimension(), 0);
332
1/2
✓ Branch 1 taken 1142 times.
✗ Branch 2 not taken.
2146 offset.at(0) = main_offset;
333
1/2
✓ Branch 1 taken 1142 times.
✗ Branch 2 not taken.
2146 size.at(0) = main_size;
334
335
1/2
✓ Branch 1 taken 1142 times.
✗ Branch 2 not taken.
2146 file.write(field, path, HDF5::Slice{
336 2146 .offset = std::move(offset),
337 2146 .count = std::move(count),
338 2146 .total_size = std::move(size)
339 });
340 2146 } else {
341
1/2
✓ Branch 2 taken 735 times.
✗ Branch 3 not taken.
1207 file.write(field, path);
342 }
343
0/4
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
5499 }
344
345 1244 std::size_t _accumulate_rank_offset(std::size_t my_size) const {
346
1/2
✓ Branch 1 taken 668 times.
✗ Branch 2 not taken.
1244 auto all_offsets = Parallel::gather(_comm, my_size, root_rank);
347
3/4
✓ Branch 1 taken 668 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 322 times.
✓ Branch 4 taken 346 times.
1244 if (Parallel::rank(_comm) == root_rank) {
348 1196 std::partial_sum(all_offsets.begin(), std::prev(all_offsets.end()), all_offsets.begin());
349
1/2
✓ Branch 4 taken 322 times.
✗ Branch 5 not taken.
1196 std::copy(std::next(all_offsets.rbegin()), all_offsets.rend(), all_offsets.rbegin());
350 598 all_offsets[0] = 0;
351 }
352
1/2
✓ Branch 1 taken 668 times.
✗ Branch 2 not taken.
1244 const auto my_offset = Parallel::scatter(_comm, all_offsets, root_rank);
353
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 668 times.
1244 if (my_offset.size() != 1)
354 ✗ throw ValueError("Unexpected scatter result");
355 2488 return my_offset[0];
356 1244 }
357
358 1388 void _write_step_offset(HDF5File& file,
359 const std::size_t offset,
360 const std::string& path) const {
361
1/2
✓ Branch 2 taken 1324 times.
✗ Branch 3 not taken.
1388 file.write(std::array{offset}, path);
362 1388 }
363
364 3660 std::size_t _get_current_offset(HDF5File& file, const std::string& path) const {
365
1/2
✓ Branch 1 taken 2072 times.
✗ Branch 2 not taken.
3660 const auto cur_dimensions = file.get_dimensions(path);
366
2/2
✓ Branch 1 taken 1442 times.
✓ Branch 2 taken 630 times.
7320 return cur_dimensions ? (*cur_dimensions)[0] : 0;
367 3660 }
368
369 228 std::size_t _get_last_step_data(const HDF5File& file, const std::string& sub_path) const {
370
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 209 times.
228 if (this->_step_count == 0)
371 ✗ throw ValueError("Last step data can only be read after at least one write");
372
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
228 const std::string path ="VTKHDF/Steps/" + sub_path;
373
374
2/4
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 209 times.
✗ Branch 5 not taken.
228 auto step_dimensions = file.get_dimensions(path).value();
375
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
228 std::ranges::fill(step_dimensions, std::size_t{1});
376
377
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
228 auto access_offset = step_dimensions;
378
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
228 std::ranges::fill(access_offset, std::size_t{0});
379
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
228 access_offset.at(0) = this->_step_count - 1;
380
381
1/2
✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
456 return file.template read_dataset_to<std::size_t>(path, HDF5::Slice{
382 228 .offset = std::move(access_offset),
383 228 .count = std::move(step_dimensions)
384 456 });
385
0/4
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
456 }
386
387 Communicator _comm;
388 std::string _timeseries_filename = "";
389 VTK::HDFTransientOptions _transient_opts;
390 };
391
392 /*!
393 * \ingroup VTK
394 * \brief Writer for the VTK HDF file format for unstructured grids.
395 */
396 template<Concepts::UnstructuredGrid G, Concepts::Communicator C = NullCommunicator>
397 class VTKHDFUnstructuredGridWriter : public VTKHDFUnstructuredGridWriterImpl<false, G, C> {
398 using ParentType = VTKHDFUnstructuredGridWriterImpl<false, G, C>;
399 public:
400 using ParentType::ParentType;
401 };
402
403 /*!
404 * \ingroup VTK
405 * \brief Writer for the transient VTK HDF file format for unstructured grids.
406 */
407 template<Concepts::UnstructuredGrid G, Concepts::Communicator C = NullCommunicator>
408 class VTKHDFUnstructuredTimeSeriesWriter : public VTKHDFUnstructuredGridWriterImpl<true, G, C> {
409 using ParentType = VTKHDFUnstructuredGridWriterImpl<true, G, C>;
410 public:
411 using ParentType::ParentType;
412 };
413
414 template<Concepts::UnstructuredGrid G>
415 VTKHDFUnstructuredGridWriter(const G&) -> VTKHDFUnstructuredGridWriter<G, NullCommunicator>;
416 template<Concepts::UnstructuredGrid G, Concepts::Communicator C>
417 VTKHDFUnstructuredGridWriter(const G&, const C&) -> VTKHDFUnstructuredGridWriter<G, C>;
418
419 template<Concepts::UnstructuredGrid G>
420 VTKHDFUnstructuredTimeSeriesWriter(const G&, std::string, VTK::HDFTransientOptions = {}) -> VTKHDFUnstructuredTimeSeriesWriter<G, NullCommunicator>;
421 template<Concepts::UnstructuredGrid G, Concepts::Communicator C>
422 VTKHDFUnstructuredTimeSeriesWriter(const G&, const C&, std::string, VTK::HDFTransientOptions = {}) -> VTKHDFUnstructuredTimeSeriesWriter<G, C>;
423
424 } // namespace GridFormat
425
426 #endif // GRIDFORMAT_HAVE_HIGH_FIVE
427 #endif // GRIDFORMAT_VTK_HDF_UNSTRUCTURED_GRID_WRITER_HPP_
428