SpECTRE Documentation Coverage Report
Current view: top level - Domain/Structure - Direction.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 21 39 53.8 %
Date: 2025-12-05 05:03:31
Legend: Lines: hit not hit

          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             : };

Generated by: LCOV version 1.14