SpECTRE Documentation Coverage Report
Current view: top level - Domain/Structure - ElementId.hpp Hit Total Coverage
Commit: c428a3e2e0ca78fe0364ec1b0e0493c627d428d4 Lines: 19 52 36.5 %
Date: 2026-04-26 20:20:36
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 ElementId.
       6             : 
       7             : #pragma once
       8             : 
       9             : #include <array>
      10             : #include <cstddef>
      11             : #include <cstdint>
      12             : #include <functional>
      13             : #include <iosfwd>
      14             : #include <optional>
      15             : #include <string>
      16             : 
      17             : #include "Domain/Structure/Direction.hpp"
      18             : #include "Domain/Structure/SegmentId.hpp"
      19             : #include "Domain/Structure/Side.hpp"
      20             : 
      21             : /// \cond
      22             : namespace PUP {
      23             : class er;
      24             : }  // namespace PUP
      25             : /// \endcond
      26             : 
      27             : /*!
      28             :  * \ingroup ComputationalDomainGroup
      29             :  * \brief An ElementId uniquely labels an Element.
      30             :  *
      31             :  * It is constructed from the BlockId of the Block to which the Element belongs
      32             :  * and the VolumeDim SegmentIds that label the segments of the Block that the
      33             :  * Element covers. An optional `grid_index` identifies elements with the same
      34             :  * BlockId and SegmentIds across multiple grids.
      35             :  *
      36             :  * \details
      37             :  * The `ElementId` serves as an index that is compatible with Charm++ and
      38             :  * therefore must adhere to the restrictions imposed by Charm++. These are:
      39             :  * - `ElementId` must satisfy `std::is_pod`
      40             :  * - `ElementId` must not be larger than the size of three `int`s, i.e.
      41             :  *   `sizeof(ElementId) <= 3 * sizeof(int)`
      42             :  */
      43             : template <size_t VolumeDim>
      44           1 : class alignas(int[2]) ElementId {  // NOLINT(modernize-avoid-c-arrays)
      45             :  public:
      46             :   // We restrict the ElementId size to 64 bits for easy hashing into
      47             :   // size_t. This still allows us to have over 9 quadrillion elements, which is
      48             :   // probably enough. 2^45 * 2^8 9 quadrillion elements per grid index, with
      49             :   // up to 16 grid indices.
      50             :   //
      51             :   // Note: C++ populates bits from right to left in order of the
      52             :   // variables. This gives us the direction_mask we use below.
      53           0 :   static constexpr size_t block_id_bits = 8;
      54           0 :   static constexpr size_t grid_index_bits = 4;
      55           0 :   static constexpr size_t direction_bits = 4;
      56             :   /// The maximum allowed refinement level
      57           1 :   static constexpr size_t max_refinement_level = 15;
      58           0 :   static constexpr uint64_t direction_shift =
      59             :       static_cast<uint64_t>(block_id_bits + grid_index_bits);
      60           0 :   static constexpr uint64_t direction_mask = static_cast<uint64_t>(0b1111)
      61             :                                              << direction_shift;
      62             :   static_assert(block_id_bits + 3 * (1 + max_refinement_level) +
      63             :                         grid_index_bits + direction_bits ==
      64             :                     static_cast<size_t>(2 * 8) * sizeof(int),
      65             :                 "Bit representation requires padding or is too large");
      66             : 
      67           0 :   static constexpr size_t volume_dim = VolumeDim;
      68             : 
      69             :   /// Default constructor needed for Charm++ serialization.
      70           1 :   ElementId() = default;
      71           0 :   ElementId(const ElementId&) = default;
      72           0 :   ElementId& operator=(const ElementId&) = default;
      73           0 :   ElementId(ElementId&&) = default;
      74           0 :   ElementId& operator=(ElementId&&) = default;
      75           0 :   ~ElementId() = default;
      76             : 
      77             :   /// Create the ElementId of the root Element of a Block.
      78           1 :   explicit ElementId(size_t block_id, size_t grid_index = 0);
      79             : 
      80             :   /// Create an arbitrary ElementId.
      81           1 :   ElementId(size_t block_id,
      82             :             const std::array<SegmentId, VolumeDim>& segment_ids,
      83             :             size_t grid_index = 0);
      84             : 
      85             :   /// Create an ElementId from its string representation (see `operator<<`).
      86           1 :   explicit ElementId(const std::string& grid_name);
      87             : 
      88           0 :   ElementId<VolumeDim> id_of_child(size_t dim, Side side) const;
      89             : 
      90           0 :   ElementId<VolumeDim> id_of_parent(size_t dim) const;
      91             : 
      92           0 :   size_t block_id() const { return block_id_; }
      93             : 
      94           0 :   size_t grid_index() const { return grid_index_; }
      95             : 
      96           0 :   std::array<size_t, VolumeDim> refinement_levels() const;
      97             : 
      98           0 :   std::array<SegmentId, VolumeDim> segment_ids() const;
      99             : 
     100           0 :   SegmentId segment_id(size_t dim) const;
     101             : 
     102             :   /// Returns an ElementId meant for identifying data on external boundaries,
     103             :   /// which does not correspond to the Id of an actual element.
     104           1 :   static ElementId<VolumeDim> external_boundary_id();
     105             : 
     106             :   /*!
     107             :    * \brief Returns a compact ID containing only the segment ID bits,
     108             :    * with block_id, grid_index, and direction bits stripped.
     109             :    *
     110             :    * This is useful for identifying elements in visualization tools
     111             :    * like ParaView.
     112             :    */
     113           1 :   size_t to_short_id() const;
     114             : 
     115             :   /// Returns the number of block boundaries the element has.
     116           1 :   size_t number_of_block_boundaries() const;
     117             : 
     118             :  protected:
     119             :   /// Create an `ElementId` in a specified direction.
     120           1 :   ElementId(const Direction<VolumeDim>& direction,
     121             :             const ElementId<VolumeDim>& element_id);
     122             : 
     123           0 :   Direction<VolumeDim> direction() const;
     124             : 
     125           0 :   ElementId without_direction() const;
     126             : 
     127             :  private:
     128             :   template <size_t Dim>
     129             :   // NOLINTNEXTLINE(readability-redundant-declaration)
     130           0 :   friend bool operator==(const ElementId<Dim>& lhs, const ElementId<Dim>& rhs);
     131             : 
     132             :   template <size_t Dim>
     133             :   // NOLINTNEXTLINE(readability-redundant-declaration)
     134           0 :   friend bool operator<(const ElementId<Dim>& lhs, const ElementId<Dim>& rhs);
     135             : 
     136             :   template <size_t Dim>
     137             :   // NOLINTNEXTLINE(readability-redundant-declaration)
     138           1 :   friend bool is_zeroth_element(const ElementId<Dim>& id,
     139             :                                 const std::optional<size_t>& grid_index);
     140             : 
     141           0 :   ElementId(uint8_t block_id, uint8_t grid_index, uint8_t direction,
     142             :             uint16_t compact_segment_id_xi, uint16_t compact_segment_id_eta,
     143             :             uint16_t compact_segment_id_zeta);
     144             : 
     145           0 :   uint8_t block_id_ : block_id_bits;
     146           0 :   uint8_t grid_index_ : grid_index_bits;
     147           0 :   uint8_t direction_ : direction_bits;  // end first 16 bits
     148             :   // each of the following is 16 bits in length
     149           0 :   uint16_t compact_segment_id_xi_ : max_refinement_level + 1;
     150           0 :   uint16_t compact_segment_id_eta_ : max_refinement_level + 1;
     151           0 :   uint16_t compact_segment_id_zeta_ : max_refinement_level + 1;
     152             : };
     153             : 
     154             : /// \cond
     155             : // clang-format off
     156             : // macro that generate the pup operator for SegmentId
     157             : PUPbytes(ElementId<1>)      // NOLINT
     158             : PUPbytes(ElementId<2>)  // NOLINT
     159             : PUPbytes(ElementId<3>)  // NOLINT
     160             : /// \endcond
     161             : 
     162             : /// Output operator for ElementId.
     163             : template <size_t VolumeDim>
     164           1 : std::ostream& operator<<(std::ostream& os, const ElementId<VolumeDim>& id);
     165             : // clang-format on
     166             : 
     167             : /// Equivalence operator for ElementId.
     168             : template <size_t VolumeDim>
     169             : bool operator==(const ElementId<VolumeDim>& lhs,
     170             :                 const ElementId<VolumeDim>& rhs);
     171             : 
     172             : /// Inequivalence operator for ElementId.
     173             : template <size_t VolumeDim>
     174             : bool operator!=(const ElementId<VolumeDim>& lhs,
     175             :                 const ElementId<VolumeDim>& rhs);
     176             : 
     177             : /// Define an ordering of element IDs first by grid index, then by block ID,
     178             : /// then by segment ID in each dimension in turn. In each dimension, segment IDs
     179             : /// are ordered first by refinement level (which will typically be the same when
     180             : /// comparing two element IDs), and second by index. There's no particular
     181             : /// reason for this choice of ordering. For applications such as distributing
     182             : /// elements among cores, orderings such as defined by
     183             : /// `domain::BlockZCurveProcDistribution` may be more appropriate.
     184             : template <size_t VolumeDim>
     185           1 : bool operator<(const ElementId<VolumeDim>& lhs,
     186             :                const ElementId<VolumeDim>& rhs);
     187             : 
     188             : template <size_t VolumeDim>
     189           0 : bool operator>(const ElementId<VolumeDim>& lhs,
     190             :                const ElementId<VolumeDim>& rhs) {
     191             :   return rhs < lhs;
     192             : }
     193             : template <size_t VolumeDim>
     194           0 : bool operator<=(const ElementId<VolumeDim>& lhs,
     195             :                 const ElementId<VolumeDim>& rhs) {
     196             :   return !(lhs > rhs);
     197             : }
     198             : template <size_t VolumeDim>
     199           0 : bool operator>=(const ElementId<VolumeDim>& lhs,
     200             :                 const ElementId<VolumeDim>& rhs) {
     201             :   return !(lhs < rhs);
     202             : }
     203             : 
     204             : /// \ingroup ComputationalDomainGroup
     205             : /// Check if two elements overlap, i.e., they are in the same block
     206             : /// and all their segments overlap.
     207             : template <size_t VolumeDim>
     208           1 : bool overlapping(const ElementId<VolumeDim>& a, const ElementId<VolumeDim>& b);
     209             : 
     210             : /// @{
     211             : /// \brief Returns a bool if the element is the zeroth element in the domain.
     212             : ///
     213             : /// \details An element is considered to be the zeroth element if its ElementId
     214             : /// `id` has
     215             : /// 1. id.block_id() == 0
     216             : /// 2. All id.segment_ids() have SegmentId.index() == 0
     217             : /// 3. If the argument `grid_index` is specified, id.grid_index() == grid_index.
     218             : ///
     219             : /// This means that the only element in a domain that this function will return
     220             : /// `true` for is the element in the lower corner of Block0 of that domain. The
     221             : /// `grid_index` will determine which domain is used for the comparison. During
     222             : /// evolutions, only one domain will be active at a time so it doesn't make
     223             : /// sense to compare the `grid_index`. However, during an elliptic solve
     224             : /// when there are multiple grids, this `grid_index` is useful for specifying
     225             : /// only one element over all domains.
     226             : ///
     227             : /// This function is useful if you need a unique element in the domain because
     228             : /// only one element in the whole domain can be the zeroth element.
     229             : ///
     230             : /// \parblock
     231             : /// \warning If you have multiple grids and you don't specify the `grid_index`
     232             : /// argument, this function will return `true` for one element in every grid
     233             : /// and thus can't be used to determine a unique element in a simulation; only a
     234             : /// unique element in each grid.
     235             : /// \endparblock
     236             : /// \parblock
     237             : /// \warning If the domain is re-gridded, a different ElementId may represent
     238             : /// the zeroth element.
     239             : /// \endparblock
     240             : template <size_t Dim>
     241           1 : bool is_zeroth_element(const ElementId<Dim>& id,
     242             :                        const std::optional<size_t>& grid_index);
     243             : 
     244             : // This overload is added (instead of adding a default value for grid_index)
     245             : // in order to avoid adding DomainStructures as a dependency of Parallel
     246             : // by using a forward declaration in Parallel/DistributedObject.hpp
     247             : template <size_t Dim>
     248           1 : bool is_zeroth_element(const ElementId<Dim>& id);
     249             : /// @}
     250             : // ######################################################################
     251             : // INLINE DEFINITIONS
     252             : // ######################################################################
     253             : 
     254             : template <size_t VolumeDim>
     255           0 : size_t hash_value(const ElementId<VolumeDim>& id);
     256             : 
     257             : // NOLINTNEXTLINE(cert-dcl58-cpp)
     258             : namespace std {
     259             : template <size_t VolumeDim>
     260             : struct hash<ElementId<VolumeDim>> {
     261             :   size_t operator()(const ElementId<VolumeDim>& id) const;
     262             : };
     263             : }  // namespace std
     264             : 
     265             : template <size_t VolumeDim>
     266           1 : inline bool operator==(const ElementId<VolumeDim>& lhs,
     267             :                        const ElementId<VolumeDim>& rhs) {
     268             :   // Note: Direction is intentionally skipped.
     269             :   return lhs.block_id_ == rhs.block_id_ and
     270             :          lhs.grid_index_ == rhs.grid_index_ and
     271             :          lhs.compact_segment_id_xi_ == rhs.compact_segment_id_xi_ and
     272             :          lhs.compact_segment_id_eta_ == rhs.compact_segment_id_eta_ and
     273             :          lhs.compact_segment_id_zeta_ == rhs.compact_segment_id_zeta_;
     274             : }
     275             : 
     276             : template <size_t VolumeDim>
     277           1 : inline bool operator!=(const ElementId<VolumeDim>& lhs,
     278             :                        const ElementId<VolumeDim>& rhs) {
     279             :   return not(lhs == rhs);
     280             : }

Generated by: LCOV version 1.14