| 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 | 18361 | BackwardsMDIndexMapWalk(_L1&& source_layout, | |
| 41 | _L2&& target_layout) | ||
| 42 | 18361 | : _source_layout{std::forward<_L1>(source_layout)} | |
| 43 |
1/2✓ Branch 2 taken 18361 times.
✗ Branch 3 not taken.
|
18361 | , _target_layout{std::forward<_L2>(target_layout)} { |
| 44 |
3/6✓ Branch 1 taken 18361 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18361 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 18361 times.
|
18361 | if (_source_layout.dimension() != _target_layout.dimension()) |
| 45 | ✗ | throw InvalidState("Source and target layout dimensions mismatch"); | |
| 46 |
1/2✓ Branch 1 taken 18361 times.
✗ Branch 2 not taken.
|
18361 | if (std::ranges::any_of( |
| 47 |
3/6✓ Branch 1 taken 18361 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18361 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 18361 times.
|
36722 | std::views::iota(std::size_t{0}, _source_layout.dimension()), |
| 48 | 46017 | [&] (const std::size_t i) { | |
| 49 | 46017 | 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 18361 times.
✗ Branch 2 not taken.
|
18361 | _compute_target_offsets(); |
| 55 |
2/4✓ Branch 1 taken 18361 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18361 times.
✗ Branch 5 not taken.
|
18361 | _current = _make_end_index(_source_layout); |
| 56 |
1/2✓ Branch 1 taken 18361 times.
✗ Branch 2 not taken.
|
18361 | _current_flat = flat_index(_current, _source_layout); |
| 57 |
1/2✓ Branch 1 taken 18361 times.
✗ Branch 2 not taken.
|
18361 | _current_target_flat = flat_index(_current, _target_layout); |
| 58 | 18361 | } | |
| 59 | |||
| 60 | 788250 | void next() { | |
| 61 | 788250 | _decrement(); | |
| 62 | 788250 | } | |
| 63 | |||
| 64 | 806611 | bool is_finished() const { | |
| 65 |
1/2✓ Branch 1 taken 806611 times.
✗ Branch 2 not taken.
|
806611 | return std::ranges::any_of( |
| 66 |
2/4✓ Branch 1 taken 806611 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 806611 times.
✗ Branch 5 not taken.
|
1613222 | std::views::iota(std::size_t{0}, _source_layout.dimension()), |
| 67 | 2339943 | [&] (const std::size_t i) { | |
| 68 | 2339943 | return _current.get(i) >= _source_layout.extent(i); | |
| 69 | } | ||
| 70 | 1613222 | ); | |
| 71 | } | ||
| 72 | |||
| 73 | const MDIndex& current() const { | ||
| 74 | return _current; | ||
| 75 | } | ||
| 76 | |||
| 77 | 1576500 | std::size_t source_index_flat() const { | |
| 78 | 1576500 | return _current_flat; | |
| 79 | } | ||
| 80 | |||
| 81 | 1576500 | std::size_t target_index_flat() const { | |
| 82 | 1576500 | return _current_target_flat; | |
| 83 | } | ||
| 84 | |||
| 85 | private: | ||
| 86 | 788250 | void _decrement() { | |
| 87 | 788250 | _decrement(_source_layout.dimension() - 1); | |
| 88 | 788250 | } | |
| 89 | |||
| 90 | 1190851 | void _decrement(std::size_t i) { | |
| 91 |
2/2✓ Branch 1 taken 420962 times.
✓ Branch 2 taken 769889 times.
|
1190851 | if (_current.get(i) == 0) { |
| 92 | 420962 | _current.set(i, _source_layout.extent(i) - 1); | |
| 93 |
2/2✓ Branch 0 taken 402601 times.
✓ Branch 1 taken 18361 times.
|
420962 | if (i > 0) |
| 94 | 402601 | _decrement(i-1); | |
| 95 | else { | ||
| 96 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18361 times.
|
18361 | assert(i == 0); |
| 97 | 18361 | _current.set(i, _source_layout.extent(i)); | |
| 98 | } | ||
| 99 | } else { | ||
| 100 | 769889 | _current.set(i, _current.get(i) - 1); | |
| 101 | 769889 | _current_flat--; | |
| 102 | 769889 | _current_target_flat--; | |
| 103 | 769889 | _current_target_flat -= _target_offsets[i]; | |
| 104 | } | ||
| 105 | 1190851 | } | |
| 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 | 18361 | MDIndex _make_end_index(const MDLayout& layout) const { | |
| 117 | return MDIndex{ | ||
| 118 |
2/4✓ Branch 1 taken 18361 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 18361 times.
✗ Branch 5 not taken.
|
18361 | std::views::iota(std::size_t{0}, layout.dimension()) |
| 119 |
1/2✓ Branch 1 taken 18361 times.
✗ Branch 2 not taken.
|
36722 | | std::views::transform([&] (const std::size_t i) { |
| 120 | 46017 | return layout.extent(i) - 1; | |
| 121 |
1/2✓ Branch 1 taken 18361 times.
✗ Branch 2 not taken.
|
18361 | }) |
| 122 |
1/2✓ Branch 1 taken 18361 times.
✗ Branch 2 not taken.
|
36722 | }; |
| 123 | } | ||
| 124 | |||
| 125 | 18361 | void _compute_target_offsets() { | |
| 126 | 18361 | _target_offsets.reserve(_source_layout.dimension()); | |
| 127 |
2/2✓ Branch 1 taken 27656 times.
✓ Branch 2 taken 18361 times.
|
46017 | for (std::size_t i = 1; i < _source_layout.dimension(); ++i) |
| 128 | 55312 | _target_offsets.push_back( | |
| 129 |
1/2✓ Branch 1 taken 27656 times.
✗ Branch 2 not taken.
|
27656 | _target_layout.number_of_entries(i) |
| 130 |
2/4✓ Branch 1 taken 27656 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 27656 times.
✗ Branch 5 not taken.
|
27656 | - _source_layout.number_of_entries(i) |
| 131 | ); | ||
| 132 |
1/2✓ Branch 1 taken 18361 times.
✗ Branch 2 not taken.
|
18361 | _target_offsets.push_back(0); |
| 133 |
2/2✓ Branch 1 taken 27656 times.
✓ Branch 2 taken 18361 times.
|
46017 | for (std::size_t i = 0; i < _target_offsets.size() - 1; ++i) |
| 134 | 27656 | _target_offsets[i] -= (_source_layout.extent(i+1) - 1)*_target_offsets[i+1]; | |
| 135 | 18361 | } | |
| 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 | 17348 | explicit ExtendedField(FieldPtr f, MDLayout target_sub_layout) | |
| 247 | 34696 | : _field{f} | |
| 248 |
1/2✓ Branch 4 taken 17348 times.
✗ Branch 5 not taken.
|
17348 | , _target_sub_layout{std::move(target_sub_layout)} |
| 249 | 17348 | {} | |
| 250 | |||
| 251 | private: | ||
| 252 | FieldPtr _field; | ||
| 253 | MDLayout _target_sub_layout; | ||
| 254 | |||
| 255 | 38895 | MDLayout _extended_layout() const { | |
| 256 |
3/4✓ Branch 2 taken 38895 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 38892 times.
✓ Branch 6 taken 3 times.
|
38895 | return _extended_layout(_field->layout()); |
| 257 | } | ||
| 258 | |||
| 259 | 56151 | MDLayout _extended_layout(const MDLayout& orig_layout) const { | |
| 260 |
3/4✓ Branch 1 taken 56151 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 56150 times.
|
56151 | 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 56150 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56150 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 56148 times.
|
56150 | 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 56148 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56148 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 56148 times.
✗ Branch 8 not taken.
|
56148 | std::vector<std::size_t> extents(orig_layout.dimension(), orig_layout.extent(0)); |
| 266 |
1/2✓ Branch 2 taken 56148 times.
✗ Branch 3 not taken.
|
112296 | std::ranges::copy( |
| 267 |
2/4✓ Branch 1 taken 56148 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56148 times.
✗ Branch 5 not taken.
|
56148 | std::views::iota(std::size_t{0}, _target_sub_layout.dimension()) |
| 268 |
2/4✓ Branch 1 taken 56148 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56148 times.
✗ Branch 5 not taken.
|
194415 | | std::views::transform([&] (auto i) { return _target_sub_layout.extent(i); }), |
| 269 | 56148 | extents.begin() + 1 | |
| 270 | ); | ||
| 271 |
1/2✓ Branch 1 taken 56148 times.
✗ Branch 2 not taken.
|
56148 | _check_valid_layout(orig_layout, extents); |
| 272 |
1/2✓ Branch 2 taken 56148 times.
✗ Branch 3 not taken.
|
112296 | return MDLayout{std::move(extents)}; |
| 273 | 56148 | } | |
| 274 | |||
| 275 | 38895 | MDLayout _layout() const override { | |
| 276 | 38895 | return _extended_layout(); | |
| 277 | } | ||
| 278 | |||
| 279 | 55290 | DynamicPrecision _precision() const override { | |
| 280 | 55290 | return _field->precision(); | |
| 281 | } | ||
| 282 | |||
| 283 | 17256 | Serialization _serialized() const override { | |
| 284 |
1/2✓ Branch 2 taken 17256 times.
✗ Branch 3 not taken.
|
17256 | const auto orig_layout = _field->layout(); |
| 285 |
1/2✓ Branch 1 taken 17256 times.
✗ Branch 2 not taken.
|
17256 | const auto new_layout = _extended_layout(orig_layout); |
| 286 | |||
| 287 |
1/2✓ Branch 2 taken 17256 times.
✗ Branch 3 not taken.
|
17256 | auto serialization = _field->serialized(); |
| 288 |
2/4✓ Branch 1 taken 17256 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 17256 times.
|
17256 | if (orig_layout == new_layout) |
| 289 | ✗ | return serialization; | |
| 290 | |||
| 291 | using Walk = FieldTransformationDetail::BackwardsMDIndexMapWalk; | ||
| 292 |
1/2✓ Branch 1 taken 17256 times.
✗ Branch 2 not taken.
|
17256 | Walk index_walk{orig_layout, new_layout}; |
| 293 | |||
| 294 |
2/4✓ Branch 2 taken 17256 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 17256 times.
✗ Branch 6 not taken.
|
51768 | _field->precision().visit([&] <typename T> (const Precision<T>&) { |
| 295 |
2/4✓ Branch 1 taken 17256 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17256 times.
✗ Branch 5 not taken.
|
34512 | serialization.resize(new_layout.number_of_entries()*sizeof(T), typename Serialization::Byte{0}); |
| 296 |
1/2✓ Branch 1 taken 17256 times.
✗ Branch 2 not taken.
|
34512 | const auto data = serialization.template as_span_of<T>(); |
| 297 |
3/4✓ Branch 1 taken 542948 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 525692 times.
✓ Branch 4 taken 17256 times.
|
1085896 | while (!index_walk.is_finished()) { |
| 298 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 525692 times.
|
1051384 | assert(index_walk.source_index_flat() < data.size()); |
| 299 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 525692 times.
|
1051384 | assert(index_walk.target_index_flat() < data.size()); |
| 300 | 1051384 | std::swap( | |
| 301 | 1051384 | data[index_walk.source_index_flat()], | |
| 302 | 1051384 | data[index_walk.target_index_flat()] | |
| 303 | ); | ||
| 304 |
1/2✓ Branch 1 taken 525692 times.
✗ Branch 2 not taken.
|
1051384 | index_walk.next(); |
| 305 | } | ||
| 306 | 34512 | }); | |
| 307 | 17256 | return serialization; | |
| 308 | 17256 | } | |
| 309 | |||
| 310 | 56148 | void _check_valid_layout(const MDLayout& orig, const std::vector<std::size_t>& extents) const { | |
| 311 |
1/2✓ Branch 1 taken 56148 times.
✗ Branch 2 not taken.
|
56148 | if (std::ranges::any_of( |
| 312 |
3/6✓ Branch 1 taken 56148 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 56148 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 56148 times.
|
112296 | std::views::iota(std::size_t{0}, orig.dimension()), |
| 313 | 138267 | [&] (const std::size_t i) { | |
| 314 | 138267 | return orig.extent(i) > extents[i]; | |
| 315 | } | ||
| 316 | )) | ||
| 317 | ✗ | throw SizeError("Given target extension smaller than original extension."); | |
| 318 | 56148 | } | |
| 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 | 17342 | explicit ExtendAllFieldAdapter(std::size_t space_dimension) | |
| 511 | 17342 | : _space_dim{space_dimension} | |
| 512 | 17342 | {} | |
| 513 | |||
| 514 | 17342 | auto operator()(FieldPtr f) const { | |
| 515 |
2/4✓ Branch 2 taken 17342 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 17342 times.
✗ Branch 6 not taken.
|
17342 | const std::size_t dim = f->layout().dimension(); |
| 516 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17342 times.
|
17342 | if (dim <= 1) |
| 517 | ✗ | throw SizeError("Extension only works for fields with dimension > 1"); | |
| 518 | 17342 | return make_field_ptr(ExtendedField{f, MDLayout{ | |
| 519 |
1/2✓ Branch 1 taken 17342 times.
✗ Branch 2 not taken.
|
17342 | std::views::iota(std::size_t{1}, dim) |
| 520 |
2/4✓ Branch 1 taken 17342 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 17342 times.
✗ Branch 5 not taken.
|
60073 | | std::views::transform([&] (const auto&) { return _space_dim; }) |
| 521 |
3/6✓ Branch 2 taken 17342 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 17342 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 17342 times.
✗ Branch 9 not taken.
|
52026 | }}); |
| 522 | } | ||
| 523 | |||
| 524 | private: | ||
| 525 | std::size_t _space_dim; | ||
| 526 | }; | ||
| 527 | |||
| 528 | struct ExtendAllFieldAdapterClosure { | ||
| 529 | 17342 | auto operator()(std::size_t space_dimension) const { | |
| 530 | 17342 | 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 | 25605 | FieldPtr transform(FieldPtr field, T&& trafo) { | |
| 611 |
1/2✓ Branch 2 taken 19965 times.
✗ Branch 3 not taken.
|
25605 | 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 |