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

Generated by: LCOV version 2.0-1