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