SpECTRE Documentation Coverage Report
Current view: top level - Evolution/DiscontinuousGalerkin - AtomicInboxBoundaryData.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 6 20 30.0 %
Date: 2025-12-05 05:03:31
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : // This file is tested in Test_InboxTags.cpp
       5             : 
       6             : #pragma once
       7             : 
       8             : #include <atomic>
       9             : #include <cstddef>
      10             : #include <map>
      11             : #include <tuple>
      12             : 
      13             : #include "Domain/Structure/DirectionalId.hpp"
      14             : #include "Domain/Structure/DirectionalIdMap.hpp"
      15             : #include "Domain/Structure/MaxNumberOfNeighbors.hpp"
      16             : #include "Evolution/DiscontinuousGalerkin/BoundaryData.hpp"
      17             : #include "Parallel/StaticSpscQueue.hpp"
      18             : #include "Time/TimeStepId.hpp"
      19             : 
      20             : /// \cond
      21             : namespace PUP {
      22             : class er;
      23             : }  // namespace PUP
      24             : /// \endcond
      25             : 
      26             : namespace evolution::dg {
      27             : /*!
      28             :  * \brief Holds the data in the different directions for the nodegroup
      29             :  * `DgElementArray` implementation.
      30             :  *
      31             :  * The reason for this class is to reduce contention between cores and to
      32             :  * allow the use of a Single-Producer-Single-Consumer (SPSC) queue instead of
      33             :  * an MPMC queue. This has significant performance improvements since it
      34             :  * drastically reduces contention.
      35             :  *
      36             :  * \warning Only `AtomicInboxBoundaryData` with zero messages can be
      37             :  * move constructed or serialized.  This is necessary in order to be
      38             :  * able to serialize a
      39             :  * `std::unordered_map<Key,AtomicInboxBoundaryData>`.
      40             :  */
      41             : template <size_t Dim>
      42           1 : struct AtomicInboxBoundaryData {
      43           0 :   using stored_type = evolution::dg::BoundaryData<Dim>;
      44           0 :   AtomicInboxBoundaryData() = default;
      45           0 :   AtomicInboxBoundaryData(const AtomicInboxBoundaryData&) = delete;
      46           0 :   AtomicInboxBoundaryData& operator=(const AtomicInboxBoundaryData&) = delete;
      47           0 :   AtomicInboxBoundaryData(AtomicInboxBoundaryData&& rhs) noexcept;
      48           0 :   AtomicInboxBoundaryData& operator=(AtomicInboxBoundaryData&&) noexcept =
      49             :       delete;
      50           0 :   ~AtomicInboxBoundaryData() = default;
      51             : 
      52             :   /*!
      53             :    * Computes the 1d index into the `boundary_data_in_directions` array
      54             :    * for a specific `directional_id` that has been re-oriented using the
      55             :    * `OrientationMap` to be put in the same block frame as the element that is
      56             :    * receiving the data (i.e. that whose inbox this is being inserted into).
      57             :    *
      58             :    * The hash is computed as
      59             :    * \f{align}{
      60             :    * 2^D d + 2^{D-1} s + e
      61             :    * \f}
      62             :    * where \f$D\f$ is the number of spatial dimensions, \f$d\f$ is the logical
      63             :    * dimension of the direction to the neighbor from the element whose inbox
      64             :    * this is, \f$s\f$ is the side in the logical dimension \f$d\f$ with a value
      65             :    * of 1 for upper and 0 for lower, and \f$e\f$ is a hash of the index of the
      66             :    * `SegmentId`'s of the neighbor's `ElementId` for the dimensions other than
      67             :    * \f$d\f$. In particular: for \f$d=1\f$, \f$e\f$ is 0 (1) if
      68             :    * the `SegmentId` index along the face is even (odd); and for \f$d = 3\f$
      69             :    * \f$e\f$ is 0 (1, 2, 3) if the `SegmentId` indices along the face are both
      70             :    * even (lower dim odd, higher dim odd, both dims odd). The element segment
      71             :    * hash is computed as the logical `and` of the `SegmentID`'s index in that
      72             :    * direction, left shifted by which direction on the face it is.
      73             :    */
      74           1 :   static size_t index(const DirectionalId<Dim>& directional_id);
      75             : 
      76             :   /*!
      77             :    * Moves data from the SPSC queues into the `messages` map.
      78             :    */
      79           1 :   void collect_messages();
      80             : 
      81             :   /*!
      82             :    * Set a lower bound on the number of messages required for the
      83             :    * algorithm to make progress since the most recent call to
      84             :    * `collect_messages`.  After that number of new messages have been
      85             :    * received, `BoundaryCorrectionAndGhostCellsInbox` will restart
      86             :    * the algorithm.
      87             :    *
      88             :    * \return whether enough messages have been received.
      89             :    */
      90           1 :   bool set_missing_messages(size_t count);
      91             : 
      92           0 :   void pup(PUP::er& p);
      93             : 
      94             :   // We use 20 entries in the SPSC under the assumption that each neighbor
      95             :   // will never insert more than 20 entries before the element uses
      96             :   // them. While in practice a smaller buffer could be used, this is to
      97             :   // safeguard against future features.
      98             :   std::array<Parallel::StaticSpscQueue<
      99             :                  std::tuple<::TimeStepId, stored_type, DirectionalId<Dim>>, 20>,
     100             :              maximum_number_of_neighbors(Dim)>
     101           0 :       boundary_data_in_directions{};
     102           0 :   std::map<TimeStepId, DirectionalIdMap<Dim, stored_type>> messages{};
     103           0 :   std::atomic_int missing_messages{};
     104             : 
     105             :   // The number of messages in the SPSC queues is
     106             :   // passed_missing_messages - missing_messages - processed_messages.
     107             :   // The various manipulations change these fields as follows:
     108             :   //
     109             :   // New message => --missing_messages (by insert_into_inbox)
     110             :   // collect_messages => processed_messages += num unqueued messages
     111             :   // set_missing_messages(count) =>
     112             :   //   missing_messages += count + processed_messages - passed_missing_messages
     113             :   //   processed_messages = 0
     114             :   //   passed_missing_messages = count
     115             :   //
     116             :   // The processed_messages field exists (rather than just including
     117             :   // it in missing_messages) so that missing_messages is positive if
     118             :   // and only if the element is blocked on messages in this queue.
     119           0 :   int processed_messages{};
     120           0 :   int passed_missing_messages{};
     121             : };
     122             : 
     123             : /// \brief `std::true` if `T` is a `AtomicInboxBoundaryData`
     124             : template <typename T>
     125           1 : struct is_atomic_inbox_boundary_data : std::false_type {};
     126             : 
     127             : /// \cond
     128             : template <size_t Dim>
     129             : struct is_atomic_inbox_boundary_data<AtomicInboxBoundaryData<Dim>>
     130             :     : std::true_type {};
     131             : /// \endcond
     132             : 
     133             : /// \brief `true` if `T` is a `AtomicInboxBoundaryData`
     134             : template <typename T>
     135           1 : constexpr size_t is_atomic_inbox_boundary_data_v =
     136             :     is_atomic_inbox_boundary_data<T>::value;
     137             : }  // namespace evolution::dg

Generated by: LCOV version 1.14