SpECTRE Documentation Coverage Report
Current view: top level - Time - BoundaryHistory.hpp Hit Total Coverage
Commit: 1e29a35ad8559408f21493dc5db8a49a237bb2f0 Lines: 13 89 14.6 %
Date: 2026-03-31 22:27:51
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 1.14