SpECTRE Documentation Coverage Report
Current view: top level - Evolution/DiscontinuousGalerkin - AtomicInboxBoundaryData.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 30.0 % 20 6
Test Date: 2026-10-22 17:29:42
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 2.0-1