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

Generated by: LCOV version 1.14