SpECTRE Documentation Coverage Report
Current view: top level - Domain/Structure - ElementId.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 36.5 % 52 19
Test Date: 2026-10-22 17:29:42
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 2.0-1