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 |