SpECTRE Documentation Coverage Report
Current view: top level - Time - History.hpp Hit Total Coverage
Commit: a8efe75339f4781ca06d43fed14c40144d5e8a08 Lines: 39 101 38.6 %
Date: 2024-10-17 21:19:21
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 <optional>
       9             : #include <pup.h>
      10             : #include <type_traits>
      11             : #include <utility>
      12             : 
      13             : #include "DataStructures/DataBox/PrefixHelpers.hpp"
      14             : #include "DataStructures/DataBox/Prefixes.hpp"
      15             : #include "DataStructures/MathWrapper.hpp"
      16             : #include "DataStructures/StaticDeque.hpp"
      17             : #include "Time/TimeStepId.hpp"
      18             : #include "Utilities/ContainsAllocations.hpp"
      19             : #include "Utilities/ErrorHandling/Assert.hpp"
      20             : #include "Utilities/ErrorHandling/Error.hpp"
      21             : #include "Utilities/Gsl.hpp"
      22             : #include "Utilities/Serialization/PupBoost.hpp"
      23             : #include "Utilities/Serialization/PupStlCpp17.hpp"
      24             : #include "Utilities/StdHelpers.hpp"
      25             : #include "Utilities/StlBoilerplate.hpp"
      26             : #include "Utilities/TMPL.hpp"
      27             : 
      28             : namespace TimeSteppers {
      29             : 
      30             : /// Largest number of past steps supported by the time stepper
      31             : /// `History`.  Corresponds to the `number_of_past_steps()` method of
      32             : /// `TimeStepper`.  AdamsBashforth with order 8 has the largest
      33             : /// requirement.
      34           1 : constexpr size_t history_max_past_steps = 7;
      35             : /// Largest number of substeps supported by the time stepper
      36             : /// `History`.  Corresponds to the `number_of_substeps()` and
      37             : /// `number_of_substeps_for_error()` methods of `TimeStepper`.
      38             : /// DormandPrince5 with an error estimate and Rk5Owren have the
      39             : /// largest requirement.
      40           1 : constexpr size_t history_max_substeps = 6;
      41             : 
      42             : /// \ingroup TimeSteppersGroup
      43             : /// Entry in the time-stepper history, in type-erased form.
      44             : ///
      45             : /// The history access classes do not provide mutable references to
      46             : /// these structs, so they cannot be used to modify history data.
      47             : ///
      48             : /// This struct mirrors the typed `StepRecord` struct.  See that for
      49             : /// details.
      50             : ///
      51             : /// \tparam T One of the types in \ref MATH_WRAPPER_TYPES
      52             : template <typename T>
      53           1 : struct UntypedStepRecord {
      54           0 :   TimeStepId time_step_id;
      55           0 :   std::optional<T> value;
      56           0 :   T derivative;
      57             : 
      58             :   static_assert(tmpl::list_contains_v<tmpl::list<MATH_WRAPPER_TYPES>, T>);
      59             : };
      60             : 
      61             : template <typename T>
      62           0 : bool operator==(const UntypedStepRecord<T>& a, const UntypedStepRecord<T>& b);
      63             : 
      64             : template <typename T>
      65           0 : bool operator!=(const UntypedStepRecord<T>& a, const UntypedStepRecord<T>& b);
      66             : 
      67             : #if defined(__GNUC__) and not defined(__clang__)
      68             : #pragma GCC diagnostic push
      69             : #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
      70             : #endif  // defined(__GNUC__) and not defined(__clang__)
      71             : /// \ingroup TimeSteppersGroup
      72             : /// Access to the history data used by a TimeStepper in type-erased
      73             : /// form.  Obtain an instance with `History::untyped()`.
      74             : ///
      75             : /// The methods mirror similar ones in `History`.  See that class for
      76             : /// details.
      77             : ///
      78             : /// \tparam T One of the types in \ref MATH_WRAPPER_TYPES
      79             : template <typename T>
      80           1 : class ConstUntypedHistory
      81             :     : public stl_boilerplate::RandomAccessSequence<
      82             :           ConstUntypedHistory<T>, const UntypedStepRecord<T>, false> {
      83             :   static_assert(tmpl::list_contains_v<tmpl::list<MATH_WRAPPER_TYPES>, T>);
      84             : 
      85             :  protected:
      86           0 :   ConstUntypedHistory() = default;
      87           0 :   ConstUntypedHistory(const ConstUntypedHistory&) = default;
      88           0 :   ConstUntypedHistory(ConstUntypedHistory&&) = default;
      89           0 :   ConstUntypedHistory& operator=(const ConstUntypedHistory&) = default;
      90           0 :   ConstUntypedHistory& operator=(ConstUntypedHistory&&) = default;
      91           0 :   ~ConstUntypedHistory() = default;
      92             : 
      93             :  public:
      94           0 :   using WrapperType = T;
      95             : 
      96             :   /// \cond
      97             :   class UntypedSubsteps
      98             :       : public stl_boilerplate::RandomAccessSequence<
      99             :             UntypedSubsteps, const UntypedStepRecord<T>, true> {
     100             :    public:
     101             :     UntypedSubsteps() = delete;
     102             :     UntypedSubsteps(const UntypedSubsteps&) = delete;
     103             :     UntypedSubsteps(UntypedSubsteps&&) = default;
     104             :     UntypedSubsteps& operator=(const UntypedSubsteps&) = delete;
     105             :     UntypedSubsteps& operator=(UntypedSubsteps&&) = default;
     106             :     ~UntypedSubsteps() = default;
     107             : 
     108             :     using WrapperType = T;
     109             : 
     110             :     size_t size() const;
     111             :     size_t max_size() const;
     112             : 
     113             :     const UntypedStepRecord<T>& operator[](const size_t index) const;
     114             : 
     115             :    private:
     116             :     friend ConstUntypedHistory;
     117             :     explicit UntypedSubsteps(const ConstUntypedHistory& history);
     118             : 
     119             :     gsl::not_null<const ConstUntypedHistory*> history_;
     120             :   };
     121             :   /// \endcond
     122             : 
     123           0 :   virtual size_t integration_order() const = 0;
     124             : 
     125           0 :   virtual size_t size() const = 0;
     126           0 :   virtual size_t max_size() const = 0;
     127             : 
     128           0 :   virtual const UntypedStepRecord<T>& operator[](size_t index) const = 0;
     129             : 
     130           0 :   virtual const UntypedStepRecord<T>& operator[](
     131             :       const TimeStepId& id) const = 0;
     132             : 
     133           0 :   virtual bool at_step_start() const = 0;
     134             : 
     135           0 :   UntypedSubsteps substeps() const;
     136             : 
     137             :  private:
     138           0 :   friend UntypedSubsteps;
     139             :   virtual const boost::container::static_vector<UntypedStepRecord<WrapperType>,
     140             :                                                 history_max_substeps>&
     141           0 :   substep_values() const = 0;
     142             : };
     143             : 
     144             : /// \ingroup TimeSteppersGroup
     145             : /// Mutable access to the history data used by a TimeStepper in
     146             : /// type-erased form.  Obtain an instance with `History::untyped()`.
     147             : ///
     148             : /// Data cannot be inserted or modified through the type-erased
     149             : /// interface.  The only mutability exposed is the ability to delete
     150             : /// data.
     151             : ///
     152             : /// The methods mirror similar ones in `History`.  See that class for
     153             : /// details.
     154             : ///
     155             : /// \tparam T One of the types in \ref MATH_WRAPPER_TYPES
     156             : template <typename T>
     157           1 : class MutableUntypedHistory : public ConstUntypedHistory<T> {
     158             :   static_assert(tmpl::list_contains_v<tmpl::list<MATH_WRAPPER_TYPES>, T>);
     159             : 
     160             :  protected:
     161           0 :   MutableUntypedHistory() = default;
     162           0 :   MutableUntypedHistory(const MutableUntypedHistory&) = default;
     163           0 :   MutableUntypedHistory(MutableUntypedHistory&&) = default;
     164           0 :   MutableUntypedHistory& operator=(const MutableUntypedHistory&) = default;
     165           0 :   MutableUntypedHistory& operator=(MutableUntypedHistory&&) = default;
     166           0 :   ~MutableUntypedHistory() = default;
     167             : 
     168             :  public:
     169           0 :   virtual void discard_value(const TimeStepId& id_to_discard) const = 0;
     170             : 
     171           0 :   virtual void pop_front() const = 0;
     172             : 
     173           0 :   virtual void clear_substeps() const = 0;
     174             : };
     175             : #if defined(__GNUC__) and not defined(__clang__)
     176             : #pragma GCC diagnostic pop
     177             : #endif  // defined(__GNUC__) and not defined(__clang__)
     178             : 
     179             : /// \ingroup TimeSteppersGroup
     180             : /// Data in an entry of the time-stepper history.
     181             : ///
     182             : /// The `value` field may be empty if the value has been discarded to
     183             : /// save memory.  See `History::discard_value` and
     184             : /// `ConstUntypedHistory::discard_value`.
     185             : template <typename Vars>
     186           1 : struct StepRecord {
     187           0 :   using DerivVars = db::prefix_variables<::Tags::dt, Vars>;
     188             : 
     189           0 :   TimeStepId time_step_id;
     190           0 :   std::optional<Vars> value;
     191           0 :   DerivVars derivative;
     192             : 
     193             :   // NOLINTNEXTLINE(google-runtime-references)
     194           0 :   void pup(PUP::er& p);
     195             : 
     196           0 :   std::ostream& print(std::ostream& os) const;
     197             : };
     198             : 
     199             : template <typename Vars>
     200             : void StepRecord<Vars>::pup(PUP::er& p) {
     201             :   p | time_step_id;
     202             :   p | value;
     203             :   p | derivative;
     204             : }
     205             : 
     206             : template <typename Vars>
     207             : std::ostream& StepRecord<Vars>::print(std::ostream& os) const {
     208             :   using ::operator<<;
     209             :   os << "TimeStepId: " << time_step_id << "\n";
     210             :   os << "Value: " << value << "\n";
     211             :   os << "Derivative: " << derivative << "\n";
     212             :   return os;
     213             : }
     214             : 
     215             : template <typename Vars>
     216           0 : bool operator==(const StepRecord<Vars>& a, const StepRecord<Vars>& b) {
     217             :   return a.time_step_id == b.time_step_id and a.value == b.value and
     218             :          a.derivative == b.derivative;
     219             : }
     220             : 
     221             : template <typename Vars>
     222           0 : bool operator!=(const StepRecord<Vars>& a, const StepRecord<Vars>& b) {
     223             :   return not(a == b);
     224             : }
     225             : 
     226             : /// \cond
     227             : template <typename Vars>
     228             : class History;
     229             : /// \endcond
     230             : 
     231             : namespace History_detail {
     232             : // Find a record from either the steps or substeps.  This takes the
     233             : // arguments it does so it can be used to find both const and
     234             : // non-const values from both typed and untyped histories.
     235             : template <typename History>
     236             : decltype(auto) find_record(History&& history, const TimeStepId& id) {
     237             :   const size_t substep = id.substep();
     238             :   if (substep == 0) {
     239             :     for (size_t i = 0; i < history.size(); ++i) {
     240             :       auto& record = history[i];
     241             :       if (record.time_step_id == id) {
     242             :         return record;
     243             :       }
     244             :     }
     245             :     ERROR(id << " not present");
     246             :   } else {
     247             :     ASSERT(substep - 1 < history.substeps().size(), id << " not present");
     248             :     auto& record = history.substeps()[substep - 1];
     249             :     ASSERT(record.time_step_id == id, id << " not present");
     250             :     return record;
     251             :   }
     252             : }
     253             : 
     254             : #if defined(__GNUC__) and not defined(__clang__)
     255             : #pragma GCC diagnostic push
     256             : #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
     257             : #endif  // defined(__GNUC__) and not defined(__clang__)
     258             : template <typename UntypedBase>
     259             : class UntypedAccessCommon : public UntypedBase {
     260             :  protected:
     261             :   UntypedAccessCommon() = delete;
     262             :   UntypedAccessCommon(const UntypedAccessCommon&) = delete;
     263             :   UntypedAccessCommon(UntypedAccessCommon&&) = default;
     264             :   UntypedAccessCommon& operator=(const UntypedAccessCommon&) = delete;
     265             :   // Can't move-assign non-owning DataVectors.
     266             :   UntypedAccessCommon& operator=(UntypedAccessCommon&&) = delete;
     267             :   ~UntypedAccessCommon() = default;
     268             : 
     269             :  public:
     270             :   using WrapperType = typename UntypedBase::WrapperType;
     271             : 
     272             :   size_t integration_order() const override;
     273             : 
     274             :   size_t size() const override;
     275             :   size_t max_size() const override;
     276             : 
     277             :   const UntypedStepRecord<WrapperType>& operator[](
     278             :       const size_t index) const override;
     279             :   const UntypedStepRecord<WrapperType>& operator[](
     280             :       const TimeStepId& id) const override;
     281             : 
     282             :   bool at_step_start() const override;
     283             : 
     284             :  protected:
     285             :   template <typename Vars>
     286             :   UntypedAccessCommon(const History<Vars>& history) {
     287             :     reinitialize(history);
     288             :   }
     289             : 
     290             :   template <typename Vars>
     291             :   void reinitialize(const History<Vars>& history) const {
     292             :     // This class is presenting a view.  Regenerating its internal
     293             :     // representation after a change to the parent structure is an
     294             :     // implementation detail.
     295             :     auto* const mutable_this = const_cast<UntypedAccessCommon*>(this);
     296             :     mutable_this->integration_order_ = history.integration_order();
     297             :     mutable_this->step_values_.clear();
     298             :     for (const StepRecord<Vars>& record : history) {
     299             :       mutable_this->step_values_.push_back(make_untyped(record));
     300             :     }
     301             :     mutable_this->substep_values_.clear();
     302             :     for (const StepRecord<Vars>& record : history.substeps()) {
     303             :       mutable_this->substep_values_.push_back(make_untyped(record));
     304             :     }
     305             :   }
     306             : 
     307             :  private:
     308             :   const boost::container::static_vector<UntypedStepRecord<WrapperType>,
     309             :                                         history_max_substeps>&
     310             :   substep_values() const override;
     311             : 
     312             :   template <typename Vars>
     313             :   static UntypedStepRecord<WrapperType> make_untyped(
     314             :       const StepRecord<Vars>& record) {
     315             :     // This class only exposes these records as const references, so
     316             :     // it is OK if we break non-allocating references to the original
     317             :     // data by moving out of the MathWrappers since you can't modify
     318             :     // through them.
     319             :     return {record.time_step_id,
     320             :             record.value.has_value() ? std::optional{const_cast<WrapperType&&>(
     321             :                                            *make_math_wrapper(*record.value))}
     322             :                                      : std::nullopt,
     323             :             const_cast<WrapperType&&>(*make_math_wrapper(record.derivative))};
     324             :   }
     325             : 
     326             :   size_t integration_order_{};
     327             :   // static_vector never reallocates, so storing non-owning
     328             :   // DataVectors is safe.
     329             :   boost::container::static_vector<UntypedStepRecord<WrapperType>,
     330             :                                   history_max_past_steps + 2>
     331             :       step_values_{};
     332             :   boost::container::static_vector<UntypedStepRecord<WrapperType>,
     333             :                                   history_max_substeps>
     334             :       substep_values_{};
     335             : };
     336             : #if defined(__GNUC__) and not defined(__clang__)
     337             : #pragma GCC diagnostic pop
     338             : #endif  // defined(__GNUC__) and not defined(__clang__)
     339             : }  // namespace History_detail
     340             : 
     341             : /// \ingroup TimeSteppersGroup
     342             : /// The past-time data used by TimeStepper classes to update the
     343             : /// evolved variables.
     344             : ///
     345             : /// This class exposes an STL-like container interface for accessing
     346             : /// the step (not substep) `StepRecord` data.  The records can be
     347             : /// freely modified through this interface, although modifying the
     348             : /// `time_step_id` field is generally inadvisable.
     349             : ///
     350             : /// This class is designed to minimize the number of memory
     351             : /// allocations performed during a step, and so caches discarded
     352             : /// entries for reuse if they contain dynamic allocations.  During
     353             : /// steady-state operation, this class will perform no heap
     354             : /// allocations.  If `Vars` and the associated `DerivVars` do not
     355             : /// allocate internally, then this class will perform no heap
     356             : /// allocations under any circumstances.
     357             : template <typename Vars>
     358           1 : class History
     359             :     : public stl_boilerplate::RandomAccessSequence<History<Vars>,
     360             :                                                    StepRecord<Vars>, false> {
     361             :  public:
     362           0 :   using DerivVars = db::prefix_variables<::Tags::dt, Vars>;
     363             : 
     364           0 :   History() = default;
     365           0 :   explicit History(const size_t integration_order)
     366             :       : integration_order_(integration_order) {}
     367           0 :   History(const History& other);
     368           0 :   History(History&&) = default;
     369           0 :   History& operator=(const History& other);
     370           0 :   History& operator=(History&&) = default;
     371             : 
     372             :   /// The wrapped type presented by the type-erased history.  One of
     373             :   /// the types in \ref MATH_WRAPPER_TYPES.
     374           1 :   using UntypedVars = math_wrapper_type<Vars>;
     375             : 
     376             :   /// \cond
     377             :   // This wrapper around UntypedAccessCommon exists because we want
     378             :   // the special members of that to be protected, and we want this to
     379             :   // be final.
     380             :   class ConstUntypedAccess final : public History_detail::UntypedAccessCommon<
     381             :                                        ConstUntypedHistory<UntypedVars>> {
     382             :    public:
     383             :     ConstUntypedAccess() = delete;
     384             :     ConstUntypedAccess(const ConstUntypedAccess&) = delete;
     385             :     ConstUntypedAccess(ConstUntypedAccess&&) = default;
     386             :     ConstUntypedAccess& operator=(const ConstUntypedAccess&) = delete;
     387             :     ConstUntypedAccess& operator=(ConstUntypedAccess&&) = default;
     388             :     ~ConstUntypedAccess() = default;
     389             : 
     390             :    private:
     391             :     friend History;
     392             : 
     393             :     explicit ConstUntypedAccess(const History& history)
     394             :         : History_detail::UntypedAccessCommon<ConstUntypedHistory<UntypedVars>>(
     395             :               history) {}
     396             :   };
     397             : 
     398             :   class MutableUntypedAccess final : public History_detail::UntypedAccessCommon<
     399             :                                          MutableUntypedHistory<UntypedVars>> {
     400             :    public:
     401             :     MutableUntypedAccess() = delete;
     402             :     MutableUntypedAccess(const MutableUntypedAccess&) = delete;
     403             :     MutableUntypedAccess(MutableUntypedAccess&&) = default;
     404             :     MutableUntypedAccess& operator=(const MutableUntypedAccess&) = delete;
     405             :     MutableUntypedAccess& operator=(MutableUntypedAccess&&) = default;
     406             :     ~MutableUntypedAccess() = default;
     407             : 
     408             :     void discard_value(const TimeStepId& id_to_discard) const override {
     409             :       history_->discard_value(id_to_discard);
     410             :       this->reinitialize(*history_);
     411             :     }
     412             : 
     413             :     void pop_front() const override {
     414             :       history_->pop_front();
     415             :       this->reinitialize(*history_);
     416             :     }
     417             : 
     418             :     void clear_substeps() const override {
     419             :       history_->clear_substeps();
     420             :       this->reinitialize(*history_);
     421             :     }
     422             : 
     423             :    private:
     424             :     friend History;
     425             : 
     426             :     explicit MutableUntypedAccess(const gsl::not_null<History*> history)
     427             :         : History_detail::UntypedAccessCommon<
     428             :               MutableUntypedHistory<UntypedVars>>(*history),
     429             :           history_(history) {}
     430             : 
     431             :    private:
     432             :     gsl::not_null<History*> history_;
     433             :   };
     434             : 
     435             :   class ConstSubsteps : public stl_boilerplate::RandomAccessSequence<
     436             :                             ConstSubsteps, const StepRecord<Vars>, true> {
     437             :    public:
     438             :     ConstSubsteps() = delete;
     439             :     ConstSubsteps(const ConstSubsteps&) = delete;
     440             :     ConstSubsteps(ConstSubsteps&&) = default;
     441             :     ConstSubsteps& operator=(const ConstSubsteps&) = delete;
     442             :     ConstSubsteps& operator=(ConstSubsteps&&) = default;
     443             :     ~ConstSubsteps() = default;
     444             : 
     445             :     size_t size() const { return history_->substep_values_.size(); }
     446             :     static constexpr size_t max_size() { return history_max_substeps; }
     447             : 
     448             :     const StepRecord<Vars>& operator[](const size_t index) const {
     449             :       ASSERT(index < size(),
     450             :              "Requested substep " << index << " but only have " << size());
     451             :       return history_->substep_values_[index];
     452             :     }
     453             : 
     454             :    private:
     455             :     friend History;
     456             :     explicit ConstSubsteps(const History& history) : history_(&history) {}
     457             : 
     458             :     gsl::not_null<const History*> history_;
     459             :   };
     460             : 
     461             :   class MutableSubsteps
     462             :       : public stl_boilerplate::RandomAccessSequence<MutableSubsteps,
     463             :                                                      StepRecord<Vars>, true> {
     464             :    public:
     465             :     MutableSubsteps() = delete;
     466             :     MutableSubsteps(const MutableSubsteps&) = delete;
     467             :     MutableSubsteps(MutableSubsteps&&) = default;
     468             :     MutableSubsteps& operator=(const MutableSubsteps&) = delete;
     469             :     MutableSubsteps& operator=(MutableSubsteps&&) = default;
     470             :     ~MutableSubsteps() = default;
     471             : 
     472             :     size_t size() const { return history_->substep_values_.size(); }
     473             :     static constexpr size_t max_size() { return history_max_substeps; }
     474             : 
     475             :     StepRecord<Vars>& operator[](const size_t index) const {
     476             :       ASSERT(index < size(),
     477             :              "Requested substep " << index << " but only have " << size());
     478             :       return history_->substep_values_[index];
     479             :     }
     480             : 
     481             :    private:
     482             :     friend History;
     483             :     explicit MutableSubsteps(const gsl::not_null<History*> history)
     484             :         : history_(history) {}
     485             : 
     486             :     gsl::not_null<History*> history_;
     487             :   };
     488             :   /// \endcond
     489             : 
     490             :   /// Immutable, type-erased access to the history.  This method
     491             :   /// returns a class derived from `ConstUntypedHistory<UntypedVars>`.
     492             :   /// Any modifications to the History class invalidate the object
     493             :   /// returned by this function.
     494           1 :   ConstUntypedAccess untyped() const { return ConstUntypedAccess(*this); }
     495             : 
     496             :   /// Mutable, type-erased access to the history.  This method returns
     497             :   /// a class derived from `MutableUntypedHistory<UntypedVars>`.  Any
     498             :   /// modifications to the History class invalidate the object
     499             :   /// returned by this function, except for modifications performed
     500             :   /// through the object itself.
     501           1 :   MutableUntypedAccess untyped() { return MutableUntypedAccess(this); }
     502             : 
     503             :   /// Get or set the order the time stepper is running at.  Many time
     504             :   /// steppers expect this to have a particular value.  This has no
     505             :   /// effect on the storage of past data.
     506             :   /// @{
     507           1 :   size_t integration_order() const { return integration_order_; }
     508           1 :   void integration_order(const size_t new_integration_order) {
     509             :     integration_order_ = new_integration_order;
     510             :   }
     511             :   /// @}
     512             : 
     513             :   /// Type and value used to indicate that a record is to be created
     514             :   /// without the `value` field set.
     515             :   /// @{
     516           1 :   struct NoValue {};
     517           0 :   static constexpr NoValue no_value{};
     518             :   /// @}
     519             : 
     520             :   /// Insert a new entry into the history.  It will be inserted as a
     521             :   /// step or substep as appropriate.
     522             :   ///
     523             :   /// The supplied `time_step_id` must be later than the current
     524             :   /// latest entry, and if the substep of `time_step_id` is nonzero,
     525             :   /// the id must be consistent with the existing data for the current
     526             :   /// step.
     527             :   ///
     528             :   /// If the constant `History::no_value` is passed, the created
     529             :   /// record will not have its `value` field set.  This is useful for
     530             :   /// histories other than the history of the primary evolution
     531             :   /// integration, such as the implicit portion of an IMEX evolution.
     532             :   /// These other uses adjust the result of the main history, and so
     533             :   /// do not require values (which are only used for the zeroth-order
     534             :   /// terms).
     535             :   ///
     536             :   /// The `value` and `derivative` data will be copied into cached
     537             :   /// allocations, if any are available.  That is, only a copy, not a
     538             :   /// memory allocation, will be performed when possible.
     539             :   /// @{
     540           1 :   void insert(const TimeStepId& time_step_id, const Vars& value,
     541             :               const DerivVars& derivative);
     542           1 :   void insert(const TimeStepId& time_step_id, NoValue /*unused*/,
     543             :               const DerivVars& derivative);
     544             :   /// @}
     545             : 
     546             :   /// Insert a new entry in the history by modifying the fields of the
     547             :   /// record directly, instead of passing a value to copy.
     548             :   ///
     549             :   /// The (optional) \p value_inserter must be a functor callable with
     550             :   /// single argument of type `gsl::not_null<Vars*>` and the \p
     551             :   /// derivative_inserter must be callable with a single argument of
     552             :   /// type `gsl::not_null<DerivVars*>`.  The passed objects will be
     553             :   /// created from a cached memory allocation if one is available, and
     554             :   /// will otherwise be default-constructed.
     555             :   ///
     556             :   /// All the restrictions on valid values \p time_step_id for the
     557             :   /// `insert` method apply here as well.
     558             :   /// @{
     559             :   template <typename ValueFunc, typename DerivativeFunc>
     560           1 :   void insert_in_place(const TimeStepId& time_step_id,
     561             :                        ValueFunc&& value_inserter,
     562             :                        DerivativeFunc&& derivative_inserter);
     563             :   template <typename DerivativeFunc>
     564           1 :   void insert_in_place(const TimeStepId& time_step_id, NoValue /*unused*/,
     565             :                        DerivativeFunc&& derivative_inserter);
     566             :   /// @}
     567             : 
     568             :   /// Insert data at the start of the history, similar to `push_front`
     569             :   /// on some STL containers.  This can be useful when initializing a
     570             :   /// multistep integrator.
     571             :   ///
     572             :   /// This should only be used for initialization, and cannot be used
     573             :   /// for substep data.  The supplied `time_step_id` must be earlier
     574             :   /// than the current first entry.
     575             :   /// @{
     576           1 :   void insert_initial(TimeStepId time_step_id, Vars value,
     577             :                       DerivVars derivative);
     578           1 :   void insert_initial(TimeStepId time_step_id, NoValue /*unused*/,
     579             :                       DerivVars derivative);
     580             :   /// @}
     581             : 
     582             :   /// The number of stored step (not substep) entries.
     583           1 :   size_t size() const { return step_values_.size(); }
     584             :   /// The maximum number of step (not substep) entries that can be
     585             :   /// stored in the history.  This number is not very large, but will
     586             :   /// be sufficient for running a time stepper requiring
     587             :   /// `history_max_past_steps` past step values.
     588           1 :   static constexpr size_t max_size() {
     589             :     // In addition to the past steps, we must store the start and end
     590             :     // of the current step.
     591             :     return history_max_past_steps + 2;
     592             :   }
     593             : 
     594             :   /// Access the `StepRecord` for a step (not substep).
     595             :   /// @{
     596           1 :   const StepRecord<Vars>& operator[](const size_t index) const {
     597             :     return step_values_[index];
     598             :   }
     599           1 :   StepRecord<Vars>& operator[](const size_t index) {
     600             :     return step_values_[index];
     601             :   }
     602             :   /// @}
     603             : 
     604             :   /// Access the `StepRecord` for a step or substep with a given
     605             :   /// TimeStepId.  It is an error if there is no entry with that id.
     606             :   /// @{
     607           1 :   const StepRecord<Vars>& operator[](const TimeStepId& id) const {
     608             :     return History_detail::find_record(*this, id);
     609             :   }
     610           1 :   StepRecord<Vars>& operator[](const TimeStepId& id) {
     611             :     return History_detail::find_record(*this, id);
     612             :   }
     613             :   /// @}
     614             : 
     615             :   /// Get the value at the latest step or substep in the history, even
     616             :   /// if the value in that record has been discarded.  This is not
     617             :   /// available if `undo_latest` has been called since the last
     618             :   /// insertion.
     619             :   ///
     620             :   /// This function exists to allow access to the previous value after
     621             :   /// a potentially bad step has been taken without restricting how
     622             :   /// the time steppers can manage their history.
     623           1 :   const Vars& latest_value() const;
     624             : 
     625             :   /// Get the record for the start of the latest complete step.  This
     626             :   /// is the step with substeps stored if there is one, and otherwise
     627             :   /// the most recent step.
     628           1 :   const StepRecord<Vars>& complete_step_start() const;
     629             : 
     630             :   /// Check whether we are at the start of a step, i.e, the most
     631             :   /// recent entry in the history is not a substep.
     632           1 :   bool at_step_start() const;
     633             : 
     634             :   /// Container view of the `StepRecord`s for the history substeps.
     635             :   /// These methods return classes providing an STL-like container
     636             :   /// interface to the substep data.  These containers are
     637             :   /// zero-indexed, so `substeps()[0]` will have a substep value of 1.
     638             :   ///
     639             :   /// These containers do not have methods to modify their sizes.  Use
     640             :   /// the methods on the `History` class for those operations.
     641             :   /// @{
     642           1 :   ConstSubsteps substeps() const { return ConstSubsteps(*this); }
     643           1 :   MutableSubsteps substeps() { return MutableSubsteps(this); }
     644             :   /// @}
     645             : 
     646             :   /// Clear the `value` in the indicated record.  It is an error if
     647             :   /// there is no record with the passed TimeStepId.  Any memory
     648             :   /// allocations will be cached for future reuse.
     649           1 :   void discard_value(const TimeStepId& id_to_discard);
     650             : 
     651             :   /// Drop the oldest step (not substep) entry in the history.  Any
     652             :   /// memory allocations will be cached for future reuse.
     653           1 :   void pop_front();
     654             : 
     655             :   /// Drop the newest step or substep entry in the history.  Any
     656             :   /// memory allocations will be cached for future reuse.
     657           1 :   void undo_latest();
     658             : 
     659             :   /// Remove all substep entries from the history.  Any memory
     660             :   /// allocations will be cached for future reuse.
     661           1 :   void clear_substeps();
     662             : 
     663             :   /// Remove all (step and substep) entries from the history.  Any
     664             :   /// memory allocations will be cached for future reuse.
     665           1 :   void clear();
     666             : 
     667             :   /// Release any cached memory allocations.
     668           1 :   void shrink_to_fit();
     669             : 
     670             :   /// Apply \p func to `make_not_null(&e)` for `e` every `derivative`
     671             :   /// and valid `*value` in records held by the history.
     672             :   template <typename F>
     673           1 :   void map_entries(F&& func);
     674             : 
     675             :   // NOLINTNEXTLINE(google-runtime-references)
     676           0 :   void pup(PUP::er& p);
     677             : 
     678           0 :   std::ostream& print(std::ostream& os) const;
     679             : 
     680             :  private:
     681           0 :   void discard_value(gsl::not_null<std::optional<Vars>*> value);
     682           0 :   void cache_allocations(gsl::not_null<StepRecord<Vars>*> record);
     683             : 
     684             :   template <typename ValueFunc, typename DerivativeFunc>
     685           0 :   void insert_impl(const TimeStepId& time_step_id, ValueFunc&& value_inserter,
     686             :                    DerivativeFunc&& derivative_inserter);
     687             : 
     688             :   template <typename InsertedVars>
     689           0 :   void insert_initial_impl(TimeStepId time_step_id, InsertedVars value,
     690             :                            DerivVars derivative);
     691             : 
     692           0 :   size_t integration_order_{0};
     693             : 
     694           0 :   StaticDeque<StepRecord<Vars>, max_size()> step_values_{};
     695             :   boost::container::static_vector<StepRecord<Vars>, history_max_substeps>
     696           0 :       substep_values_{};
     697             : 
     698             :   // If the algorithm undoes a substep, it may want to reset the
     699             :   // variables to the previous value.  We don't want the time stepper
     700             :   // to have to keep track of this when discarding values, so we hang
     701             :   // onto the last value if it gets discarded.
     702           0 :   std::optional<Vars> latest_value_if_discarded_{};
     703             : 
     704             :   // Memory allocations available for reuse.
     705             :   boost::container::static_vector<Vars, max_size() + history_max_substeps>
     706           0 :       vars_allocation_cache_{};
     707             :   boost::container::static_vector<DerivVars, max_size() + history_max_substeps>
     708           0 :       deriv_vars_allocation_cache_{};
     709             : };
     710             : 
     711             : // Don't copy the allocation caches.
     712             : template <typename Vars>
     713             : History<Vars>::History(const History& other)
     714             :     : integration_order_(other.integration_order_),
     715             :       step_values_(other.step_values_),
     716             :       substep_values_(other.substep_values_),
     717             :       latest_value_if_discarded_(other.latest_value_if_discarded_) {}
     718             : 
     719             : // Don't copy the allocation caches.
     720             : template <typename Vars>
     721             : History<Vars>& History<Vars>::operator=(const History& other) {
     722             :   integration_order_ = other.integration_order_;
     723             :   step_values_ = other.step_values_;
     724             :   substep_values_ = other.substep_values_;
     725             :   latest_value_if_discarded_ = other.latest_value_if_discarded_;
     726             :   return *this;
     727             : }
     728             : 
     729             : template <typename Vars>
     730             : void History<Vars>::insert(const TimeStepId& time_step_id, const Vars& value,
     731             :                            const DerivVars& derivative) {
     732             :   insert_impl(
     733             :       time_step_id,
     734             :       [&](const gsl::not_null<Vars*> record_value) { *record_value = value; },
     735             :       [&](const gsl::not_null<DerivVars*> record_derivative) {
     736             :         *record_derivative = derivative;
     737             :       });
     738             : }
     739             : 
     740             : template <typename Vars>
     741             : void History<Vars>::insert(const TimeStepId& time_step_id, NoValue /*unused*/,
     742             :                            const DerivVars& derivative) {
     743             :   insert_impl(time_step_id, NoValue{},
     744             :               [&](const gsl::not_null<DerivVars*> record_derivative) {
     745             :                 *record_derivative = derivative;
     746             :               });
     747             : }
     748             : 
     749             : template <typename Vars>
     750             : template <typename ValueFunc, typename DerivativeFunc>
     751             : void History<Vars>::insert_in_place(const TimeStepId& time_step_id,
     752             :                                     ValueFunc&& value_inserter,
     753             :                                     DerivativeFunc&& derivative_inserter) {
     754             :   insert_impl(time_step_id, std::forward<ValueFunc>(value_inserter),
     755             :               std::forward<DerivativeFunc>(derivative_inserter));
     756             : }
     757             : 
     758             : template <typename Vars>
     759             : template <typename DerivativeFunc>
     760             : void History<Vars>::insert_in_place(const TimeStepId& time_step_id,
     761             :                                     NoValue /*unused*/,
     762             :                                     DerivativeFunc&& derivative_inserter) {
     763             :   insert_impl(time_step_id, NoValue{},
     764             :               std::forward<DerivativeFunc>(derivative_inserter));
     765             : }
     766             : 
     767             : template <typename Vars>
     768             : void History<Vars>::insert_initial(TimeStepId time_step_id, Vars value,
     769             :                                    DerivVars derivative) {
     770             :   insert_initial_impl(std::move(time_step_id), std::move(value),
     771             :                       std::move(derivative));
     772             : }
     773             : 
     774             : template <typename Vars>
     775             : void History<Vars>::insert_initial(TimeStepId time_step_id, NoValue /*unused*/,
     776             :                                    DerivVars derivative) {
     777             :   insert_initial_impl(std::move(time_step_id), NoValue{},
     778             :                       std::move(derivative));
     779             : }
     780             : 
     781             : template <typename Vars>
     782             : const Vars& History<Vars>::latest_value() const {
     783             :   const auto& latest_record =
     784             :       at_step_start() ? this->back() : substeps().back();
     785             :   if (latest_record.value.has_value()) {
     786             :     return *latest_record.value;
     787             :   } else {
     788             :     ASSERT(latest_value_if_discarded_.has_value(),
     789             :            "Latest value unavailable.  The latest insertion was undone.");
     790             :     return *latest_value_if_discarded_;
     791             :   }
     792             : }
     793             : 
     794             : template <typename Vars>
     795             : const StepRecord<Vars>& History<Vars>::complete_step_start() const {
     796             :   ASSERT(not this->empty(), "History is empty");
     797             :   if (substep_values_.empty()) {
     798             :     return this->back();
     799             :   } else {
     800             :     const TimeStepId substep_id = substep_values_.front().time_step_id;
     801             :     const TimeStepId step_id(substep_id.time_runs_forward(),
     802             :                              substep_id.slab_number(), substep_id.step_time());
     803             :     return (*this)[step_id];
     804             :   }
     805             : }
     806             : 
     807             : template <typename Vars>
     808             : bool History<Vars>::at_step_start() const {
     809             :   ASSERT(not this->empty(), "History is empty");
     810             :   return substep_values_.empty() or
     811             :          substep_values_.back().time_step_id < this->back().time_step_id;
     812             : }
     813             : 
     814             : template <typename Vars>
     815             : void History<Vars>::discard_value(const TimeStepId& id_to_discard) {
     816             :   auto& latest_record = at_step_start() ? this->back() : substeps().back();
     817             :   if (latest_record.time_step_id == id_to_discard) {
     818             :     discard_value(&latest_value_if_discarded_);
     819             :     latest_value_if_discarded_ = std::move(latest_record.value);
     820             :     latest_record.value.reset();
     821             :   } else {
     822             :     discard_value(&History_detail::find_record(*this, id_to_discard).value);
     823             :   }
     824             : }
     825             : 
     826             : template <typename Vars>
     827             : void History<Vars>::pop_front() {
     828             :   ASSERT(not this->empty(), "History is empty");
     829             :   ASSERT(substeps().empty() or substeps().front().time_step_id.step_time() !=
     830             :                                    this->front().time_step_id.step_time(),
     831             :          "Cannot remove a step with substeps.  Call clear_substeps() first.");
     832             :   cache_allocations(&step_values_.front());
     833             :   step_values_.pop_front();
     834             : }
     835             : 
     836             : template <typename Vars>
     837             : void History<Vars>::undo_latest() {
     838             :   if (at_step_start()) {
     839             :     cache_allocations(&step_values_.back());
     840             :     step_values_.pop_back();
     841             :   } else {
     842             :     cache_allocations(&substep_values_.back());
     843             :     substep_values_.pop_back();
     844             :   }
     845             :   discard_value(&latest_value_if_discarded_);
     846             : }
     847             : 
     848             : template <typename Vars>
     849             : void History<Vars>::clear_substeps() {
     850             :   for (auto& record : substep_values_) {
     851             :     cache_allocations(&record);
     852             :   }
     853             :   substep_values_.clear();
     854             : }
     855             : 
     856             : template <typename Vars>
     857             : void History<Vars>::clear() {
     858             :   clear_substeps();
     859             :   while (not this->empty()) {
     860             :     pop_front();
     861             :   }
     862             :   discard_value(&latest_value_if_discarded_);
     863             : }
     864             : 
     865             : template <typename Vars>
     866             : void History<Vars>::shrink_to_fit() {
     867             :   vars_allocation_cache_.clear();
     868             :   vars_allocation_cache_.shrink_to_fit();
     869             :   deriv_vars_allocation_cache_.clear();
     870             :   deriv_vars_allocation_cache_.shrink_to_fit();
     871             : }
     872             : 
     873             : template <typename Vars>
     874             : template <typename F>
     875             : void History<Vars>::map_entries(F&& func) {
     876             :   for (auto& record : *this) {
     877             :     func(make_not_null(&record.derivative));
     878             :     if (record.value.has_value()) {
     879             :       func(make_not_null(&*record.value));
     880             :     }
     881             :   }
     882             :   for (auto& record : this->substeps()) {
     883             :     func(make_not_null(&record.derivative));
     884             :     if (record.value.has_value()) {
     885             :       func(make_not_null(&*record.value));
     886             :     }
     887             :   }
     888             : }
     889             : 
     890             : template <typename Vars>
     891             : void History<Vars>::pup(PUP::er& p) {
     892             :   p | integration_order_;
     893             :   p | step_values_;
     894             :   p | substep_values_;
     895             :   p | latest_value_if_discarded_;
     896             : 
     897             :   // Don't serialize the allocation cache.
     898             : }
     899             : 
     900             : template <typename Vars>
     901             : std::ostream& History<Vars>::print(std::ostream& os) const {
     902             :   using ::operator<<;
     903             :   os << "Integration order: " << integration_order_ << "\n";
     904             :   os << "Step values:\n";
     905             :   for (auto& record : *this) {
     906             :     record.print(os);
     907             :   }
     908             :   os << "Substep values:\n";
     909             :   for (auto& record : substep_values_) {
     910             :     record.print(os);
     911             :   }
     912             :   os << "Latest value if discarded: " << latest_value_if_discarded_ << "\n";
     913             :   return os;
     914             : }
     915             : 
     916             : // Doxygen is confused by this function for some reason.
     917             : /// \cond
     918             : template <typename Vars>
     919             : void History<Vars>::discard_value(
     920             :     const gsl::not_null<std::optional<Vars>*> value) {
     921             :   if (not value->has_value()) {
     922             :     return;
     923             :   }
     924             :   // If caching doesn't save anything, don't allocate memory for the cache.
     925             :   if (contains_allocations(**value)) {
     926             :     vars_allocation_cache_.emplace_back(std::move(**value));
     927             :   }
     928             :   value->reset();
     929             : }
     930             : /// \endcond
     931             : 
     932             : template <typename Vars>
     933             : void History<Vars>::cache_allocations(
     934             :     const gsl::not_null<StepRecord<Vars>*> record) {
     935             :   discard_value(&record->value);
     936             :   // If caching doesn't save anything, don't allocate memory for the cache.
     937             :   if (contains_allocations(record->derivative)) {
     938             :     deriv_vars_allocation_cache_.emplace_back(std::move(record->derivative));
     939             :   }
     940             : }
     941             : 
     942             : template <typename Vars>
     943             : template <typename ValueFunc, typename DerivativeFunc>
     944             : void History<Vars>::insert_impl(const TimeStepId& time_step_id,
     945             :                                 ValueFunc&& value_inserter,
     946             :                                 DerivativeFunc&& derivative_inserter) {
     947             :   ASSERT(this->empty() or time_step_id > this->back().time_step_id,
     948             :          "New entry at " << time_step_id
     949             :          << " must be later than previous entry at "
     950             :          << this->back().time_step_id);
     951             :   discard_value(&latest_value_if_discarded_);
     952             :   StepRecord<Vars> record{};
     953             :   record.time_step_id = time_step_id;
     954             :   if constexpr (not std::is_same_v<ValueFunc, NoValue>) {
     955             :     if (vars_allocation_cache_.empty()) {
     956             :       record.value.emplace();
     957             :     } else {
     958             :       record.value.emplace(std::move(vars_allocation_cache_.back()));
     959             :       vars_allocation_cache_.pop_back();
     960             :     }
     961             :     std::forward<ValueFunc>(value_inserter)(make_not_null(&*record.value));
     962             :   }
     963             :   if (not deriv_vars_allocation_cache_.empty()) {
     964             :     record.derivative = std::move(deriv_vars_allocation_cache_.back());
     965             :     deriv_vars_allocation_cache_.pop_back();
     966             :   }
     967             :   std::forward<DerivativeFunc>(derivative_inserter)(
     968             :       make_not_null(&record.derivative));
     969             : 
     970             :   const size_t substep = time_step_id.substep();
     971             :   if (substep == 0) {
     972             :     step_values_.push_back(std::move(record));
     973             :   } else {
     974             :     ASSERT(not this->empty(), "Cannot insert substep into empty history.");
     975             :     ASSERT(time_step_id.step_time() == this->back().time_step_id.step_time(),
     976             :            "Cannot insert substep " << time_step_id << " of different step "
     977             :            << this->back().time_step_id);
     978             :     ASSERT(substep == substeps().size() + 1,
     979             :            "Cannot insert substep " << substep << " following "
     980             :            << substeps().size());
     981             :     ASSERT(substep_values_.size() < substep_values_.max_size(),
     982             :            "Cannot insert new substep because the History is full.");
     983             :     substep_values_.push_back(std::move(record));
     984             :   }
     985             : }
     986             : 
     987             : template <typename Vars>
     988             : template <typename InsertedVars>
     989             : void History<Vars>::insert_initial_impl(TimeStepId time_step_id,
     990             :                                         InsertedVars value,
     991             :                                         DerivVars derivative) {
     992             :   ASSERT(vars_allocation_cache_.empty(),
     993             :          "insert_initial should only be used for initialization");
     994             :   ASSERT(deriv_vars_allocation_cache_.empty(),
     995             :          "insert_initial should only be used for initialization");
     996             :   ASSERT(time_step_id.substep() == 0, "Cannot use insert_initial for substeps");
     997             :   ASSERT(this->empty() or time_step_id < this->front().time_step_id,
     998             :          "New initial entry at " << time_step_id
     999             :          << " must be earlier than previous entry at "
    1000             :          << this->front().time_step_id);
    1001             : 
    1002             :   if constexpr (std::is_same_v<InsertedVars, Vars>) {
    1003             :     step_values_.push_front(StepRecord<Vars>{
    1004             :         std::move(time_step_id), std::move(value), std::move(derivative)});
    1005             :   } else {
    1006             :     static_assert(std::is_same_v<InsertedVars, NoValue>);
    1007             :     (void)value;
    1008             :     step_values_.push_front(StepRecord<Vars>{
    1009             :         std::move(time_step_id), std::nullopt, std::move(derivative)});
    1010             :   }
    1011             : }
    1012             : 
    1013             : template <typename Vars>
    1014           0 : bool operator==(const History<Vars>& a, const History<Vars>& b) {
    1015             :   return a.integration_order() == b.integration_order() and
    1016             :          a.size() == b.size() and std::equal(a.begin(), a.end(), b.begin()) and
    1017             :          a.substeps() == b.substeps();
    1018             : }
    1019             : 
    1020             : template <typename Vars>
    1021           0 : bool operator!=(const History<Vars>& a, const History<Vars>& b) {
    1022             :   return not(a == b);
    1023             : }
    1024             : 
    1025             : template <typename Vars>
    1026           0 : std::ostream& operator<<(std::ostream& os, const History<Vars>& history) {
    1027             :   return history.print(os);
    1028             : }
    1029             : 
    1030             : /// \ingroup TimeSteppersGroup
    1031             : /// Initialize a History object based on the contents of another,
    1032             : /// applying a transformation to each value and derivative.
    1033             : ///
    1034             : /// The transformation functions can either take a value from the
    1035             : /// source history and return a value for the destination history or
    1036             : /// take a `gsl::not_null` value from the destination history and a
    1037             : /// value from the source history to initialize it with.  For the sake
    1038             : /// of implementation simplicity, either both transformers must mutate
    1039             : /// or both must produce values.
    1040             : ///
    1041             : /// An overload applying the same transformation to the values and
    1042             : /// derivatives is provided for convenience.
    1043             : /// @{
    1044             : template <typename DestVars, typename SourceVars, typename ValueTransformer,
    1045             :           typename DerivativeTransformer>
    1046           1 : void transform(const gsl::not_null<History<DestVars>*> dest,
    1047             :                const History<SourceVars>& source,
    1048             :                ValueTransformer&& value_transformer,
    1049             :                DerivativeTransformer&& derivative_transformer) {
    1050             :   dest->clear_substeps();
    1051             :   dest->clear();
    1052             :   dest->integration_order(source.integration_order());
    1053             :   if (source.empty()) {
    1054             :     return;
    1055             :   }
    1056             :   auto pre_substep_end = source.end();
    1057             :   if (not source.substeps().empty() and
    1058             :       source.back().time_step_id > source.substeps().back().time_step_id) {
    1059             :     --pre_substep_end;
    1060             :   }
    1061             : 
    1062             :   const auto transform_record =
    1063             :       [&derivative_transformer, &dest, &value_transformer](
    1064             :           const typename History<SourceVars>::value_type& record) {
    1065             :         if constexpr (std::is_invocable_v<ValueTransformer,
    1066             :                                           gsl::not_null<DestVars*>,
    1067             :                                           const SourceVars&>) {
    1068             :           if (record.value.has_value()) {
    1069             :             dest->insert_in_place(
    1070             :                 record.time_step_id,
    1071             :                 [&](const auto result) {
    1072             :                   value_transformer(result, *record.value);
    1073             :                 },
    1074             :                 [&](const auto result) {
    1075             :                   derivative_transformer(result, record.derivative);
    1076             :                 });
    1077             :           } else {
    1078             :             dest->insert_in_place(
    1079             :                 record.time_step_id, History<DestVars>::no_value,
    1080             :                 [&](const auto result) {
    1081             :                   derivative_transformer(result, record.derivative);
    1082             :                 });
    1083             :           }
    1084             :         } else {
    1085             :           static_assert(
    1086             :               std::is_invocable_v<ValueTransformer, const SourceVars&>,
    1087             :               "Transform function must either be callable to mutate entries "
    1088             :               "or return the transformed state by value.");
    1089             :           if (record.value.has_value()) {
    1090             :             dest->insert(record.time_step_id, value_transformer(*record.value),
    1091             :                          derivative_transformer(record.derivative));
    1092             :           } else {
    1093             :             dest->insert(record.time_step_id, History<DestVars>::no_value,
    1094             :                          derivative_transformer(record.derivative));
    1095             :           }
    1096             :         }
    1097             :       };
    1098             : 
    1099             :   auto copying_step = source.begin();
    1100             :   for (; copying_step != pre_substep_end; ++copying_step) {
    1101             :     transform_record(*copying_step);
    1102             :   }
    1103             :   for (const auto& record : source.substeps()) {
    1104             :     transform_record(record);
    1105             :   }
    1106             :   if (pre_substep_end != source.end()) {
    1107             :     transform_record(*pre_substep_end);
    1108             :   }
    1109             : }
    1110             : 
    1111             : template <typename DestVars, typename SourceVars, typename Transformer>
    1112           1 : void transform(const gsl::not_null<History<DestVars>*> dest,
    1113             :                const History<SourceVars>& source, Transformer&& transformer) {
    1114             :   transform(dest, source, transformer, transformer);
    1115             : }
    1116             : /// @}
    1117             : }  // namespace TimeSteppers

Generated by: LCOV version 1.14