Line data Source code
1 0 : // Distributed under the MIT License. 2 : // See LICENSE.txt for details. 3 : 4 : #pragma once 5 : 6 : #include <array> 7 : #include <cstddef> 8 : #include <iosfwd> 9 : 10 : #include "DataStructures/Tensor/TypeAliases.hpp" 11 : #include "Domain/Structure/Direction.hpp" 12 : #include "Domain/Structure/SegmentId.hpp" 13 : #include "Domain/Structure/Side.hpp" 14 : #include "NumericalAlgorithms/Spectral/Mesh.hpp" 15 : #include "Utilities/Gsl.hpp" 16 : 17 : namespace PUP { 18 : class er; 19 : } // namespace PUP 20 : 21 : /*! 22 : * \ingroup ComputationalDomainGroup 23 : * \brief A mapping of the logical coordinate axes of a host to the logical 24 : * coordinate axes of a neighbor of the host. 25 : * 26 : * Given a `size_t dimension`, a `Direction`, a `SegmentId`, or a `Mesh` of the 27 : * host, an `OrientationMap` will give the corresponding value in the neighbor. 28 : * 29 : * \tparam VolumeDim the dimension of the blocks. 30 : * 31 : * See the [tutorial](@ref tutorial_orientations) for information on how 32 : * OrientationMaps are used and constructed. 33 : * 34 : * \note If there is no discrete rotation between logical coordinates (e.g. the 35 : * angular coordinates of a spherical shell abutting a wedge of a cubed sphere) 36 : * specify Direction<VolumeDim>::self() as the mapped direction 37 : * 38 : */ 39 : template <size_t VolumeDim> 40 1 : class OrientationMap { 41 : public: 42 0 : static constexpr uint16_t aligned_mask = 0b1000000000000000; 43 0 : static constexpr uint16_t version_mask = 0b0111000000000000; 44 : 45 : /// \brief Creates an OrientationMap in an uninitialized state. 46 : /// 47 : /// This can be helpful for debugging code. If you would like the identity 48 : /// map, please use `create_aligned()`. 49 1 : OrientationMap(); 50 : /// Mapped directions relative to the positive (`Side::Upper`) direction in 51 : /// each logical direction. 52 1 : explicit OrientationMap( 53 : std::array<Direction<VolumeDim>, VolumeDim> mapped_directions); 54 0 : OrientationMap( 55 : const std::array<Direction<VolumeDim>, VolumeDim>& directions_in_host, 56 : const std::array<Direction<VolumeDim>, VolumeDim>& 57 : directions_in_neighbor); 58 0 : ~OrientationMap() = default; 59 0 : OrientationMap(const OrientationMap&) = default; 60 0 : OrientationMap& operator=(const OrientationMap&) = default; 61 0 : OrientationMap(OrientationMap&& /*rhs*/) = default; 62 0 : OrientationMap& operator=(OrientationMap&& /*rhs*/) = default; 63 : 64 : /// Creates an OrientationMap that is the identity map on directions. 65 : /// `is_aligned()` is `true` in this case. 66 1 : static OrientationMap<VolumeDim> create_aligned(); 67 : 68 : /// True when mapped(Direction) == Direction 69 1 : bool is_aligned() const { 70 : ASSERT(bit_field_ != static_cast<uint16_t>(0b1 << 15), 71 : "Cannot use a default-constructed OrientationMap"); 72 : return (bit_field_ bitand aligned_mask) == aligned_mask; 73 : } 74 : 75 : /// The corresponding dimension in the neighbor. 76 1 : size_t operator()(const size_t dim) const { 77 : ASSERT(bit_field_ != static_cast<uint16_t>(0b1 << 15), 78 : "Cannot use a default-constructed OrientationMap"); 79 : const auto neighbor_direction = get_direction(dim); 80 : ASSERT(neighbor_direction.side() != Side::Self, 81 : "There is no corresponding dimension"); 82 : return neighbor_direction.dimension(); 83 : } 84 : 85 : /// The corresponding direction in the neighbor. 86 1 : Direction<VolumeDim> operator()(const Direction<VolumeDim>& direction) const { 87 : ASSERT(bit_field_ != static_cast<uint16_t>(0b1 << 15), 88 : "Cannot use a default-constructed OrientationMap"); 89 : return direction.side() == Side::Upper 90 : ? get_direction(direction.dimension()) 91 : : get_direction(direction.dimension()).opposite(); 92 : } 93 : 94 : /// The corresponding SegmentIds in the neighbor. 95 1 : std::array<SegmentId, VolumeDim> operator()( 96 : const std::array<SegmentId, VolumeDim>& segmentIds) const; 97 : 98 : /// The corresponding Mesh in the neighbor 99 1 : Mesh<VolumeDim> operator()(const Mesh<VolumeDim>& mesh) const; 100 : 101 : /// An array whose elements are permuted such that 102 : /// `result[this->operator()(d)] = array_to_permute[d]`. 103 : /// 104 : /// \note the permutation depends only on how the dimension is mapped 105 : /// and ignores the side of the mapped direction. 106 : template <typename T> 107 1 : std::array<T, VolumeDim> permute_to_neighbor( 108 : const std::array<T, VolumeDim>& array_to_permute) const; 109 : 110 : /// An array whose elements are permuted such that 111 : /// `result[d] = array_in_neighbor[this->operator()(d)]` 112 : /// 113 : /// \note the permutation depends only on how the dimension is mapped 114 : /// and ignores the side of the mapped direction. 115 : template <typename T> 116 1 : std::array<T, VolumeDim> permute_from_neighbor( 117 : const std::array<T, VolumeDim>& array_in_neighbor) const; 118 : 119 : /// The corresponding Orientation of the host in the frame of the neighbor. 120 1 : OrientationMap<VolumeDim> inverse_map() const; 121 : 122 : /// Serialization for Charm++ 123 : // NOLINTNEXTLINE(google-runtime-references) 124 1 : void pup(PUP::er& p); 125 : 126 : private: 127 0 : friend bool operator==(const OrientationMap& lhs, const OrientationMap& rhs) { 128 : return lhs.bit_field_ == rhs.bit_field_; 129 : } 130 : 131 0 : Direction<VolumeDim> get_direction(size_t dim) const; 132 0 : void set_direction(size_t dim, const Direction<VolumeDim>& direction); 133 0 : void set_aligned(bool is_aligned); 134 0 : std::set<size_t> set_of_dimensions() const; 135 : 136 0 : uint16_t bit_field_{0b1 << 15}; 137 : }; 138 : 139 : /// Output operator for OrientationMap. 140 : template <size_t VolumeDim> 141 1 : std::ostream& operator<<(std::ostream& os, 142 : const OrientationMap<VolumeDim>& orientation); 143 : 144 : template <size_t VolumeDim> 145 0 : bool operator!=(const OrientationMap<VolumeDim>& lhs, 146 : const OrientationMap<VolumeDim>& rhs) { 147 : return not(lhs == rhs); 148 : } 149 : 150 : template <size_t VolumeDim> 151 : template <typename T> 152 : std::array<T, VolumeDim> OrientationMap<VolumeDim>::permute_to_neighbor( 153 : const std::array<T, VolumeDim>& array_to_permute) const { 154 : std::array<T, VolumeDim> array_in_neighbor = array_to_permute; 155 : if (is_aligned() or VolumeDim <= 1) { 156 : return array_in_neighbor; 157 : } 158 : for (size_t i = 0; i < VolumeDim; i++) { 159 : gsl::at(array_in_neighbor, this->operator()(i)) = 160 : gsl::at(array_to_permute, i); 161 : } 162 : return array_in_neighbor; 163 : } 164 : 165 : template <size_t VolumeDim> 166 : template <typename T> 167 : std::array<T, VolumeDim> OrientationMap<VolumeDim>::permute_from_neighbor( 168 : const std::array<T, VolumeDim>& array_in_neighbor) const { 169 : std::array<T, VolumeDim> result = array_in_neighbor; 170 : if (not is_aligned() and VolumeDim > 1) { 171 : for (size_t i = 0; i < VolumeDim; i++) { 172 : gsl::at(result, i) = gsl::at(array_in_neighbor, this->operator()(i)); 173 : } 174 : } 175 : return result; 176 : } 177 : 178 : /// \ingroup ComputationalDomainGroup 179 : /// `OrientationMap`s define an active rotation of the logical axes that bring 180 : /// the axes of a host block into alignment with the logical axes of the 181 : /// neighbor block. `discrete_rotation` applies this active rotation on the 182 : /// coordinates as opposed to the axes. 183 : /// For a two-dimensional example, consider a host block and a neighbor block, 184 : /// where the OrientationMap between them is \f$\{-\eta,+\xi\}\f$. A quarter- 185 : /// turn counterclockwise of the host block's logical axes would bring them into 186 : /// alignment with those of the neighbor. That is, after this active rotation, 187 : /// the blocks would be Aligned. Now consider a point A with coordinates 188 : /// (+1.0,-0.5). An active quarter-turn rotation counter-clockwise about the 189 : /// origin, keeping the axes fixed, brings point A into the coordinates 190 : /// (+0.5,+1.0). This is how `discrete_rotation` interprets the 191 : /// `OrientationMap` passed to it. 192 : template <size_t VolumeDim, typename T> 193 1 : std::array<T, VolumeDim> discrete_rotation( 194 : const OrientationMap<VolumeDim>& rotation, 195 : std::array<T, VolumeDim> source_coords); 196 : 197 : /*! 198 : * \ingroup ComputationalDomainGroup 199 : * \brief Computes the Jacobian of the transformation that is computed by 200 : * `discrete_rotation()` 201 : * 202 : * \note This always returns a `double` because the Jacobian is spatially 203 : * constant. 204 : */ 205 : template <size_t VolumeDim> 206 1 : tnsr::Ij<double, VolumeDim, Frame::NoFrame> discrete_rotation_jacobian( 207 : const OrientationMap<VolumeDim>& orientation); 208 : 209 : /*! 210 : * \ingroup ComputationalDomainGroup 211 : * \brief Computes the inverse Jacobian of the transformation that is computed 212 : * by `discrete_rotation()` 213 : */ 214 : template <size_t VolumeDim> 215 1 : tnsr::Ij<double, VolumeDim, Frame::NoFrame> discrete_rotation_inverse_jacobian( 216 : const OrientationMap<VolumeDim>& orientation);