GCC Code Coverage Report


Directory: gridformat/
File: gridformat/grid/converter.hpp
Date: 2024-11-20 14:41:59
Exec Total Coverage
Lines: 92 114 80.7%
Functions: 67 87 77.0%
Branches: 59 206 28.6%

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 Grid
6 * \brief Converter between grid formats.
7 */
8 #ifndef GRIDFORMAT_GRID_CONVERTER_HPP_
9 #define GRIDFORMAT_GRID_CONVERTER_HPP_
10
11 #include <array>
12 #include <vector>
13 #include <ranges>
14 #include <concepts>
15 #include <utility>
16 #include <numeric>
17 #include <optional>
18 #include <cstdint>
19
20 #include <gridformat/common/field.hpp>
21 #include <gridformat/common/exceptions.hpp>
22
23 #include <gridformat/grid/cell_type.hpp>
24 #include <gridformat/grid/reader.hpp>
25 #include <gridformat/grid/writer.hpp>
26
27 namespace GridFormat {
28
29 #ifndef DOXYGEN
30 namespace ConverterDetail {
31
32 struct ConverterGrid {
33 const GridReader& reader;
34 std::vector<std::array<double, 3>> points;
35 std::vector<std::pair<CellType, std::vector<std::size_t>>> cells;
36
37 35 explicit ConverterGrid(const GridReader& r) : reader{r} {}
38
39 81 void make_grid() {
40 81 points.clear();
41 81 cells.clear();
42 81 _make_points();
43 81 _make_cells();
44 81 }
45
46 private:
47 81 void _make_points() {
48
1/2
✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
81 const auto in_points = reader.points();
49
1/2
✓ Branch 2 taken 81 times.
✗ Branch 3 not taken.
81 const auto in_layout = in_points->layout();
50
1/2
✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
81 const auto in_np = in_layout.extent(0);
51
3/6
✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 81 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 81 times.
✗ Branch 7 not taken.
81 const auto in_dim = in_layout.dimension() > 1 ? in_layout.extent(1) : 0;
52
2/4
✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 81 times.
81 if (in_np != reader.number_of_points())
53 throw SizeError("Mismatch between stored and defined number of points.");
54
55
1/2
✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
81 points.reserve(in_np);
56
1/2
✓ Branch 2 taken 81 times.
✗ Branch 3 not taken.
243 in_points->visit_field_values([&] <typename T> (std::span<const T> values) {
57
2/4
✓ Branch 1 taken 81 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 81 times.
✗ Branch 5 not taken.
25646 std::ranges::for_each(std::views::iota(std::size_t{0}, in_np), [&] (auto p_idx) {
58
1/22
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 33 not taken.
✗ Branch 34 not taken.
✓ Branch 37 taken 12742 times.
✗ Branch 38 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
12742 points.push_back({});
59
2/44
✗ Branch 1 not taken.
✗ 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.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 52 not taken.
✗ Branch 53 not taken.
✓ Branch 55 taken 12742 times.
✗ Branch 56 not taken.
✓ Branch 58 taken 12742 times.
✗ Branch 59 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
89194 std::ranges::for_each(std::views::iota(std::size_t{0}, in_dim), [&] (auto dim) {
60 38226 points.back().at(dim) = static_cast<double>(values[p_idx*in_dim + dim]);
61 });
62 });
63 162 });
64 81 }
65
66 81 void _make_cells() {
67 81 cells.reserve(reader.number_of_cells());
68
1/2
✓ Branch 2 taken 81 times.
✗ Branch 3 not taken.
81 reader.visit_cells([&] (CellType ct, std::vector<std::size_t> corners) {
69
1/2
✓ Branch 4 taken 10380 times.
✗ Branch 5 not taken.
10380 cells.emplace_back(std::pair{std::move(ct), std::move(corners)});
70 10380 });
71
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 81 times.
81 if (cells.size() != reader.number_of_cells())
72 throw SizeError("Mismatch between stored and defined number of cells.");
73 81 }
74 };
75
76 template<typename T>
77 concept Writer
78 = requires { typename std::remove_cvref_t<T>::Grid; }
79 and std::derived_from<std::remove_cvref_t<T>, GridWriterBase<typename T::Grid>>;
80
81 template<typename T>
82 concept PieceWriter = Writer<T> and std::derived_from<std::remove_cvref_t<T>, GridWriter<typename T::Grid>>;
83
84 template<typename T>
85 concept TimeSeriesWriter = Writer<T> and std::derived_from<std::remove_cvref_t<T>, TimeSeriesGridWriter<typename T::Grid>>;
86
87 template<typename T>
88 concept PieceWriterFactory = requires (const T& factory, const ConverterGrid& grid) {
89 { factory(grid) } -> PieceWriter;
90 };
91
92 template<typename T>
93 concept TimeSeriesWriterFactory = requires (const T& factory, const ConverterGrid& grid, const std::string& filename) {
94 { factory(grid) } -> TimeSeriesWriter;
95 };
96
97 template<typename T>
98 concept WriterFactory = PieceWriterFactory<T> or TimeSeriesWriterFactory<T>;
99
100 template<typename Reader, Writer Writer>
101 166 void add_piece_fields(const Reader& reader, Writer& writer) {
102 166 writer.clear();
103
6/10
✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 83 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 83 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 117 times.
✗ Branch 11 not taken.
✓ Branch 16 taken 117 times.
✓ Branch 17 taken 83 times.
400 for (auto [name, field_ptr] : cell_fields(reader))
104
1/2
✓ Branch 4 taken 117 times.
✗ Branch 5 not taken.
234 writer.set_cell_field(std::move(name), std::move(field_ptr));
105
6/10
✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 83 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 83 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 186 times.
✗ Branch 11 not taken.
✓ Branch 16 taken 186 times.
✓ Branch 17 taken 83 times.
538 for (auto [name, field_ptr] : point_fields(reader))
106
1/2
✓ Branch 4 taken 186 times.
✗ Branch 5 not taken.
372 writer.set_point_field(std::move(name), std::move(field_ptr));
107
4/10
✓ Branch 1 taken 83 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 83 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 83 times.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 16 not taken.
✓ Branch 17 taken 83 times.
166 for (auto [name, field_ptr] : meta_data_fields(reader))
108 writer.set_meta_data(std::move(name), std::move(field_ptr));
109 166 }
110
111 template<typename Reader, PieceWriter Writer>
112 44 std::string write_piece(const Reader& reader, Writer& writer, const std::string& filename) {
113 44 add_piece_fields(reader, writer);
114 44 return writer.write(filename);
115 }
116
117 template<typename Reader, TimeSeriesWriter Writer>
118 105 std::string write_piece(const Reader& reader, Writer& writer, double time_step) {
119 105 add_piece_fields(reader, writer);
120 105 return writer.write(time_step);
121 }
122
123 } // namespace ConverterDetail
124 #endif // DOXYGEN
125
126 /*!
127 * \ingroup Grid
128 * \brief Convert between grid formats.
129 * \param reader A grid reader on which a file was opened.
130 * \param factory A factory to construct a writer with the desired output format.
131 * \param filename The name of the file to be written.
132 */
133 template<std::derived_from<GridReader> Reader, ConverterDetail::WriterFactory Factory>
134 44 std::string convert(const Reader& reader, const std::string& filename, const Factory& factory) {
135 44 ConverterDetail::ConverterGrid grid{reader};
136
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
44 auto writer = factory(grid);
137
2/4
✓ Branch 2 taken 23 times.
✗ Branch 3 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 23 times.
44 if (reader.filename() == filename + writer.extension())
138 throw GridFormat::IOError("Cannot read/write from/to the same file");
139 if constexpr (Traits::WritesConnectivity<std::remove_cvref_t<decltype(writer)>>::value)
140
1/2
✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
40 grid.make_grid();
141
1/2
✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
88 return ConverterDetail::write_piece(reader, writer, filename);
142 44 }
143
144 /*!
145 * \ingroup Grid
146 * \brief Overload for time series formats.
147 * \param reader A grid reader on which a file was opened.
148 * \param factory A factory to construct a time series writer with the desired output format.
149 * \param call_back (optional) A callback that is invoked after writing each step.
150 */
151 template<std::derived_from<GridReader> Reader,
152 ConverterDetail::TimeSeriesWriterFactory Factory,
153 15 std::invocable<std::size_t, const std::string&> StepCallBack = decltype([] (std::size_t, const std::string&) {})>
154 21 std::string convert(Reader& reader,
155 const Factory& factory,
156 const StepCallBack& call_back = {}) {
157
2/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
21 if (!reader.is_sequence())
158 throw ValueError("Cannot convert data from reader to a sequence as the file read is no sequence.");
159
160 21 ConverterDetail::ConverterGrid grid{reader};
161
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
21 auto writer = factory(grid);
162 21 std::string filename;
163
3/4
✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 60 times.
✓ Branch 4 taken 12 times.
126 for (std::size_t step = 0; step < reader.number_of_steps(); ++step) {
164
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
105 reader.set_step(step);
165 if constexpr (Traits::WritesConnectivity<std::remove_cvref_t<decltype(writer)>>::value)
166
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
105 grid.make_grid();
167
2/4
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 60 times.
✗ Branch 5 not taken.
105 filename = ConverterDetail::write_piece(reader, writer, reader.time_at_step(step));
168
1/2
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
105 call_back(step, filename);
169 }
170 21 return filename;
171 21 }
172
173 namespace Traits {
174
175 // to distinguish points/cells we use different integer types
176
177 template<>
178 struct Points<ConverterDetail::ConverterGrid> {
179 162 static std::ranges::range auto get(const ConverterDetail::ConverterGrid& grid) {
180
2/4
✓ Branch 1 taken 162 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 162 times.
✗ Branch 5 not taken.
162 return std::views::iota(std::size_t{0}, grid.reader.number_of_points());
181 }
182 };
183
184 template<>
185 struct Cells<ConverterDetail::ConverterGrid> {
186 243 static std::ranges::range auto get(const ConverterDetail::ConverterGrid& grid) {
187
1/2
✓ Branch 1 taken 243 times.
✗ Branch 2 not taken.
243 const auto max = static_cast<std::int64_t>(grid.reader.number_of_cells());
188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 243 times.
243 if (max < 0)
189 throw TypeError("Integer overflow. Too many grid cells.");
190
1/2
✓ Branch 1 taken 243 times.
✗ Branch 2 not taken.
243 return std::views::iota(std::int64_t{0}, max);
191 }
192 };
193
194 template<>
195 struct NumberOfPoints<ConverterDetail::ConverterGrid> {
196 411 static std::size_t get(const ConverterDetail::ConverterGrid& grid) {
197 411 return grid.reader.number_of_points();
198 }
199 };
200
201 template<>
202 struct NumberOfCells<ConverterDetail::ConverterGrid> {
203 413 static std::size_t get(const ConverterDetail::ConverterGrid& grid) {
204 413 return grid.reader.number_of_cells();
205 }
206 };
207
208 template<>
209 struct CellPoints<ConverterDetail::ConverterGrid, std::int64_t> {
210 10380 static std::ranges::range auto get(const ConverterDetail::ConverterGrid& grid, const std::int64_t i) {
211 10380 return grid.cells.at(i).second | std::views::all;
212 }
213 };
214
215 template<>
216 struct CellType<ConverterDetail::ConverterGrid, std::int64_t> {
217 10380 static GridFormat::CellType get(const ConverterDetail::ConverterGrid& grid, const std::int64_t i) {
218 10380 return grid.cells.at(i).first;
219 }
220 };
221
222 template<>
223 struct PointCoordinates<ConverterDetail::ConverterGrid, std::size_t> {
224 12742 static std::array<double, 3> get(const ConverterDetail::ConverterGrid& grid, const std::size_t i) {
225 12742 return grid.points.at(i);
226 }
227 };
228
229 template<>
230 struct PointId<ConverterDetail::ConverterGrid, std::size_t> {
231 70364 static std::size_t get(const ConverterDetail::ConverterGrid&, const std::size_t i) {
232 70364 return i;
233 }
234 };
235
236 template<>
237 struct NumberOfCellPoints<ConverterDetail::ConverterGrid, std::int64_t> {
238 20760 static std::size_t get(const ConverterDetail::ConverterGrid& grid, const std::int64_t i) {
239 20760 return grid.cells.at(i).second.size();
240 }
241 };
242
243 template<>
244 struct Origin<ConverterDetail::ConverterGrid> {
245 2 static std::array<double, 3> get(const ConverterDetail::ConverterGrid& grid) {
246 2 return grid.reader.origin();
247 }
248 };
249
250 template<>
251 struct Spacing<ConverterDetail::ConverterGrid> {
252 2 static std::array<double, 3> get(const ConverterDetail::ConverterGrid& grid) {
253 2 return grid.reader.spacing();
254 }
255 };
256
257 template<>
258 struct Basis<ConverterDetail::ConverterGrid> {
259 2 static std::array<std::array<double, 3>, 3> get(const ConverterDetail::ConverterGrid& grid) {
260 std::array<std::array<double, 3>, 3> result;
261
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 for (unsigned int i = 0; i < 3; ++i)
262 6 result[i] = grid.reader.basis_vector(i);
263 2 return result;
264 }
265 };
266
267 template<>
268 struct Extents<ConverterDetail::ConverterGrid> {
269 3 static std::array<std::size_t, 3> get(const ConverterDetail::ConverterGrid& grid) {
270 3 return grid.reader.extents();
271 }
272 };
273
274 template<>
275 struct Ordinates<ConverterDetail::ConverterGrid> {
276 static std::vector<double> get(const ConverterDetail::ConverterGrid& grid, unsigned int i) {
277 return grid.reader.ordinates(i);
278 }
279 };
280
281 template<typename Entity>
282 struct Location<ConverterDetail::ConverterGrid, Entity> {
283 static std::array<std::size_t, 3> get(const ConverterDetail::ConverterGrid& grid, const std::size_t point) {
284 return _get(Ranges::incremented(Extents<ConverterDetail::ConverterGrid>::get(grid), 1), point);
285 }
286
287 static std::array<std::size_t, 3> get(const ConverterDetail::ConverterGrid& grid, const std::int64_t cell) {
288 return _get(Extents<ConverterDetail::ConverterGrid>::get(grid), cell);
289 }
290
291 private:
292 static std::array<std::size_t, 3> _get(std::ranges::range auto extents, std::integral auto index) {
293 // avoid zero extents
294 std::ranges::for_each(extents, [] <std::integral T> (T& e) { e = std::max(e, T{1}); });
295 const auto accumulate_until = [&] (int dim) {
296 auto range = extents | std::views::take(dim);
297 return std::accumulate(
298 std::ranges::begin(range),
299 std::ranges::end(range),
300 std::size_t{1},
301 std::multiplies{}
302 );
303 };
304
305 const auto divisor1 = accumulate_until(1);
306 const auto divisor2 = accumulate_until(2);
307 return {
308 index%divisor2%divisor1,
309 index%divisor2/divisor1,
310 index/divisor2
311 };
312 }
313 };
314
315 } // namespace Traits
316 } // namespace GridFormat
317
318 #endif // GRIDFORMAT_GRID_CONVERTER_HPP_
319