GCC Code Coverage Report


Directory: gridformat/
File: gridformat/common/md_layout.hpp
Date: 2026-10-18 00:09:19
Exec Total Coverage
Lines: 87 87 100.0%
Functions: 100 100 100.0%
Branches: 50 89 56.2%

Line Branch Exec Source
1 // SPDX-FileCopyrightText: 2022-2023 Dennis Gläser <dennis.glaeser@iws.uni-stuttgart.de>
2 // SPDX-License-Identifier: MIT
3 /*!
4 * \file
5 * \ingroup Common
6 * \copydoc GridFormat::MDLayout
7 */
8 #ifndef GRIDFORMAT_COMMON_MD_LAYOUT_HPP_
9 #define GRIDFORMAT_COMMON_MD_LAYOUT_HPP_
10
11 #include <ostream>
12 #include <utility>
13 #include <vector>
14 #include <numeric>
15 #include <iterator>
16 #include <algorithm>
17 #include <initializer_list>
18 #include <type_traits>
19 #include <string>
20
21 #include <gridformat/common/reserved_vector.hpp>
22 #include <gridformat/common/type_traits.hpp>
23 #include <gridformat/common/concepts.hpp>
24 #include <gridformat/common/ranges.hpp>
25
26 namespace GridFormat {
27
28 #ifndef DOXYGEN
29 namespace Detail {
30
31 template<Concepts::StaticallySizedRange R, typename Iterator>
32 580476 constexpr void set_sub_extents(Iterator it) {
33 580476 *it = static_size<R>;
34 580476 ++it;
35 if constexpr (has_sub_range<R>)
36 2 set_sub_extents<std::ranges::range_value_t<R>>(it);
37 580476 }
38
39 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
40 2377107 auto get_md_extents() {
41 if constexpr (Concepts::Scalar<T>)
42 1141115 return std::array<std::size_t, 0>{};
43 else {
44 std::array<std::size_t, mdrange_dimension<T>> extents;
45 1235992 extents[0] = static_size<T>;
46 if constexpr (mdrange_dimension<T> > 1)
47 598978 Detail::set_sub_extents<std::ranges::range_value_t<T>>(extents.begin() + 1);
48 1235992 return extents;
49 }
50 }
51
52 template<typename T> requires(std::ranges::range<T> and !Concepts::StaticallySizedRange<T>)
53 1 auto get_md_extents(const T& range) {
54 static_assert(
55 Concepts::StaticallySizedRange<std::ranges::range_value_t<T>>,
56 "Sub-ranges must be statically sized in order to determine md layouts from md ranges"
57 );
58 std::array<std::size_t, mdrange_dimension<T>> extents;
59 1 extents[0] = Ranges::size(range);
60 1 Detail::set_sub_extents<std::ranges::range_value_t<T>>(extents.begin() + 1);
61 1 return extents;
62 }
63
64 } // namespace Detail
65 #endif // DOXYGEN
66
67 /*!
68 * \ingroup Common
69 * \brief Represents the layout (dimension, extents) of a multi-dimensional range
70 */
71 class MDLayout {
72 static constexpr std::size_t buffered_dimensions = 5;
73
74 public:
75 169 MDLayout() = default;
76
77 template<Concepts::MDRange<1> R>
78 736324 explicit MDLayout(R&& extents) {
79
1/2
✓ Branch 2 taken 368167 times.
✗ Branch 3 not taken.
736324 _extents.reserve(Ranges::size(extents));
80 if constexpr (!std::is_lvalue_reference_v<R>)
81
2/4
✓ Branch 1 taken 365441 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 365441 times.
✗ Branch 6 not taken.
730872 std::ranges::move(std::move(extents), std::back_inserter(_extents));
82 else
83
2/4
✓ Branch 1 taken 2726 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2726 times.
✗ Branch 5 not taken.
5452 std::ranges::copy(extents, std::back_inserter(_extents));
84 736324 }
85
86 template<std::integral T>
87 1326041 explicit MDLayout(const std::initializer_list<T>& extents) {
88
1/2
✓ Branch 2 taken 1178805 times.
✗ Branch 3 not taken.
1326041 _extents.reserve(Ranges::size(extents));
89
2/4
✓ Branch 1 taken 1178805 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1178805 times.
✗ Branch 5 not taken.
1326041 std::ranges::copy(extents, std::back_inserter(_extents));
90 1326041 }
91
92 7255 auto begin() const { return _extents.begin(); }
93 463 auto begin() { return _extents.begin(); }
94
95 7255 auto end() const { return _extents.end(); }
96 463 auto end() { return _extents.end(); }
97
98 3638740 std::size_t dimension() const {
99 3638740 return _extents.size();
100 }
101
102 4722249 std::size_t extent(unsigned int codim) const {
103 4722249 return _extents.at(codim);
104 }
105
106 760432 std::size_t number_of_entries() const {
107
2/2
✓ Branch 1 taken 32434 times.
✓ Branch 2 taken 727998 times.
760432 if (_is_scalar())
108 32434 return 1;
109 727998 return std::accumulate(
110 _extents.begin(),
111 _extents.end(),
112 std::size_t{1},
113 std::multiplies{}
114 727998 );
115 }
116
117 181559 std::size_t number_of_entries(unsigned int codim) const {
118
2/4
✓ Branch 1 taken 181559 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 181559 times.
✗ Branch 5 not taken.
181559 return sub_layout(codim).number_of_entries();
119 }
120
121 236844 MDLayout sub_layout(unsigned int codim) const {
122
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 236843 times.
236844 if (codim >= dimension())
123 throw ValueError(
124
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 "Given codim " + std::to_string(codim)
125
4/8
✓ 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.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
4 + " exceeds dimensions (" + std::to_string(dimension()) + ")"
126
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
3 );
127 236843 return MDLayout{std::vector<std::size_t>{
128 236843 _extents.begin() + codim,
129 236843 _extents.end()
130
2/4
✓ Branch 1 taken 236843 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 236843 times.
✗ Branch 5 not taken.
473686 }};
131 }
132
133 17678 bool operator==(const MDLayout& other) const {
134 17678 return std::ranges::equal(_extents, other._extents);
135 }
136
137 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
138 2311156 MDLayout& with_sub_layout_from() {
139
1/2
✓ Branch 1 taken 299489 times.
✗ Branch 2 not taken.
2311156 auto sub_extents = Detail::get_md_extents<T>();
140
1/2
✓ Branch 2 taken 1155590 times.
✗ Branch 3 not taken.
4622312 _extents.reserve(_extents.size() + sub_extents.size());
141
2/4
✓ Branch 1 taken 1155590 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1155590 times.
✗ Branch 6 not taken.
2311156 std::ranges::move(std::move(sub_extents), std::back_inserter(_extents));
142 2311156 return *this;
143 }
144
145 template<std::ranges::range T>
146 1 MDLayout& with_sub_layout_from(T&& range) {
147
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 auto sub_extents = Detail::get_md_extents<T>(std::forward<T>(range));
148
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 _extents.reserve(_extents.size() + sub_extents.size());
149
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 std::ranges::move(std::move(sub_extents), std::back_inserter(_extents));
150 1 return *this;
151 }
152
153 9 MDLayout& with_sub_layout(const MDLayout& layout) {
154 9 _extents.reserve(_extents.size() + layout.dimension());
155
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 9 times.
19 for (std::size_t i = 0; i < layout.dimension(); ++i)
156
2/4
✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10 times.
✗ Branch 5 not taken.
10 _extents.push_back(layout.extent(i));
157 9 return *this;
158 }
159
160 template<std::ranges::range R>
161 4178 void export_to(R&& out) const {
162
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2782 times.
4178 if (Ranges::size(out) < dimension())
163
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 throw SizeError("Given output range is too small");
164 4177 std::ranges::copy(_extents, std::ranges::begin(out));
165 4177 }
166
167 3 friend std::ostream& operator<<(std::ostream& s, const MDLayout& layout) {
168 3 s << "(";
169
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if (layout._extents.size() > 0) {
170 3 s << layout._extents[0];
171
3/6
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
3 std::ranges::for_each(layout._extents | std::views::drop(1), [&] (const auto ext) {
172 3 s << "," << ext;
173 3 });
174 }
175 3 s << ")";
176 3 return s;
177 }
178
179 private:
180 760432 bool _is_scalar() const {
181 760432 return _extents.size() == 0;
182 }
183
184 ReservedVector<std::size_t, buffered_dimensions> _extents;
185 };
186
187
188 /*!
189 * \ingroup Common
190 * \brief Get the layout of a range (or a scalar) whose size is known at compile-time
191 */
192 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
193 43579 MDLayout get_md_layout() {
194
1/2
✓ Branch 2 taken 31467 times.
✗ Branch 3 not taken.
43579 return MDLayout{Detail::get_md_extents<T>()};
195 }
196
197 /*!
198 * \ingroup Common
199 * \brief Get the layout for a range consisting of n instances of the range (or scalar)
200 * given as template argument, whose size is known at compile-time.
201 */
202 template<typename T> requires(Concepts::StaticallySizedRange<T> or Concepts::Scalar<T>)
203 [[deprecated("use MDLayout{{n}}.with_sub_layout_from<T>() instead.")]]
204 MDLayout get_md_layout(std::size_t n) {
205 if constexpr (Concepts::Scalar<T>)
206 return MDLayout{{n}};
207 else {
208 std::array<std::size_t, mdrange_dimension<T> + 1> extents;
209 extents[0] = n;
210 Detail::set_sub_extents<T>(extents.begin() + 1);
211 return MDLayout{extents};
212 }
213 }
214
215 /*!
216 * \ingroup Common
217 * \brief Get the multi-dimensional layout for the given range
218 */
219 template<std::ranges::range R>
220 requires(
221 Concepts::StaticallySizedRange<std::ranges::range_value_t<R>> or
222 Concepts::Scalar<std::ranges::range_value_t<R>>
223 )
224 255 MDLayout get_md_layout(R&& r) {
225
7/11
✓ Branch 1 taken 187 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 187 times.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 187 times.
✓ Branch 8 taken 6 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 187 times.
✗ Branch 11 not taken.
255 return MDLayout{{Ranges::size(r)}}.template with_sub_layout_from<std::ranges::range_value_t<R>>();
226 }
227
228 /*!
229 * \ingroup Common
230 * \brief Overload for scalars
231 */
232 template<Concepts::Scalar T>
233 1 MDLayout get_md_layout(const T&) {
234 1 return MDLayout{};
235 }
236
237 } // namespace GridFormat
238
239 #endif // GRIDFORMAT_COMMON_MD_LAYOUT_HPP_
240