GCC Code Coverage Report


Directory: gridformat/
File: gridformat/common/field_transformations.hpp
Date: 2024-11-20 14:41:59
Exec Total Coverage
Lines: 281 290 96.9%
Functions: 84 102 82.4%
Branches: 229 428 53.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 Common
6 * \brief Transformed field implementations
7 */
8 #ifndef GRIDFORMAT_COMMON_FIELD_TRANSFORMATIONS_HPP_
9 #define GRIDFORMAT_COMMON_FIELD_TRANSFORMATIONS_HPP_
10
11 #include <vector>
12 #include <utility>
13 #include <concepts>
14 #include <algorithm>
15 #include <type_traits>
16 #include <sstream>
17 #include <ranges>
18
19 #include <gridformat/common/field.hpp>
20 #include <gridformat/common/serialization.hpp>
21 #include <gridformat/common/string_conversion.hpp>
22 #include <gridformat/common/exceptions.hpp>
23 #include <gridformat/common/precision.hpp>
24 #include <gridformat/common/md_index.hpp>
25 #include <gridformat/common/iterator_facades.hpp>
26 #include <gridformat/common/flat_index_mapper.hpp>
27
28 namespace GridFormat {
29
30 #ifndef DOXYGEN
31 namespace FieldTransformationDetail {
32
33 // walks along md indices in the source & target layouts
34 // and exposes the corresponding flat indices within the
35 // layouts according to row-major-ordering (we store data this way)
36 class BackwardsMDIndexMapWalk {
37 public:
38 template<std::convertible_to<MDLayout> _L1,
39 std::convertible_to<MDLayout> _L2>
40 17621 BackwardsMDIndexMapWalk(_L1&& source_layout,
41 _L2&& target_layout)
42 17621 : _source_layout{std::forward<_L1>(source_layout)}
43
1/2
✓ Branch 2 taken 17621 times.
✗ Branch 3 not taken.
17621 , _target_layout{std::forward<_L2>(target_layout)} {
44
3/6
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17621 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 17621 times.
17621 if (_source_layout.dimension() != _target_layout.dimension())
45 throw InvalidState("Source and target layout dimensions mismatch");
46
1/2
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
17621 if (std::ranges::any_of(
47
3/6
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17621 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 17621 times.
35242 std::views::iota(std::size_t{0}, _source_layout.dimension()),
48 44169 [&] (const std::size_t i) {
49 44169 return _source_layout.extent(i) > _target_layout.extent(i);
50 }
51 ))
52 throw InvalidState("Only mapping into larger layouts supported");
53
54
1/2
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
17621 _compute_target_offsets();
55
2/4
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17621 times.
✗ Branch 5 not taken.
17621 _current = _make_end_index(_source_layout);
56
1/2
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
17621 _current_flat = flat_index(_current, _source_layout);
57
1/2
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
17621 _current_target_flat = flat_index(_current, _target_layout);
58 17621 }
59
60 785366 void next() {
61 785366 _decrement();
62 785366 }
63
64 802987 bool is_finished() const {
65
1/2
✓ Branch 1 taken 802987 times.
✗ Branch 2 not taken.
802987 return std::ranges::any_of(
66
2/4
✓ Branch 1 taken 802987 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 802987 times.
✗ Branch 5 not taken.
1605974 std::views::iota(std::size_t{0}, _source_layout.dimension()),
67 2331939 [&] (const std::size_t i) {
68 2331939 return _current.get(i) >= _source_layout.extent(i);
69 }
70 1605974 );
71 }
72
73 const MDIndex& current() const {
74 return _current;
75 }
76
77 1570732 std::size_t source_index_flat() const {
78 1570732 return _current_flat;
79 }
80
81 1570732 std::size_t target_index_flat() const {
82 1570732 return _current_target_flat;
83 }
84
85 private:
86 785366 void _decrement() {
87 785366 _decrement(_source_layout.dimension() - 1);
88 785366 }
89
90 1184261 void _decrement(std::size_t i) {
91
2/2
✓ Branch 1 taken 416516 times.
✓ Branch 2 taken 767745 times.
1184261 if (_current.get(i) == 0) {
92 416516 _current.set(i, _source_layout.extent(i) - 1);
93
2/2
✓ Branch 0 taken 398895 times.
✓ Branch 1 taken 17621 times.
416516 if (i > 0)
94 398895 _decrement(i-1);
95 else {
96
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17621 times.
17621 assert(i == 0);
97 17621 _current.set(i, _source_layout.extent(i));
98 }
99 } else {
100 767745 _current.set(i, _current.get(i) - 1);
101 767745 _current_flat--;
102 767745 _current_target_flat--;
103 767745 _current_target_flat -= _target_offsets[i];
104 }
105 1184261 }
106
107 MDIndex _make_begin_index(const MDLayout& layout) const {
108 return MDIndex{
109 std::views::iota(std::size_t{0}, layout.dimension())
110 | std::views::transform([&] (const std::size_t&) {
111 return 0;
112 })
113 };
114 }
115
116 17621 MDIndex _make_end_index(const MDLayout& layout) const {
117 return MDIndex{
118
2/4
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17621 times.
✗ Branch 5 not taken.
17621 std::views::iota(std::size_t{0}, layout.dimension())
119
1/2
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
35242 | std::views::transform([&] (const std::size_t i) {
120 44169 return layout.extent(i) - 1;
121
1/2
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
17621 })
122
1/2
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
35242 };
123 }
124
125 17621 void _compute_target_offsets() {
126 17621 _target_offsets.reserve(_source_layout.dimension());
127
2/2
✓ Branch 1 taken 26548 times.
✓ Branch 2 taken 17621 times.
44169 for (std::size_t i = 1; i < _source_layout.dimension(); ++i)
128 53096 _target_offsets.push_back(
129
1/2
✓ Branch 1 taken 26548 times.
✗ Branch 2 not taken.
26548 _target_layout.number_of_entries(i)
130
2/4
✓ Branch 1 taken 26548 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 26548 times.
✗ Branch 5 not taken.
26548 - _source_layout.number_of_entries(i)
131 );
132
1/2
✓ Branch 1 taken 17621 times.
✗ Branch 2 not taken.
17621 _target_offsets.push_back(0);
133
2/2
✓ Branch 1 taken 26548 times.
✓ Branch 2 taken 17621 times.
44169 for (std::size_t i = 0; i < _target_offsets.size() - 1; ++i)
134 26548 _target_offsets[i] -= (_source_layout.extent(i+1) - 1)*_target_offsets[i+1];
135 17621 }
136
137 MDLayout _source_layout;
138 MDLayout _target_layout;
139 std::vector<std::size_t> _target_offsets;
140
141 MDIndex _current;
142 std::size_t _current_flat;
143 std::size_t _current_target_flat;
144 };
145
146 } // namespace FieldTransformationDetail
147 #endif // DOXYGEN
148
149
150 /*!
151 * \ingroup Common
152 * \brief Wraps an underlying field by an identity transformation
153 */
154 class IdentityField : public Field {
155 public:
156 3 explicit IdentityField(FieldPtr field)
157 3 : _field(field)
158 3 {}
159
160 private:
161 FieldPtr _field;
162
163 11 MDLayout _layout() const override {
164 11 return _field->layout();
165 }
166
167 14 DynamicPrecision _precision() const override {
168 14 return _field->precision();
169 }
170
171 2 Serialization _serialized() const override {
172 2 return _field->serialized();
173 }
174 };
175
176 /*!
177 * \ingroup Common
178 * \brief Exposes a flat view on a given field
179 */
180 class FlattenedField : public Field {
181 public:
182 25 explicit FlattenedField(FieldPtr ptr)
183 25 : _field{ptr}
184 25 {}
185
186 private:
187 FieldPtr _field;
188
189 28 MDLayout _layout() const override {
190
3/6
✓ Branch 2 taken 28 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 28 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 28 times.
✗ Branch 9 not taken.
28 return MDLayout{{_field->layout().number_of_entries()}};
191 }
192
193 29 DynamicPrecision _precision() const override {
194 29 return _field->precision();
195 }
196
197 25 Serialization _serialized() const override {
198 25 return _field->serialized();
199 }
200 };
201
202 /*!
203 * \ingroup Common
204 * \brief Exposes a given field with a different layout.
205 * \note Requires the target layout to have the same number of entries as the original layout.
206 */
207 class ReshapedField : public Field {
208 public:
209 3359 explicit ReshapedField(FieldPtr field, MDLayout target_layout)
210 6718 : _field(field)
211
1/2
✓ Branch 4 taken 3359 times.
✗ Branch 5 not taken.
3359 , _target_layout{std::move(target_layout)} {
212
5/8
✓ Branch 2 taken 3359 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3359 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3359 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 1 times.
✓ Branch 12 taken 3358 times.
3359 if (_field->layout().number_of_entries() != _target_layout.number_of_entries()) {
213
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 std::ostringstream in;
214
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 std::ostringstream target;
215
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 in << _field->layout();
216
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 target << _target_layout;
217
6/12
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 1 times.
✗ Branch 12 not taken.
✓ Branch 14 taken 1 times.
✗ Branch 15 not taken.
✓ Branch 18 taken 1 times.
✗ Branch 19 not taken.
1 throw SizeError("Cannot reshape field with layout " + in.str() + " to " + target.str());
218 2 }
219 3361 }
220
221 private:
222 FieldPtr _field;
223 MDLayout _target_layout;
224
225 13443 MDLayout _layout() const override {
226 13443 return _target_layout;
227 }
228
229 10474 DynamicPrecision _precision() const override {
230 10474 return _field->precision();
231 }
232
233 3358 Serialization _serialized() const override {
234 3358 return _field->serialized();
235 }
236 };
237
238 /*!
239 * \ingroup Common
240 * \brief Extends a given field with zeros up to the given extents
241 * \note This takes the extents of the desired sub-layout as constructor
242 * argument. The extent of the first dimension stays the same.
243 */
244 class ExtendedField : public Field {
245 public:
246 16608 explicit ExtendedField(FieldPtr f, MDLayout target_sub_layout)
247 33216 : _field{f}
248
1/2
✓ Branch 4 taken 16608 times.
✗ Branch 5 not taken.
16608 , _target_sub_layout{std::move(target_sub_layout)}
249 16608 {}
250
251 private:
252 FieldPtr _field;
253 MDLayout _target_sub_layout;
254
255 37271 MDLayout _extended_layout() const {
256
3/4
✓ Branch 2 taken 37271 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 37268 times.
✓ Branch 6 taken 3 times.
37271 return _extended_layout(_field->layout());
257 }
258
259 53787 MDLayout _extended_layout(const MDLayout& orig_layout) const {
260
3/4
✓ Branch 1 taken 53787 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 53786 times.
53787 if (orig_layout.dimension() <= 1)
261
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 throw SizeError("Can only reshape fields with dimension > 1");
262
4/6
✓ Branch 1 taken 53786 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 53786 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 53784 times.
53786 if (orig_layout.dimension() != _target_sub_layout.dimension() + 1)
263
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 throw SizeError("Field sub-dimension does not match given target layout dimension");
264
265
3/6
✓ Branch 1 taken 53784 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 53784 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 53784 times.
✗ Branch 8 not taken.
53784 std::vector<std::size_t> extents(orig_layout.dimension(), orig_layout.extent(0));
266
1/2
✓ Branch 2 taken 53784 times.
✗ Branch 3 not taken.
107568 std::ranges::copy(
267
2/4
✓ Branch 1 taken 53784 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 53784 times.
✗ Branch 5 not taken.
53784 std::views::iota(std::size_t{0}, _target_sub_layout.dimension())
268
2/4
✓ Branch 1 taken 53784 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 53784 times.
✗ Branch 5 not taken.
186147 | std::views::transform([&] (auto i) { return _target_sub_layout.extent(i); }),
269 53784 extents.begin() + 1
270 );
271
1/2
✓ Branch 1 taken 53784 times.
✗ Branch 2 not taken.
53784 _check_valid_layout(orig_layout, extents);
272
1/2
✓ Branch 2 taken 53784 times.
✗ Branch 3 not taken.
107568 return MDLayout{std::move(extents)};
273 53784 }
274
275 37271 MDLayout _layout() const override {
276 37271 return _extended_layout();
277 }
278
279 52927 DynamicPrecision _precision() const override {
280 52927 return _field->precision();
281 }
282
283 16516 Serialization _serialized() const override {
284
1/2
✓ Branch 2 taken 16516 times.
✗ Branch 3 not taken.
16516 const auto orig_layout = _field->layout();
285
1/2
✓ Branch 1 taken 16516 times.
✗ Branch 2 not taken.
16516 const auto new_layout = _extended_layout(orig_layout);
286
287
1/2
✓ Branch 2 taken 16516 times.
✗ Branch 3 not taken.
16516 auto serialization = _field->serialized();
288
2/4
✓ Branch 1 taken 16516 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 16516 times.
16516 if (orig_layout == new_layout)
289 return serialization;
290
291 using Walk = FieldTransformationDetail::BackwardsMDIndexMapWalk;
292
1/2
✓ Branch 1 taken 16516 times.
✗ Branch 2 not taken.
16516 Walk index_walk{orig_layout, new_layout};
293
294
2/4
✓ Branch 2 taken 16516 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16516 times.
✗ Branch 6 not taken.
49548 _field->precision().visit([&] <typename T> (const Precision<T>&) {
295
2/4
✓ Branch 1 taken 16516 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16516 times.
✗ Branch 5 not taken.
33032 serialization.resize(new_layout.number_of_entries()*sizeof(T), typename Serialization::Byte{0});
296
1/2
✓ Branch 1 taken 16516 times.
✗ Branch 2 not taken.
33032 const auto data = serialization.template as_span_of<T>();
297
3/4
✓ Branch 1 taken 539324 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 522808 times.
✓ Branch 4 taken 16516 times.
1078648 while (!index_walk.is_finished()) {
298
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 522808 times.
1045616 assert(index_walk.source_index_flat() < data.size());
299
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 522808 times.
1045616 assert(index_walk.target_index_flat() < data.size());
300 1045616 std::swap(
301 1045616 data[index_walk.source_index_flat()],
302 1045616 data[index_walk.target_index_flat()]
303 );
304
1/2
✓ Branch 1 taken 522808 times.
✗ Branch 2 not taken.
1045616 index_walk.next();
305 }
306 33032 });
307 16516 return serialization;
308 16516 }
309
310 53784 void _check_valid_layout(const MDLayout& orig, const std::vector<std::size_t>& extents) const {
311
1/2
✓ Branch 1 taken 53784 times.
✗ Branch 2 not taken.
53784 if (std::ranges::any_of(
312
3/6
✓ Branch 1 taken 53784 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 53784 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 53784 times.
107568 std::views::iota(std::size_t{0}, orig.dimension()),
313 132363 [&] (const std::size_t i) {
314 132363 return orig.extent(i) > extents[i];
315 }
316 ))
317 throw SizeError("Given target extension smaller than original extension.");
318 53784 }
319 };
320
321
322 /*!
323 * \ingroup Common
324 * \brief Exposes a field that represents a slice of another field.
325 */
326 class SlicedField : public Field {
327 public:
328 struct Slice {
329 std::vector<std::size_t> from;
330 std::vector<std::size_t> to;
331 };
332
333 1108 explicit SlicedField(FieldPtr field, Slice slice)
334 2216 : _field{field}
335 1108 , _slice{std::move(slice)}
336 1108 {}
337
338 private:
339 4967 MDLayout _make_slice_layout() const {
340
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 4966 times.
4967 if (_slice.from.size() != _slice.to.size())
341
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 throw SizeError("Slice bounds must have the same dimension");
342
4/6
✓ Branch 3 taken 4966 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 4966 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 2 times.
✓ Branch 10 taken 4964 times.
4966 if (_slice.from.size() != _field->layout().dimension())
343
1/2
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
2 throw SizeError("Slice dimension does not match that of the original field");
344 return MDLayout{
345
1/2
✓ Branch 2 taken 4964 times.
✗ Branch 3 not taken.
4964 std::views::iota(std::size_t{0}, _slice.from.size())
346
1/2
✓ Branch 1 taken 4964 times.
✗ Branch 2 not taken.
9928 | std::views::transform([&] (const std::size_t i) {
347
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 15410 times.
15410 if (_slice.to[i] < _slice.from[i])
348 throw SizeError("slice.from must be smaller than slice.to");
349 15410 return _slice.to[i] - _slice.from[i];
350
1/2
✓ Branch 1 taken 4964 times.
✗ Branch 2 not taken.
4964 })
351
1/2
✓ Branch 1 taken 4964 times.
✗ Branch 2 not taken.
9928 };
352 }
353
354 4967 MDLayout _layout() const override {
355 4967 return _make_slice_layout();
356 }
357
358 1105 Serialization _serialized() const override {
359
1/2
✓ Branch 2 taken 1105 times.
✗ Branch 3 not taken.
1105 const auto in_layout = _field->layout();
360
1/2
✓ Branch 1 taken 1105 times.
✗ Branch 2 not taken.
1105 const auto out_layout = _layout();
361
1/2
✓ Branch 1 taken 1105 times.
✗ Branch 2 not taken.
1105 const auto precision = _precision();
362
1/2
✓ Branch 2 taken 1105 times.
✗ Branch 3 not taken.
1105 Serialization in_serialization = _field->serialized();
363
3/6
✓ Branch 1 taken 1105 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1105 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1105 times.
✗ Branch 8 not taken.
1105 Serialization out_serialization(out_layout.number_of_entries()*precision.size_in_bytes());
364
365 // data is stored row-major, so we reverse the layout here
366
3/6
✓ Branch 1 taken 1105 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1105 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1105 times.
✗ Branch 8 not taken.
2210 const auto in_offset = FlatIndexMapper{in_layout | std::views::reverse}.map(
367
1/2
✓ Branch 1 taken 1105 times.
✗ Branch 2 not taken.
1105 _slice.from | std::views::reverse
368 1105 );
369
370
1/2
✓ Branch 1 taken 1105 times.
✗ Branch 2 not taken.
1105 auto index_walk = FieldTransformationDetail::BackwardsMDIndexMapWalk{out_layout, in_layout};
371
1/2
✓ Branch 1 taken 1105 times.
✗ Branch 2 not taken.
3315 precision.visit([&] <typename T> (const Precision<T>&) {
372
1/2
✓ Branch 1 taken 1105 times.
✗ Branch 2 not taken.
2210 auto in = in_serialization.template as_span_of<T>();
373
1/2
✓ Branch 1 taken 1105 times.
✗ Branch 2 not taken.
2210 auto out = out_serialization.template as_span_of<T>();
374
3/4
✓ Branch 1 taken 263663 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 262558 times.
✓ Branch 4 taken 1105 times.
527326 while (!index_walk.is_finished()) {
375
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 262558 times.
525116 assert(index_walk.source_index_flat() < out.size());
376
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 262558 times.
525116 assert(index_walk.target_index_flat() + in_offset < in.size());
377 525116 out[index_walk.source_index_flat()] = in[index_walk.target_index_flat() + in_offset];
378
1/2
✓ Branch 1 taken 262558 times.
✗ Branch 2 not taken.
525116 index_walk.next();
379 }
380 2210 });
381
382 1105 return out_serialization;
383 1105 }
384
385 3860 DynamicPrecision _precision() const override {
386 3860 return _field->precision();
387 }
388
389 FieldPtr _field;
390 Slice _slice;
391 };
392
393 /*!
394 * \ingroup Common
395 * \brief Exposes a field that is the result of merging the given fields.
396 */
397 class MergedField : public Field {
398 public:
399 template<typename... Ptrs>
400 requires(std::same_as<Ptrs, FieldPtr> && ...)
401 12 explicit MergedField(FieldPtr first, Ptrs&&... fields)
402
4/6
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 2 times.
12 : MergedField(_as_vector(first, std::forward<Ptrs>(fields)...))
403 6 {}
404
405 167 explicit MergedField(std::vector<FieldPtr>&& fields)
406 167 : _fields{std::move(fields)} {
407
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 167 times.
167 if (_fields.empty())
408 throw ValueError("Need at least one field for merging");
409
4/6
✓ Branch 3 taken 167 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 165 times.
✓ Branch 7 taken 2 times.
✓ Branch 9 taken 165 times.
✗ Branch 10 not taken.
169 _merged_layout = _merge_layouts(_fields.front()->layout());
410
5/8
✓ Branch 1 taken 165 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 165 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 165 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 164 times.
165 if (std::ranges::any_of(_fields | std::views::drop(1), [&] (const FieldPtr& ptr) {
411
2/4
✓ Branch 3 taken 178 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 178 times.
✗ Branch 8 not taken.
178 return ptr->precision() != _fields.front()->precision();
412 }))
413
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 throw ValueError("Can only merge fields with matching precision");
414 173 }
415
416 private:
417 template<typename... Ptrs>
418 12 static std::vector<FieldPtr> _as_vector(Ptrs&&... ptrs) {
419 12 std::vector<FieldPtr> result;
420
2/4
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
12 (result.push_back(ptrs), ...);
421 12 return result;
422 }
423
424 167 MDLayout _merge_layouts(const MDLayout& first_layout) const {
425
3/4
✓ Branch 1 taken 167 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 166 times.
167 if (first_layout.dimension() == 0)
426
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 throw ValueError("Cannot merge layouts with zero dimension");
427
428
2/4
✓ Branch 1 taken 166 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 166 times.
✗ Branch 5 not taken.
166 std::vector<std::size_t> merged_layout(first_layout.dimension());
429
1/2
✓ Branch 1 taken 166 times.
✗ Branch 2 not taken.
166 first_layout.export_to(merged_layout);
430
4/6
✓ Branch 1 taken 166 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 166 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 165 times.
✓ Branch 8 taken 1 times.
166 std::ranges::for_each(_fields | std::views::drop(1), [&] (const FieldPtr& ptr) {
431
1/2
✓ Branch 2 taken 179 times.
✗ Branch 3 not taken.
179 MDLayout layout = ptr->layout();
432
3/4
✓ Branch 1 taken 179 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 178 times.
179 if (!_are_compatible(first_layout, layout))
433 throw ValueError(
434 "Fields to be merged have incompatible layouts: ("
435
3/6
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
2 + as_string(first_layout) + ") and ("
436
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
4 + as_string(layout) + ")."
437
1/2
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
3 );
438
2/4
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 178 times.
✗ Branch 5 not taken.
178 merged_layout.at(0) += layout.extent(0);
439 179 });
440
1/2
✓ Branch 1 taken 165 times.
✗ Branch 2 not taken.
330 return MDLayout{merged_layout};
441 166 }
442
443 179 bool _are_compatible(const MDLayout& first, const MDLayout& second) const {
444
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 178 times.
179 if (first.dimension() != second.dimension())
445 1 return false;
446
5/10
✓ Branch 1 taken 178 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 178 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 178 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 178 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 178 times.
✗ Branch 14 not taken.
178 return std::ranges::equal(first | std::views::drop(1), second | std::views::drop(1));
447 }
448
449 344 MDLayout _layout() const override {
450 344 return _merged_layout;
451 }
452
453 164 Serialization _serialized() const override {
454 164 Serialization result = _fields.front()->serialized();
455
3/6
✓ Branch 1 taken 164 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 164 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 164 times.
✗ Branch 8 not taken.
164 std::ranges::for_each(_fields | std::views::drop(1), [&] (const FieldPtr& ptr) {
456
2/4
✓ Branch 2 taken 177 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 177 times.
✗ Branch 7 not taken.
177 result.push_back(ptr->serialized().data());
457 177 });
458 164 return result;
459 }
460
461 325 DynamicPrecision _precision() const override {
462 325 return _fields.front()->precision();
463 }
464
465 std::vector<FieldPtr> _fields;
466 MDLayout _merged_layout;
467 };
468
469 namespace FieldTransformation {
470
471 #ifndef DOXYGEN
472 namespace Detail {
473
474 struct IdentityFieldAdapter {
475 3 auto operator()(FieldPtr f) const {
476
1/2
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
3 return make_field_ptr(IdentityField{f});
477 }
478 };
479
480 struct FlattenedFieldAdapter {
481 1 auto operator()(FieldPtr f) const {
482
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 return make_field_ptr(FlattenedField{f});
483 }
484 };
485
486 class ExtendFieldAdapter {
487 public:
488 5 explicit ExtendFieldAdapter(MDLayout sub_layout)
489 5 : _sub_layout{std::move(sub_layout)}
490 5 {}
491
492 5 auto operator()(FieldPtr f) const {
493
4/6
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✓ Branch 9 taken 4 times.
5 if (f->layout().dimension() <= 1)
494
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 throw SizeError("Extension only works for fields with dimension > 1");
495
3/6
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 4 times.
✗ Branch 9 not taken.
4 return make_field_ptr(ExtendedField{f, _sub_layout});
496 }
497
498 private:
499 MDLayout _sub_layout;
500 };
501
502 struct ExtendFieldAdapterClosure {
503 5 auto operator()(MDLayout sub_layout) const {
504
2/4
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5 times.
✗ Branch 6 not taken.
5 return ExtendFieldAdapter{std::move(sub_layout)};
505 }
506 };
507
508 class ExtendAllFieldAdapter {
509 public:
510 16602 explicit ExtendAllFieldAdapter(std::size_t space_dimension)
511 16602 : _space_dim{space_dimension}
512 16602 {}
513
514 16602 auto operator()(FieldPtr f) const {
515
2/4
✓ Branch 2 taken 16602 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16602 times.
✗ Branch 6 not taken.
16602 const std::size_t dim = f->layout().dimension();
516
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16602 times.
16602 if (dim <= 1)
517 throw SizeError("Extension only works for fields with dimension > 1");
518 16602 return make_field_ptr(ExtendedField{f, MDLayout{
519
1/2
✓ Branch 1 taken 16602 times.
✗ Branch 2 not taken.
16602 std::views::iota(std::size_t{1}, dim)
520
2/4
✓ Branch 1 taken 16602 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 16602 times.
✗ Branch 5 not taken.
57485 | std::views::transform([&] (const auto&) { return _space_dim; })
521
3/6
✓ Branch 2 taken 16602 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 16602 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 16602 times.
✗ Branch 9 not taken.
49806 }});
522 }
523
524 private:
525 std::size_t _space_dim;
526 };
527
528 struct ExtendAllFieldAdapterClosure {
529 16602 auto operator()(std::size_t space_dimension) const {
530 16602 return ExtendAllFieldAdapter{space_dimension};
531 }
532 };
533
534 class ReshapedFieldAdapter {
535 public:
536 3005 explicit ReshapedFieldAdapter(MDLayout layout)
537 3005 : _layout{std::move(layout)}
538 3005 {}
539
540 3005 auto operator()(FieldPtr f) const {
541
3/6
✓ Branch 2 taken 3005 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3005 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3005 times.
✗ Branch 9 not taken.
3005 return make_field_ptr(ReshapedField{f, _layout});
542 }
543
544 private:
545 MDLayout _layout;
546 };
547
548 struct ReshapedFieldAdapterClosure {
549 2060 auto operator()(MDLayout sub_layout) const {
550
2/4
✓ Branch 2 taken 2060 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 2060 times.
✗ Branch 6 not taken.
2060 return ReshapedFieldAdapter{std::move(sub_layout)};
551 }
552 };
553
554 struct SubFieldAdapter {
555 945 auto operator()(FieldPtr f) const {
556
1/2
✓ Branch 2 taken 945 times.
✗ Branch 3 not taken.
945 const auto layout = f->layout();
557
2/4
✓ Branch 1 taken 945 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 945 times.
✗ Branch 5 not taken.
945 std::vector<std::size_t> new_layout(layout.dimension() + 1, 0);
558
3/6
✓ Branch 1 taken 945 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 945 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 945 times.
✗ Branch 8 not taken.
945 layout.export_to(new_layout | std::views::drop(1));
559
1/2
✓ Branch 1 taken 945 times.
✗ Branch 2 not taken.
945 new_layout.at(0) = 1;
560
3/6
✓ Branch 2 taken 945 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 945 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 945 times.
✗ Branch 10 not taken.
1890 return ReshapedFieldAdapter{MDLayout{std::move(new_layout)}}(f);
561 945 }
562 };
563
564 class SlicedFieldAdapter {
565 public:
566 1104 explicit SlicedFieldAdapter(typename SlicedField::Slice slice)
567 1104 : _slice{std::move(slice)}
568 1104 {}
569
570 1104 auto operator()(FieldPtr f) const {
571
2/4
✓ Branch 2 taken 1104 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1104 times.
✗ Branch 7 not taken.
1104 return make_field_ptr(SlicedField{f, _slice});
572 }
573
574 private:
575 typename SlicedField::Slice _slice;
576 };
577
578 struct SlicedFieldAdapterClosure {
579 1104 auto operator()(typename SlicedField::Slice slice) const {
580 1104 return SlicedFieldAdapter{std::move(slice)};
581 }
582 };
583
584 } // namespace Detail
585 #endif // DOXYGEN
586
587 inline constexpr Detail::IdentityFieldAdapter identity;
588 inline constexpr Detail::FlattenedFieldAdapter flatten;
589 inline constexpr Detail::ExtendFieldAdapterClosure extend_to;
590 inline constexpr Detail::ExtendAllFieldAdapterClosure extend_all_to;
591 inline constexpr Detail::ReshapedFieldAdapterClosure reshape_to;
592 inline constexpr Detail::SubFieldAdapter as_sub_field;
593 inline constexpr Detail::SlicedFieldAdapterClosure take_slice;
594
595 } // namespace FieldTransformation
596
597
598 namespace Concepts {
599
600 template<typename T>
601 concept FieldTransformation
602 = std::invocable<T, FieldPtr> and requires(const T& t, FieldPtr f) {
603 { t(f) } -> std::convertible_to<FieldPtr>;
604 };
605
606 } // namespace Concepts
607
608
609 template<Concepts::FieldTransformation T>
610 24864 FieldPtr transform(FieldPtr field, T&& trafo) {
611
1/2
✓ Branch 2 taken 19225 times.
✗ Branch 3 not taken.
24864 return trafo(field);
612 }
613
614
615 class TransformedField : public Field {
616 public:
617 template<Concepts::FieldTransformation T>
618 271 TransformedField(FieldPtr field, T&& trafo)
619
2/2
✓ Branch 3 taken 262 times.
✓ Branch 4 taken 1 times.
273 : _transformed{trafo(field)}
620 271 {}
621
622 private:
623 FieldPtr _transformed;
624
625 959 MDLayout _layout() const override {
626 959 return _transformed->layout();
627 }
628
629 673 DynamicPrecision _precision() const override {
630 673 return _transformed->precision();
631 }
632
633 259 Serialization _serialized() const override {
634 259 return _transformed->serialized();
635 }
636 };
637
638 } // namespace GridFormat
639
640 #endif // GRIDFORMAT_COMMON_FIELD_TRANSFORMATIONS_HPP_
641