Line data Source code
1 1 : // Distributed under the MIT License.
2 : // See LICENSE.txt for details.
3 :
4 : /// \file
5 : /// Defines class template Direction.
6 :
7 : #pragma once
8 :
9 : #include <array>
10 : #include <cstddef>
11 : #include <functional>
12 : #include <iosfwd>
13 :
14 : #include "Domain/Structure/Side.hpp"
15 : #include "Utilities/ErrorHandling/Assert.hpp"
16 :
17 : namespace PUP {
18 : class er;
19 : } // namespace PUP
20 :
21 : /// \ingroup ComputationalDomainGroup
22 : /// A particular Side along a particular coordinate Axis.
23 : template <size_t VolumeDim>
24 1 : class Direction {
25 : public:
26 0 : static constexpr const size_t volume_dim = VolumeDim;
27 0 : static constexpr uint8_t axis_mask = 0b0011;
28 0 : static constexpr uint8_t side_mask = 0b1100;
29 0 : static constexpr uint8_t all_mask = 0b1111;
30 0 : static constexpr uint8_t number_of_bits = 4;
31 :
32 : /// The logical-coordinate names of each dimension
33 : enum class Axis : uint8_t;
34 :
35 : /// Construct by specifying an Axis and a Side.
36 1 : Direction(Axis axis, Side side);
37 :
38 : /// Construct by specifying a dimension and a Side.
39 1 : Direction(size_t dimension, Side side);
40 :
41 : /// Default constructor for Charm++ serialization.
42 1 : Direction();
43 :
44 : /// Get a Direction representing "self" or "no direction"
45 1 : static Direction<VolumeDim> self();
46 :
47 : /// The dimension of the Direction
48 1 : size_t dimension() const {
49 : ASSERT(bit_field_ != 0, "Cannot use an uninitialized Direction");
50 : return static_cast<uint8_t>(axis());
51 : }
52 :
53 : /// The Axis of the Direction
54 1 : Axis axis() const {
55 : ASSERT(bit_field_ != 0, "Cannot use an uninitialized Direction");
56 : return static_cast<Axis>(bit_field_ bitand axis_mask);
57 : }
58 :
59 : /// The side of the Direction
60 1 : Side side() const { return static_cast<Side>(bit_field_ bitand side_mask); }
61 :
62 : /// The sign for the normal to the Side.
63 : ///
64 : /// This is `+1.0` if `side() == Side::Upper` and `-1.0` if
65 : /// `side() == Side::Lower`, otherwise an `ASSERT` is triggered.
66 1 : double sign() const {
67 : ASSERT(Side::Lower == side() or Side::Upper == side(),
68 : "sign() is only defined for Side::Lower and Side::Upper, not "
69 : << side());
70 : return (Side::Lower == side() ? -1.0 : 1.0);
71 : }
72 :
73 : /// The opposite Direction.
74 1 : Direction<VolumeDim> opposite() const;
75 :
76 : // An array of all logical Directions for a given dimensionality.
77 : static const std::array<Direction<VolumeDim>, 2 * VolumeDim>&
78 0 : all_directions();
79 :
80 : /// @{
81 : /// Helper functions for creating specific Directions.
82 : /// These are labeled by the logical-coordinate names (Xi,Eta,Zeta).
83 : // Note: these are functions because they contain static_assert.
84 1 : static Direction<VolumeDim> lower_xi();
85 1 : static Direction<VolumeDim> upper_xi();
86 1 : static Direction<VolumeDim> lower_eta();
87 1 : static Direction<VolumeDim> upper_eta();
88 1 : static Direction<VolumeDim> lower_zeta();
89 1 : static Direction<VolumeDim> upper_zeta();
90 : /// @}
91 :
92 0 : uint8_t bits() const { return bit_field_; }
93 :
94 : /// Serialization for Charm++
95 : // NOLINTNEXTLINE(google-runtime-references)
96 1 : void pup(PUP::er& p);
97 :
98 : private:
99 : template <size_t LocalVolumeDim>
100 0 : friend bool operator==(const Direction<LocalVolumeDim>& lhs,
101 : const Direction<LocalVolumeDim>& rhs);
102 : template <size_t LocalVolumeDim>
103 0 : friend size_t hash_value(const Direction<LocalVolumeDim>& d);
104 :
105 0 : uint8_t bit_field_{0};
106 : };
107 :
108 : /// Output operator for a Direction.
109 : template <size_t VolumeDim>
110 1 : std::ostream& operator<<(std::ostream& os,
111 : const Direction<VolumeDim>& direction);
112 :
113 : //##############################################################################
114 : // INLINE DEFINITIONS
115 : //##############################################################################
116 :
117 : /// \cond
118 : // clang-tidy: redundant declaration false positive. Needs to be here because of
119 : // the Axis enum, otherwise won't compile.
120 : template <size_t VolumeDim>
121 : Direction<VolumeDim>::Direction() = default; // NOLINT
122 :
123 : // Needed in order to address warning; ignorning -Wpedantic is needed.
124 : // Bug 61491
125 : // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61491
126 : #pragma GCC diagnostic push
127 : #pragma GCC diagnostic ignored "-Wpedantic"
128 : template <>
129 : enum class Direction<1>::Axis : uint8_t{Xi = 0};
130 :
131 : template <>
132 : enum class Direction<2>::Axis : uint8_t{Xi = 0, Eta = 1};
133 :
134 : template <>
135 : enum class Direction<3>::Axis : uint8_t{Xi = 0, Eta = 1, Zeta = 2};
136 : #pragma GCC diagnostic pop
137 :
138 : template <size_t VolumeDim>
139 : inline Direction<VolumeDim> Direction<VolumeDim>::lower_xi() {
140 : return Direction(Direction<VolumeDim>::Axis::Xi, Side::Lower);
141 : }
142 :
143 : template <size_t VolumeDim>
144 : inline Direction<VolumeDim> Direction<VolumeDim>::upper_xi() {
145 : return Direction(Direction<VolumeDim>::Axis::Xi, Side::Upper);
146 : }
147 :
148 : template <size_t VolumeDim>
149 : inline Direction<VolumeDim> Direction<VolumeDim>::lower_eta() {
150 : static_assert(VolumeDim == 2 or VolumeDim == 3, "VolumeDim must be 2 or 3.");
151 : return Direction(Direction<VolumeDim>::Axis::Eta, Side::Lower);
152 : }
153 :
154 : template <size_t VolumeDim>
155 : inline Direction<VolumeDim> Direction<VolumeDim>::upper_eta() {
156 : static_assert(VolumeDim == 2 or VolumeDim == 3, "VolumeDim must be 2 or 3.");
157 : return Direction(Direction<VolumeDim>::Axis::Eta, Side::Upper);
158 : }
159 :
160 : template <size_t VolumeDim>
161 : inline Direction<VolumeDim> Direction<VolumeDim>::lower_zeta() {
162 : static_assert(VolumeDim == 3, "VolumeDim must be 3.");
163 : return Direction(Direction<VolumeDim>::Axis::Zeta, Side::Lower);
164 : }
165 :
166 : template <size_t VolumeDim>
167 : inline Direction<VolumeDim> Direction<VolumeDim>::upper_zeta() {
168 : static_assert(VolumeDim == 3, "VolumeDim must be 3.");
169 : return Direction(Direction<VolumeDim>::Axis::Zeta, Side::Upper);
170 : }
171 :
172 : template <size_t VolumeDim>
173 : inline Direction<VolumeDim> Direction<VolumeDim>::opposite() const {
174 : ASSERT(bit_field_ != 0, "Cannot use an uninitialized Direction");
175 : return Direction<VolumeDim>(axis(), ::opposite(side()));
176 : }
177 :
178 : template <>
179 : inline const std::array<Direction<1>, 2>& Direction<1>::all_directions() {
180 : const static auto directions = std::array<Direction<1>, 2>{
181 : {Direction<1>::lower_xi(), Direction<1>::upper_xi()}};
182 : return directions;
183 : }
184 :
185 : template <>
186 : inline const std::array<Direction<2>, 4>& Direction<2>::all_directions() {
187 : const static auto directions = std::array<Direction<2>, 4>{
188 : {Direction<2>::lower_xi(), Direction<2>::upper_xi(),
189 : Direction<2>::lower_eta(), Direction<2>::upper_eta()}};
190 : return directions;
191 : }
192 :
193 : template <>
194 : inline const std::array<Direction<3>, 6>& Direction<3>::all_directions() {
195 : const static auto directions = std::array<Direction<3>, 6>{
196 : {Direction<3>::lower_xi(), Direction<3>::upper_xi(),
197 : Direction<3>::lower_eta(), Direction<3>::upper_eta(),
198 : Direction<3>::lower_zeta(), Direction<3>::upper_zeta()}};
199 : return directions;
200 : }
201 : /// \endcond
202 :
203 : template <size_t VolumeDim>
204 0 : bool operator==(const Direction<VolumeDim>& lhs,
205 : const Direction<VolumeDim>& rhs) {
206 : return lhs.bit_field_ == rhs.bit_field_;
207 : }
208 :
209 : template <size_t VolumeDim>
210 0 : bool operator!=(const Direction<VolumeDim>& lhs,
211 : const Direction<VolumeDim>& rhs) {
212 : return not(lhs == rhs);
213 : }
214 :
215 : /// Define an ordering of directions first by axis (xi, eta, zeta), then by side
216 : /// (lower, upper). There's no particular reason for this choice of ordering.
217 : template <size_t VolumeDim>
218 1 : bool operator<(const Direction<VolumeDim>& lhs,
219 : const Direction<VolumeDim>& rhs);
220 :
221 : template <size_t VolumeDim>
222 0 : bool operator>(const Direction<VolumeDim>& lhs,
223 : const Direction<VolumeDim>& rhs) {
224 : return rhs < lhs;
225 : }
226 : template <size_t VolumeDim>
227 0 : bool operator<=(const Direction<VolumeDim>& lhs,
228 : const Direction<VolumeDim>& rhs) {
229 : return !(lhs > rhs);
230 : }
231 : template <size_t VolumeDim>
232 0 : bool operator>=(const Direction<VolumeDim>& lhs,
233 : const Direction<VolumeDim>& rhs) {
234 : return !(lhs < rhs);
235 : }
236 :
237 : template <size_t VolumeDim>
238 0 : size_t hash_value(const Direction<VolumeDim>& d) {
239 : return d.bit_field_;
240 : }
241 :
242 : namespace std {
243 : template <size_t VolumeDim>
244 : struct hash<Direction<VolumeDim>> {
245 : size_t operator()(const Direction<VolumeDim>& d) const {
246 : return hash_value(d);
247 : }
248 : };
249 : } // namespace std
250 :
251 : /// \ingroup ComputationalDomainGroup
252 : /// Provides a perfect hash if the size of the hash table is `2 * Dim`. To take
253 : /// advantage of this, use the `FixedHashMap` class.
254 : template <size_t Dim>
255 1 : struct DirectionHash {
256 : template <size_t MaxSize>
257 0 : static constexpr bool is_perfect = MaxSize == 2 * Dim;
258 :
259 0 : size_t operator()(const Direction<Dim>& t) {
260 : return 2 * t.dimension() + (t.side() == Side::Upper ? 1 : 0);
261 : }
262 : };
|