SpECTRE Documentation Coverage Report
Current view: top level - Domain/Structure - ElementId.hpp Hit Total Coverage
Commit: 3c072f0ce967e2e56649d3fa12aa2a0e4fe2a42e Lines: 13 44 29.5 %
Date: 2024-04-23 20:50:18
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 <functional>
      12             : #include <iosfwd>
      13             : #include <limits>
      14             : #include <optional>
      15             : 
      16             : #include "Domain/Structure/SegmentId.hpp"
      17             : #include "Domain/Structure/Side.hpp"
      18             : #include "Utilities/Algorithm.hpp"
      19             : #include "Utilities/ErrorHandling/Assert.hpp"
      20             : #include "Utilities/ErrorHandling/Error.hpp"
      21             : #include "Utilities/MakeArray.hpp"
      22             : 
      23             : /// \cond
      24             : namespace PUP {
      25             : class er;
      26             : }  // namespace PUP
      27             : /// \endcond
      28             : 
      29             : /*!
      30             :  * \ingroup ComputationalDomainGroup
      31             :  * \brief An ElementId uniquely labels an Element.
      32             :  *
      33             :  * It is constructed from the BlockId of the Block to which the Element belongs
      34             :  * and the VolumeDim SegmentIds that label the segments of the Block that the
      35             :  * Element covers. An optional `grid_index` identifies elements with the same
      36             :  * BlockId and SegmentIds across multiple grids.
      37             :  *
      38             :  * \details
      39             :  * The `ElementId` serves as an index that is compatible with Charm++ and
      40             :  * therefore must adhere to the restrictions imposed by Charm++. These are:
      41             :  * - `ElementId` must satisfy `std::is_pod`
      42             :  * - `ElementId` must not be larger than the size of three `int`s, i.e.
      43             :  *   `sizeof(ElementId) <= 3 * sizeof(int)`
      44             :  *
      45             :  * The latter restriction can be relaxed with a special compilation flag to
      46             :  * Charm++, but we have not yet needed more elements than can be accounted for
      47             :  * by densely packing bits together. `SegmentId` is responsible for handling the
      48             :  * low-level bit manipulations to create an index that satisfies the size
      49             :  * constraints.
      50             :  */
      51             : template <size_t VolumeDim>
      52           1 : class ElementId {
      53             :  public:
      54           0 :   static constexpr size_t block_id_bits = 24;
      55           0 :   static constexpr size_t grid_index_bits = 8;
      56           0 :   static constexpr size_t refinement_bits = 4;
      57           0 :   static constexpr size_t max_refinement_level = 16;
      58             :   // We need some padding to ensure bit fields align with type boundaries,
      59             :   // otherwise the size of `ElementId` is too large.
      60           0 :   static constexpr size_t padding = 4;
      61             :   static_assert(block_id_bits + 3 * (refinement_bits + max_refinement_level) +
      62             :                         grid_index_bits + padding ==
      63             :                     3 * 8 * sizeof(int),
      64             :                 "Bit representation requires padding or is too large");
      65             :   static_assert(two_to_the(refinement_bits) >= max_refinement_level,
      66             :                 "Not enough bits to represent all refinement levels");
      67             : 
      68           0 :   static constexpr size_t volume_dim = VolumeDim;
      69             : 
      70             :   /// Default constructor needed for Charm++ serialization.
      71           1 :   ElementId() = default;
      72           0 :   ElementId(const ElementId&) = default;
      73           0 :   ElementId& operator=(const ElementId&) = default;
      74           0 :   ElementId(ElementId&&) = default;
      75           0 :   ElementId& operator=(ElementId&&) = default;
      76           0 :   ~ElementId() = default;
      77             : 
      78             :   /// Create the ElementId of the root Element of a Block.
      79           1 :   explicit ElementId(size_t block_id, size_t grid_index = 0);
      80             : 
      81             :   /// Create an arbitrary ElementId.
      82           1 :   ElementId(size_t block_id, 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 :   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             :     if constexpr (VolumeDim == 1) {
      98             :       return {{refinement_level_xi_}};
      99             :     } else if constexpr (VolumeDim == 2) {
     100             :       return {{refinement_level_xi_, refinement_level_eta_}};
     101             :     } else if constexpr (VolumeDim == 3) {
     102             :       return {{refinement_level_xi_, refinement_level_eta_,
     103             :                refinement_level_zeta_}};
     104             :     }
     105             :   }
     106             : 
     107           0 :   std::array<SegmentId, VolumeDim> segment_ids() const {
     108             :     if constexpr (VolumeDim == 1) {
     109             :       return {{SegmentId{refinement_level_xi_, index_xi_}}};
     110             :     } else if constexpr (VolumeDim == 2) {
     111             :       return {{SegmentId{refinement_level_xi_, index_xi_},
     112             :                SegmentId{refinement_level_eta_, index_eta_}}};
     113             :     } else if constexpr (VolumeDim == 3) {
     114             :       return {{SegmentId{refinement_level_xi_, index_xi_},
     115             :                SegmentId{refinement_level_eta_, index_eta_},
     116             :                SegmentId{refinement_level_zeta_, index_zeta_}}};
     117             :     }
     118             :   }
     119             : 
     120           0 :   SegmentId segment_id(const size_t dim) const {
     121             :     ASSERT(dim < VolumeDim, "Dimension must be smaller than "
     122             :                                 << VolumeDim << ", but is: " << dim);
     123             :     switch (dim) {
     124             :       case 0:
     125             :         return {refinement_level_xi_, index_xi_};
     126             :       case 1:
     127             :         return {refinement_level_eta_, index_eta_};
     128             :       case 2:
     129             :         return {refinement_level_zeta_, index_zeta_};
     130             :       default:
     131             :         ERROR("Invalid dimension: " << dim);
     132             :     }
     133             :   }
     134             : 
     135             :   /// Returns an ElementId meant for identifying data on external boundaries,
     136             :   /// which should never correspond to the Id of an actual element.
     137           1 :   static ElementId<VolumeDim> external_boundary_id();
     138             : 
     139             :  private:
     140           0 :   uint32_t block_id_ : block_id_bits;
     141           0 :   uint32_t grid_index_ : grid_index_bits;  // end first 32 bits
     142           0 :   uint32_t index_xi_ : max_refinement_level;
     143           0 :   uint32_t index_eta_ : max_refinement_level;
     144           0 :   uint32_t index_zeta_ : max_refinement_level;
     145           0 :   uint32_t empty_ : padding;
     146           0 :   uint32_t refinement_level_xi_ : refinement_bits;  // end second 32 bits
     147           0 :   uint32_t refinement_level_eta_ : refinement_bits;
     148           0 :   uint32_t refinement_level_zeta_ : refinement_bits;  // end third 32 bits
     149             : };
     150             : 
     151             : /// \cond
     152             : // macro that generate the pup operator for SegmentId
     153             : PUPbytes(ElementId<1>)      // NOLINT
     154             :     PUPbytes(ElementId<2>)  // NOLINT
     155             :     PUPbytes(ElementId<3>)  // NOLINT
     156             :     /// \endcond
     157             : 
     158             :     /// Output operator for ElementId.
     159             :     template <size_t VolumeDim>
     160           1 :     std::ostream& operator<<(std::ostream& os, const ElementId<VolumeDim>& id);
     161             : 
     162             : /// Equivalence operator for ElementId.
     163             : template <size_t VolumeDim>
     164             : bool operator==(const ElementId<VolumeDim>& lhs,
     165             :                 const ElementId<VolumeDim>& rhs);
     166             : 
     167             : /// Inequivalence operator for ElementId.
     168             : template <size_t VolumeDim>
     169             : bool operator!=(const ElementId<VolumeDim>& lhs,
     170             :                 const ElementId<VolumeDim>& rhs);
     171             : 
     172             : /// Define an ordering of element IDs first by grid index, then by block ID,
     173             : /// then by segment ID in each dimension in turn. In each dimension, segment IDs
     174             : /// are ordered first by refinement level (which will typically be the same when
     175             : /// comparing two element IDs), and second by index. There's no particular
     176             : /// reason for this choice of ordering. For applications such as distributing
     177             : /// elements among cores, orderings such as defined by
     178             : /// `domain::BlockZCurveProcDistribution` may be more appropriate.
     179             : template <size_t VolumeDim>
     180           1 : bool operator<(const ElementId<VolumeDim>& lhs,
     181             :                const ElementId<VolumeDim>& rhs);
     182             : 
     183             : template <size_t VolumeDim>
     184           0 : bool operator>(const ElementId<VolumeDim>& lhs,
     185             :                const ElementId<VolumeDim>& rhs) {
     186             :   return rhs < lhs;
     187             : }
     188             : template <size_t VolumeDim>
     189           0 : bool operator<=(const ElementId<VolumeDim>& lhs,
     190             :                 const ElementId<VolumeDim>& rhs) {
     191             :   return !(lhs > rhs);
     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             : 
     199             : /// @{
     200             : /// \brief Returns a bool if the element is the zeroth element in the domain.
     201             : ///
     202             : /// \details An element is considered to be the zeroth element if its ElementId
     203             : /// `id` has
     204             : /// 1. id.block_id() == 0
     205             : /// 2. All id.segment_ids() have SegmentId.index() == 0
     206             : /// 3. If the argument `grid_index` is specified, id.grid_index() == grid_index.
     207             : ///
     208             : /// This means that the only element in a domain that this function will return
     209             : /// `true` for is the element in the lower corner of Block0 of that domain. The
     210             : /// `grid_index` will determine which domain is used for the comparison. During
     211             : /// evolutions, only one domain will be active at a time so it doesn't make
     212             : /// sense to compare the `grid_index`. However, during an elliptic solve
     213             : /// when there are multiple grids, this `grid_index` is useful for specifying
     214             : /// only one element over all domains.
     215             : ///
     216             : /// This function is useful if you need a unique element in the domain because
     217             : /// only one element in the whole domain can be the zeroth element.
     218             : ///
     219             : /// \parblock
     220             : /// \warning If you have multiple grids and you don't specify the `grid_index`
     221             : /// argument, this function will return `true` for one element in every grid
     222             : /// and thus can't be used to determine a unique element in a simulation; only a
     223             : /// unique element in each grid.
     224             : /// \endparblock
     225             : /// \parblock
     226             : /// \warning If the domain is re-gridded, a different ElementId may represent
     227             : /// the zeroth element.
     228             : /// \endparblock
     229             : template <size_t Dim>
     230           1 : bool is_zeroth_element(const ElementId<Dim>& id,
     231             :                        const std::optional<size_t>& grid_index);
     232             : 
     233             : // This overload is added (instead of adding a default value for grid_index)
     234             : // in order to avoid adding DomainStructures as a dependency of Parallel
     235             : // by using a forward declaration in Parallel/DistributedObject.hpp
     236             : template <size_t Dim>
     237           1 : bool is_zeroth_element(const ElementId<Dim>& id);
     238             : /// @}
     239             : // ######################################################################
     240             : // INLINE DEFINITIONS
     241             : // ######################################################################
     242             : 
     243             : template <size_t VolumeDim>
     244           0 : size_t hash_value(const ElementId<VolumeDim>& id);
     245             : 
     246             : // clang-tidy: do not modify namespace std
     247             : namespace std {  // NOLINT
     248             : template <size_t VolumeDim>
     249             : struct hash<ElementId<VolumeDim>> {
     250             :   size_t operator()(const ElementId<VolumeDim>& id) const;
     251             : };
     252             : }  // namespace std
     253             : 
     254             : template <size_t VolumeDim>
     255           1 : inline bool operator==(const ElementId<VolumeDim>& lhs,
     256             :                        const ElementId<VolumeDim>& rhs) {
     257             :   return lhs.block_id() == rhs.block_id() and
     258             :          lhs.segment_ids() == rhs.segment_ids() and
     259             :          lhs.grid_index() == rhs.grid_index();
     260             : }
     261             : 
     262             : template <size_t VolumeDim>
     263           1 : inline bool operator!=(const ElementId<VolumeDim>& lhs,
     264             :                        const ElementId<VolumeDim>& rhs) {
     265             :   return not(lhs == rhs);
     266             : }

Generated by: LCOV version 1.14