SpECTRE Documentation Coverage Report
Current view: top level - Time - BoundaryHistory.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 14.6 % 89 13
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              : #pragma once
       5              : 
       6              : #include <boost/container/static_vector.hpp>
       7              : #include <cstddef>
       8              : #include <iosfwd>
       9              : #include <optional>
      10              : #include <tuple>
      11              : #include <type_traits>
      12              : #include <utility>
      13              : 
      14              : #include "DataStructures/CircularDeque.hpp"
      15              : #include "DataStructures/MathWrapper.hpp"
      16              : #include "Time/History.hpp"
      17              : #include "Time/TimeStepId.hpp"
      18              : #include "Utilities/Gsl.hpp"
      19              : #include "Utilities/StlBoilerplate.hpp"
      20              : #include "Utilities/TMPL.hpp"
      21              : 
      22              : /// \cond
      23              : namespace PUP {
      24              : class er;
      25              : }  // namespace PUP
      26              : /// \endcond
      27              : 
      28            1 : namespace TimeSteppers {
      29              : namespace BoundaryHistory_detail {
      30              : template <typename Data>
      31              : struct StepData {
      32              :   struct Entry {
      33              :     TimeStepId id;
      34              :     Data data;
      35              : 
      36              :     void pup(PUP::er& p);
      37              :   };
      38              : 
      39              :   size_t integration_order;
      40              :   // Unlike in History, the full step is the first entry, so we need
      41              :   // one more element.
      42              :   boost::container::static_vector<Entry, history_max_substeps + 1> substeps;
      43              : 
      44              :   void pup(PUP::er& p);
      45              : };
      46              : }  // namespace BoundaryHistory_detail
      47              : 
      48              : /// \ingroup TimeSteppersGroup
      49              : /// Access to the list of `TimeStepId`s in a `BoundaryHistory`.
      50              : ///
      51              : /// For simplicity of implementation, iterable-container access is not
      52              : /// provided for substeps within a step, but is instead provided
      53              : /// through additional methods on this class.
      54              : /// @{
      55            1 : class ConstBoundaryHistoryTimes
      56              :     : public stl_boilerplate::RandomAccessSequence<ConstBoundaryHistoryTimes,
      57              :                                                    const TimeStepId, false> {
      58              :  protected:
      59            0 :   ~ConstBoundaryHistoryTimes() = default;
      60              : 
      61              :  public:
      62            0 :   virtual size_t size() const = 0;
      63            0 :   virtual const TimeStepId& operator[](size_t n) const = 0;
      64            0 :   virtual const TimeStepId& operator[](
      65              :       const std::pair<size_t, size_t>& step_and_substep) const = 0;
      66            0 :   virtual size_t integration_order(size_t n) const = 0;
      67            0 :   virtual size_t integration_order(const TimeStepId& id) const = 0;
      68            0 :   virtual size_t number_of_substeps(size_t n) const = 0;
      69              :   /// This returns the same value for any substep of the same step.
      70            1 :   virtual size_t number_of_substeps(const TimeStepId& id) const = 0;
      71              : };
      72              : 
      73            0 : class MutableBoundaryHistoryTimes : public ConstBoundaryHistoryTimes {
      74              :  protected:
      75            0 :   ~MutableBoundaryHistoryTimes() = default;
      76              : 
      77              :  public:
      78              :   /// Remove the earliest step and its substeps.
      79            1 :   virtual void pop_front() const = 0;
      80            0 :   virtual void clear() const = 0;
      81              :   /// Remove all substeps for step \p n except for the step itself.
      82            1 :   virtual void clear_substeps(size_t n) const = 0;
      83              : };
      84              : /// @}
      85              : 
      86              : /// \ingroup TimeSteppersGroup
      87              : /// Type erased base class for evaluating BoundaryHistory couplings.
      88              : ///
      89              : /// The results are cached in the `BoundaryHistory` class.
      90              : template <typename UntypedCouplingResult>
      91            1 : class BoundaryHistoryEvaluator {
      92              :  public:
      93            0 :   virtual const UntypedCouplingResult& operator()(
      94              :       const TimeStepId& local_id, const TimeStepId& remote_id) const = 0;
      95              : 
      96              :  protected:
      97            0 :   ~BoundaryHistoryEvaluator() = default;
      98              : };
      99              : 
     100              : /// \ingroup TimeSteppersGroup
     101              : /// History data used by a TimeStepper for boundary integration.
     102              : ///
     103              : /// \tparam LocalData local data passed to the boundary coupling
     104              : /// \tparam RemoteData remote data passed to the boundary coupling
     105              : /// \tparam UntypedCouplingResult math_wrapper_type of cached boundary couplings
     106              : template <typename LocalData, typename RemoteData,
     107              :           typename UntypedCouplingResult>
     108            1 : class BoundaryHistory {
     109              :   static_assert(tmpl::list_contains_v<tmpl::list<MATH_WRAPPER_TYPES>,
     110              :                                       UntypedCouplingResult>);
     111              : 
     112              :  public:
     113            0 :   BoundaryHistory() = default;
     114            0 :   BoundaryHistory(const BoundaryHistory& other) = default;
     115            0 :   BoundaryHistory(BoundaryHistory&&) = default;
     116            0 :   BoundaryHistory& operator=(const BoundaryHistory& other) = default;
     117            0 :   BoundaryHistory& operator=(BoundaryHistory&&) = default;
     118            0 :   ~BoundaryHistory() = default;
     119              : 
     120              :   // Factored out of ConstSideAccess so that the base classes of
     121              :   // MutableSideAccess can have protected destructors.
     122              :   template <bool Local, bool Mutable>
     123            0 :   class SideAccessCommon
     124              :       : public tmpl::conditional_t<Mutable, MutableBoundaryHistoryTimes,
     125              :                                    ConstBoundaryHistoryTimes> {
     126              :    public:
     127            0 :     using MutableData = tmpl::conditional_t<Local, LocalData, RemoteData>;
     128            0 :     using Data = tmpl::conditional_t<Mutable, MutableData, const MutableData>;
     129              : 
     130            0 :     size_t size() const override;
     131            0 :     static constexpr size_t max_size() {
     132              :       return std::remove_cvref_t<decltype(std::declval<ConstSideAccess<Local>>()
     133              :                                               .parent_data())>::max_size();
     134              :     }
     135              : 
     136            0 :     const TimeStepId& operator[](size_t n) const override;
     137            0 :     const TimeStepId& operator[](
     138              :         const std::pair<size_t, size_t>& step_and_substep) const override;
     139              : 
     140            0 :     size_t integration_order(size_t n) const override;
     141            0 :     size_t integration_order(const TimeStepId& id) const override;
     142              : 
     143            0 :     size_t number_of_substeps(size_t n) const override;
     144            0 :     size_t number_of_substeps(const TimeStepId& id) const override;
     145              : 
     146              :     /// Access the data stored on the side.  When performed through a
     147              :     /// `MutableSideAccess`, these allow modification of the data.
     148              :     /// Performing such modifications likely invalidates the coupling
     149              :     /// cache for the associated `BoundaryHistory` object, which
     150              :     /// should be cleared.
     151              :     /// @{
     152            1 :     Data& data(size_t n) const;
     153            1 :     Data& data(const TimeStepId& id) const;
     154              :     /// @}
     155              : 
     156              :     /// Apply \p func to each entry.
     157              :     ///
     158              :     /// The function \p func must accept two arguments, one of type
     159              :     /// `const TimeStepId&` and a second of either type `const Data&`
     160              :     /// or `gsl::not_null<Data*>`, with the `not_null` version only
     161              :     /// available if this is a `MutableSideAccess`.  If \p func takes
     162              :     /// a `not_null`, it must return a `bool` indicating if it
     163              :     /// modified the entry.  If any entries are modified, the coupling
     164              :     /// cache of parent `BoundaryHistory` will be cleared.
     165              :     template <typename Func>
     166            1 :     void for_each(Func&& func) const;
     167              : 
     168              :    protected:
     169            0 :     ~SideAccessCommon() = default;
     170              : 
     171              :     tmpl::conditional_t<
     172              :         Mutable, CircularDeque<BoundaryHistory_detail::StepData<MutableData>>&,
     173              :         const CircularDeque<BoundaryHistory_detail::StepData<MutableData>>&>
     174            0 :     parent_data() const;
     175              : 
     176            0 :     auto& step_data(const TimeStepId& id) const;
     177              : 
     178            0 :     auto& entry(const TimeStepId& id) const;
     179              : 
     180            0 :     auto& entry(const std::pair<size_t, size_t>& step_and_substep) const;
     181              : 
     182            0 :     using StoredHistory =
     183              :         tmpl::conditional_t<Mutable, BoundaryHistory, const BoundaryHistory>;
     184            0 :     explicit SideAccessCommon(gsl::not_null<StoredHistory*> parent);
     185              : 
     186            0 :     gsl::not_null<StoredHistory*> parent_;
     187              :   };
     188              : 
     189              :   template <bool Local>
     190            0 :   class MutableSideAccess final : public SideAccessCommon<Local, true> {
     191              :    public:
     192            0 :     using Data = tmpl::conditional_t<Local, LocalData, RemoteData>;
     193              : 
     194            0 :     void pop_front() const override;
     195            0 :     void clear() const override;
     196            0 :     void clear_substeps(size_t n) const override;
     197              : 
     198            0 :     void insert(const TimeStepId& id, size_t integration_order,
     199              :                 Data data) const;
     200              : 
     201            0 :     void insert_initial(const TimeStepId& id, size_t integration_order,
     202              :                         Data data) const;
     203              : 
     204              :    private:
     205            0 :     friend class BoundaryHistory;
     206            0 :     explicit MutableSideAccess(gsl::not_null<BoundaryHistory*> parent);
     207              :   };
     208              : 
     209              :   template <bool Local>
     210            0 :   class ConstSideAccess final : public SideAccessCommon<Local, false> {
     211              :    private:
     212            0 :     friend class BoundaryHistory;
     213            0 :     explicit ConstSideAccess(gsl::not_null<const BoundaryHistory*> parent);
     214              :   };
     215              : 
     216            0 :   MutableSideAccess<true> local();
     217            0 :   ConstSideAccess<true> local() const;
     218              : 
     219            0 :   MutableSideAccess<false> remote();
     220            0 :   ConstSideAccess<false> remote() const;
     221              : 
     222              :  private:
     223              :   template <typename Coupling>
     224            0 :   class EvaluatorImpl final
     225              :       : public BoundaryHistoryEvaluator<UntypedCouplingResult> {
     226              :    public:
     227            0 :     const UntypedCouplingResult& operator()(
     228              :         const TimeStepId& local_id, const TimeStepId& remote_id) const override;
     229              : 
     230              :    private:
     231            0 :     friend class BoundaryHistory;
     232              : 
     233            0 :     EvaluatorImpl(const gsl::not_null<const BoundaryHistory*> parent,
     234              :                   Coupling coupling)
     235              :         : parent_(parent), coupling_(std::move(coupling)) {}
     236              : 
     237            0 :     gsl::not_null<const BoundaryHistory*> parent_;
     238            0 :     Coupling coupling_;
     239              :   };
     240              : 
     241              :  public:
     242              :   /// Obtain an object that can evaluate type-erased boundary
     243              :   /// couplings.
     244              :   ///
     245              :   /// The passed functor must take objects of types `LocalData` and
     246              :   /// `RemoteData` and return an object with math_wrapper_type
     247              :   /// `UntypedCouplingResult`.  Results are cached, so different calls
     248              :   /// to this function should pass equivalent couplings.
     249              :   template <typename Coupling>
     250            1 :   auto evaluator(Coupling&& coupling) const {
     251              :     return EvaluatorImpl<Coupling>(this, std::forward<Coupling>(coupling));
     252              :   }
     253              : 
     254              :   /// Clear the cached values.
     255              :   ///
     256              :   /// This is required after existing history entries that have been
     257              :   /// used in coupling calculations are mutated.
     258            1 :   void clear_coupling_cache();
     259              : 
     260            0 :   void pup(PUP::er& p);
     261              : 
     262              :   template <bool IncludeData>
     263            0 :   std::ostream& print(std::ostream& os, size_t padding_size = 0) const;
     264              : 
     265              :  private:
     266            0 :   void insert_local(const TimeStepId& id, size_t integration_order,
     267              :                     LocalData data);
     268            0 :   void insert_remote(const TimeStepId& id, size_t integration_order,
     269              :                      RemoteData data);
     270              : 
     271            0 :   void insert_initial_local(const TimeStepId& id, size_t integration_order,
     272              :                             LocalData data);
     273            0 :   void insert_initial_remote(const TimeStepId& id, size_t integration_order,
     274              :                              RemoteData data);
     275              : 
     276            0 :   void pop_local();
     277            0 :   void pop_remote();
     278              : 
     279            0 :   void clear_substeps_local(size_t n);
     280            0 :   void clear_substeps_remote(size_t n);
     281              : 
     282              :   std::tuple<std::optional<UntypedCouplingResult>&, const LocalData&,
     283              :              const RemoteData&>
     284            0 :   find_cache_entry(const TimeStepId& local_id,
     285              :                    const TimeStepId& remote_id) const;
     286              : 
     287            0 :   CircularDeque<BoundaryHistory_detail::StepData<LocalData>> local_data_{};
     288            0 :   CircularDeque<BoundaryHistory_detail::StepData<RemoteData>> remote_data_{};
     289              : 
     290              :   template <typename Data>
     291            0 :   using CouplingSubsteps =
     292              :       boost::container::static_vector<Data, history_max_substeps + 1>;
     293              : 
     294              :   // NOLINTNEXTLINE(spectre-mutable)
     295              :   mutable CircularDeque<CouplingSubsteps<
     296              :       CircularDeque<CouplingSubsteps<std::optional<UntypedCouplingResult>>>>>
     297            0 :       couplings_;
     298              : };
     299              : 
     300              : template <typename LocalData, typename RemoteData,
     301              :           typename UntypedCouplingResult>
     302              : template <bool Local, bool Mutable>
     303              : template <typename Func>
     304              : void BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::
     305              :     SideAccessCommon<Local, Mutable>::for_each(Func&& func) const {
     306              :   bool entries_changed = false;
     307              :   for (auto& step : parent_data()) {
     308              :     for (auto& substep : step.substeps) {
     309              :       if constexpr (std::is_invocable_v<Func&, const TimeStepId&,
     310              :                                         const Data&>) {
     311              :         func(std::as_const(substep.id), std::as_const(substep.data));
     312              :       } else {
     313              :         static_assert(Mutable,
     314              :                       "Cannot perform mutating for_each on a ConstSideAccess");
     315              :         if (func(std::as_const(substep.id), make_not_null(&substep.data))) {
     316              :           entries_changed = true;
     317              :         }
     318              :       }
     319              :     }
     320              :   }
     321              :   if constexpr (Mutable) {
     322              :     if (entries_changed) {
     323              :       // A minor optimization would be to only clear the cache entries
     324              :       // that have actually been invalidated, but most things that
     325              :       // modify the history modify all the entries.
     326              :       parent_->clear_coupling_cache();
     327              :     }
     328              :   }
     329              : }
     330              : 
     331              : template <typename LocalData, typename RemoteData,
     332              :           typename UntypedCouplingResult>
     333              : template <typename Coupling>
     334              : auto BoundaryHistory<LocalData, RemoteData, UntypedCouplingResult>::
     335              :     EvaluatorImpl<Coupling>::operator()(const TimeStepId& local_id,
     336              :                                         const TimeStepId& remote_id) const
     337              :     -> const UntypedCouplingResult& {
     338              :   const auto [coupling_entry, local_data, remote_data] =
     339              :       parent_->find_cache_entry(local_id, remote_id);
     340              :   if (not coupling_entry.has_value()) {
     341              :     auto new_entry = coupling_(local_data, remote_data);
     342              :     static_assert(std::is_same_v<math_wrapper_type<decltype(new_entry)>,
     343              :                                  UntypedCouplingResult>);
     344              :     coupling_entry.emplace(into_math_wrapper_type(std::move(new_entry)));
     345              :   }
     346              :   return *coupling_entry;
     347              : }
     348              : 
     349              : template <typename LocalData, typename RemoteData,
     350              :           typename UntypedCouplingResult>
     351            0 : std::ostream& operator<<(std::ostream& os,
     352              :                          const BoundaryHistory<LocalData, RemoteData,
     353              :                                                UntypedCouplingResult>& history);
     354              : }  // namespace TimeSteppers
     355              : 
     356              : // Documentation for macro defined in the tpp file
     357              : #ifdef SPECTRE_DOXYGEN_INVOKED
     358              : /// \ingroup TimeSteppersGroup
     359              : /// Explicitly instantiate BoundaryHistory and helpers.  Should be
     360              : /// called with the template arguments for `BoundaryHistory`.
     361            1 : #define INSTANTIATE_BOUNDARY_HISTORY(...) UNSPECIFIED
     362              : #endif  // SPECTRE_DOXYGEN_INVOKED
        

Generated by: LCOV version 2.0-1