SpECTRE Documentation Coverage Report
Current view: top level - Evolution/Systems/Cce - ScriPlusInterpolationManager.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 24 45 53.3 %
Date: 2025-12-05 05:03:31
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 <algorithm>
       7             : #include <complex>
       8             : #include <cstddef>
       9             : #include <deque>
      10             : #include <memory>
      11             : #include <pup.h>
      12             : #include <pup_stl.h>
      13             : #include <utility>
      14             : #include <vector>
      15             : 
      16             : #include "DataStructures/ApplyMatrices.hpp"
      17             : #include "DataStructures/DataVector.hpp"
      18             : #include "Evolution/Systems/Cce/WorldtubeDataManager.hpp"
      19             : #include "NumericalAlgorithms/Interpolation/SpanInterpolator.hpp"
      20             : #include "NumericalAlgorithms/Spectral/Basis.hpp"
      21             : #include "NumericalAlgorithms/Spectral/CollocationPoints.hpp"
      22             : #include "NumericalAlgorithms/Spectral/DifferentiationMatrix.hpp"
      23             : #include "NumericalAlgorithms/Spectral/Quadrature.hpp"
      24             : #include "Utilities/Algorithm.hpp"
      25             : #include "Utilities/ErrorHandling/Assert.hpp"
      26             : #include "Utilities/ErrorHandling/Error.hpp"
      27             : #include "Utilities/Literals.hpp"
      28             : #include "Utilities/MakeArray.hpp"
      29             : 
      30             : /// \cond
      31             : namespace Tags {
      32             : template <typename LhsTag, typename RhsTag>
      33             : struct Multiplies;
      34             : }  // namespace Tags
      35             : /// \endcond
      36             : 
      37             : namespace Cce {
      38             : 
      39             : /*!
      40             :  * \brief Stores necessary data and interpolates on to new time points at scri+.
      41             :  *
      42             :  * \details The coordinate time used for the CCE evolution is not the same as
      43             :  * the asymptotic inertial retarded time, which is determined through a separate
      44             :  * evolution equation. This class manages the scri+ data passed in (via
      45             :  * `insert_data()`) along with the set of inertial retarded times associated
      46             :  * with that data, and interpolates to a set of requested times (supplied via
      47             :  * `insert_target_time()`).
      48             :  *
      49             :  * Template parameters:
      50             :  * - `VectorTypeToInterpolate`: the vector type associated with the values to
      51             :  * interpolate.
      52             :  * - `Tag`: The tag associated with the interpolation procedure. This determines
      53             :  * the behavior of the interpolation return value. The default is just
      54             :  * interpolation, if `Tag` has prefix `::Tags::Multiplies` or `Tags::Du`, the
      55             :  * interpolator performs the additional multiplication or time derivative as a
      56             :  * step in the interpolation procedure.
      57             :  */
      58             : template <typename VectorTypeToInterpolate, typename Tag>
      59           1 : struct ScriPlusInterpolationManager {
      60             :  public:
      61           0 :   ScriPlusInterpolationManager() = default;
      62             : 
      63           0 :   ScriPlusInterpolationManager(
      64             :       const size_t target_number_of_points, const size_t vector_size,
      65             :       std::unique_ptr<intrp::SpanInterpolator> interpolator)
      66             :       : vector_size_{vector_size},
      67             :         target_number_of_points_{target_number_of_points},
      68             :         interpolator_{std::move(interpolator)} {}
      69             : 
      70             :   /// \brief provide data to the interpolation manager.
      71             :   ///
      72             :   /// \details `u_bondi` is a vector of inertial times, and `to_interpolate` is
      73             :   /// the vector of values that will be interpolated to target times.
      74           1 :   void insert_data(const DataVector& u_bondi,
      75             :                    const VectorTypeToInterpolate& to_interpolate) {
      76             :     ASSERT(to_interpolate.size() == vector_size_,
      77             :            "Inserted data must be of size specified at construction: "
      78             :                << vector_size_
      79             :                << " and provided data is of size: " << to_interpolate.size());
      80             :     u_bondi_values_.push_back(u_bondi);
      81             :     to_interpolate_values_.push_back(to_interpolate);
      82             :     u_bondi_ranges_.emplace_back(min(u_bondi), max(u_bondi));
      83             :   }
      84             : 
      85             :   /// \brief Request a target time to be interpolated to when enough data has
      86             :   /// been accumulated.
      87             :   ///
      88             :   /// For optimization, we assume that these are inserted in ascending order.
      89           1 :   void insert_target_time(const double time) {
      90             :     target_times_.push_back(time);
      91             :     // if this is the first time in the deque, we should use it to determine
      92             :     // whether some of the old data is ready to be removed. In other cases, this
      93             :     // check can be performed when a time is removed from the queue via
      94             :     // `interpolate_and_pop_first_time`
      95             :     if (target_times_.size() == 1) {
      96             :       remove_unneeded_early_times();
      97             :     }
      98             :   }
      99             : 
     100             :   /// \brief Determines whether enough data before and after the first time in
     101             :   /// the target time queue has been provided to interpolate.
     102             :   ///
     103             :   /// \details If possible, this function will require that the target time to
     104             :   /// be interpolated is reasonably centered on the range, but will settle for
     105             :   /// non-centered data if the time is too early for the given data, which is
     106             :   /// necessary to get the first couple of times out of the simulation. This
     107             :   /// function always returns false if all of the provided data is earlier than
     108             :   /// the next target time, indicating that the caller should wait until more
     109             :   /// data has been provided before interpolating.
     110           1 :   bool first_time_is_ready_to_interpolate() const;
     111             : 
     112           0 :   const std::deque<std::pair<double, double>>& get_u_bondi_ranges() const {
     113             :     return u_bondi_ranges_;
     114             :   }
     115             : 
     116             :   /// \brief Interpolate to the first target time in the queue, returning both
     117             :   /// the time and the interpolated data at that time.
     118             :   ///
     119             :   /// \note If this function is not able to interpolate to the full accuracy
     120             :   /// using a centered stencil, it will perform the best interpolation
     121             :   /// available.
     122             :   ///
     123             :   /// \warning This function will extrapolate if the target times are
     124             :   /// outside the range of data the class has been provided. This is intentional
     125             :   /// to support small extrapolation at the end of a simulation when no further
     126             :   /// data is available, but for full accuracy, check
     127             :   /// `first_time_is_ready_to_interpolate` before calling the interpolation
     128             :   /// functions
     129           1 :   std::pair<double, VectorTypeToInterpolate> interpolate_first_time();
     130             : 
     131             :   /// \brief Interpolate to the first target time in the queue, returning both
     132             :   /// the time and the interpolated data at that time, and remove the first time
     133             :   /// from the queue.
     134             :   ///
     135             :   /// \note If this function is not able to interpolate to the full accuracy
     136             :   /// using a centered stencil, it will perform the best interpolation
     137             :   /// available.
     138             :   ///
     139             :   /// \warning This function will extrapolate if the target times are
     140             :   /// outside the range of data the class has been provided. This is intentional
     141             :   /// to support small extrapolation at the end of a simulation when no further
     142             :   /// data is available, but for full accuracy, check
     143             :   /// `first_time_is_ready_to_interpolate` before calling the interpolation
     144             :   /// functions
     145           1 :   std::pair<double, VectorTypeToInterpolate> interpolate_and_pop_first_time();
     146             : 
     147             :   /// \brief return the number of times in the target times queue
     148           1 :   size_t number_of_target_times() const { return target_times_.size(); }
     149             : 
     150             :   /// \brief return the number of data points that have been provided to the
     151             :   /// interpolation manager
     152           1 :   size_t number_of_data_points() const { return u_bondi_ranges_.size(); }
     153             : 
     154             :   // NOLINTNEXTLINE(google-runtime-references)
     155           0 :   void pup(PUP::er& p) {
     156             :     p | u_bondi_values_;
     157             :     p | to_interpolate_values_;
     158             :     p | u_bondi_ranges_;
     159             :     p | target_times_;
     160             :     p | vector_size_;
     161             :     p | target_number_of_points_;
     162             :     p | interpolator_;
     163             :   }
     164             : 
     165             :  private:
     166           0 :   void remove_unneeded_early_times();
     167             : 
     168             :   friend struct ScriPlusInterpolationManager<VectorTypeToInterpolate,
     169             :                                              Tags::Du<Tag>>;
     170             : 
     171           0 :   std::deque<DataVector> u_bondi_values_;
     172           0 :   std::deque<VectorTypeToInterpolate> to_interpolate_values_;
     173           0 :   std::deque<std::pair<double, double>> u_bondi_ranges_;
     174           0 :   std::deque<double> target_times_;
     175           0 :   size_t vector_size_ = 0_st;
     176           0 :   size_t target_number_of_points_ = 0_st;
     177           0 :   std::unique_ptr<intrp::SpanInterpolator> interpolator_;
     178             : };
     179             : 
     180             : template <typename VectorTypeToInterpolate, typename Tag>
     181             : bool ScriPlusInterpolationManager<
     182             :     VectorTypeToInterpolate, Tag>::first_time_is_ready_to_interpolate() const {
     183             :   if (target_times_.empty()) {
     184             :     return false;
     185             :   }
     186             :   auto maxes_below = alg::count_if(
     187             :       u_bondi_ranges_, [this](const std::pair<double, double> time) {
     188             :         return time.second <= target_times_.front();
     189             :       });
     190             :   auto mins_above = alg::count_if(u_bondi_ranges_,
     191             :                                   [this](const std::pair<double, double> time) {
     192             :                                     return time.first > target_times_.front();
     193             :                                   });
     194             : 
     195             :   // we might ask for a time that's too close to the end or the beginning of
     196             :   // our data, in which case we will settle for at least one point below and
     197             :   // above and a sufficient number of total points.
     198             :   // This will always be `false` if the earliest target time is later than all
     199             :   // provided data points, which would require extrapolation
     200             :   return ((static_cast<size_t>(maxes_below) > target_number_of_points_ or
     201             :            static_cast<size_t>(maxes_below + mins_above) >
     202             :                2 * target_number_of_points_) and
     203             :           static_cast<size_t>(mins_above) > target_number_of_points_);
     204             : }
     205             : 
     206             : template <typename VectorTypeToInterpolate, typename Tag>
     207             : std::pair<double, VectorTypeToInterpolate> ScriPlusInterpolationManager<
     208             :     VectorTypeToInterpolate, Tag>::interpolate_first_time() {
     209             :   if (target_times_.empty()) {
     210             :     ERROR("There are no target times to interpolate.");
     211             :   }
     212             :   if (to_interpolate_values_.size() < 2 * target_number_of_points_) {
     213             :     ERROR("Insufficient data points to continue interpolation: have "
     214             :           << to_interpolate_values_.size() << ", need at least"
     215             :           << 2 * target_number_of_points_);
     216             :   }
     217             : 
     218             :   VectorTypeToInterpolate result{vector_size_};
     219             :   const size_t interpolation_data_size = to_interpolate_values_.size();
     220             : 
     221             :   VectorTypeToInterpolate interpolation_values{2 * target_number_of_points_};
     222             :   DataVector interpolation_times{2 * target_number_of_points_};
     223             :   for (size_t i = 0; i < vector_size_; ++i) {
     224             :     // binary search assumes times placed in sorted order
     225             :     auto upper_bound_offset = static_cast<size_t>(std::distance(
     226             :         u_bondi_values_.begin(),
     227             :         std::upper_bound(u_bondi_values_.begin(), u_bondi_values_.end(),
     228             :                          target_times_.front(),
     229             :                          [&i](const double rhs, const DataVector& lhs) {
     230             :                            return rhs < lhs[i];
     231             :                          })));
     232             :     size_t lower_bound_offset =
     233             :         upper_bound_offset == 0 ? 0 : upper_bound_offset - 1;
     234             : 
     235             :     if (upper_bound_offset + target_number_of_points_ >
     236             :         interpolation_data_size) {
     237             :       upper_bound_offset = interpolation_data_size;
     238             :       lower_bound_offset =
     239             :           interpolation_data_size - 2 * target_number_of_points_;
     240             :     } else if (lower_bound_offset < target_number_of_points_ - 1) {
     241             :       lower_bound_offset = 0;
     242             :       upper_bound_offset = 2 * target_number_of_points_;
     243             :     } else {
     244             :       lower_bound_offset = lower_bound_offset + 1 - target_number_of_points_;
     245             :       upper_bound_offset = lower_bound_offset + 2 * target_number_of_points_;
     246             :     }
     247             :     auto interpolation_values_begin =
     248             :         to_interpolate_values_.begin() +
     249             :         static_cast<ptrdiff_t>(lower_bound_offset);
     250             :     auto interpolation_times_begin =
     251             :         u_bondi_values_.begin() + static_cast<ptrdiff_t>(lower_bound_offset);
     252             :     auto interpolation_times_end =
     253             :         u_bondi_values_.begin() + static_cast<ptrdiff_t>(upper_bound_offset);
     254             : 
     255             :     // interpolate using the data sets in the restricted iterators
     256             :     auto value_it = interpolation_values_begin;
     257             :     size_t vector_position = 0;
     258             :     for (auto time_it = interpolation_times_begin;
     259             :          time_it != interpolation_times_end;
     260             :          ++time_it, ++value_it, ++vector_position) {
     261             :       interpolation_values[vector_position] = (*value_it)[i];
     262             :       interpolation_times[vector_position] = (*time_it)[i];
     263             :     }
     264             :     result[i] = interpolator_->interpolate(
     265             :         gsl::span<const double>(interpolation_times.data(),
     266             :                                 interpolation_times.size()),
     267             :         gsl::span<const typename VectorTypeToInterpolate::value_type>(
     268             :             interpolation_values.data(), interpolation_values.size()),
     269             :         target_times_.front());
     270             :   }
     271             :   return std::make_pair(target_times_.front(), std::move(result));
     272             : }
     273             : 
     274             : template <typename VectorTypeToInterpolate, typename Tag>
     275             : std::pair<double, VectorTypeToInterpolate> ScriPlusInterpolationManager<
     276             :     VectorTypeToInterpolate, Tag>::interpolate_and_pop_first_time() {
     277             :   std::pair<double, VectorTypeToInterpolate> interpolated =
     278             :       interpolate_first_time();
     279             :   target_times_.pop_front();
     280             : 
     281             :   if (not target_times_.empty()) {
     282             :     remove_unneeded_early_times();
     283             :   }
     284             :   return interpolated;
     285             : }
     286             : 
     287             : template <typename VectorTypeToInterpolate, typename Tag>
     288             : void ScriPlusInterpolationManager<VectorTypeToInterpolate,
     289             :                                   Tag>::remove_unneeded_early_times() {
     290             :   // pop times we no longer need because their maxes are too far in the past
     291             :   auto time_it = u_bondi_ranges_.begin();
     292             :   size_t times_counter = 0;
     293             :   while (time_it < u_bondi_ranges_.end() and
     294             :          (*time_it).second < target_times_.front()) {
     295             :     if (times_counter > target_number_of_points_ and
     296             :         u_bondi_ranges_.size() >= 2 * target_number_of_points_) {
     297             :       u_bondi_ranges_.pop_front();
     298             :       u_bondi_values_.pop_front();
     299             :       to_interpolate_values_.pop_front();
     300             :     } else {
     301             :       ++times_counter;
     302             :     }
     303             :     ++time_it;
     304             :   }
     305             : }
     306             : 
     307             : /*!
     308             :  * \brief Stores necessary data and interpolates on to new time points at scri+,
     309             :  * multiplying two results together before supplying the result.
     310             :  *
     311             :  * \details The coordinate time used for the CCE evolution is not the same as
     312             :  * the asymptotic inertial retarded time, which is determined through a separate
     313             :  * evolution equation. This class manages the two sets of  scri+ data passed in
     314             :  * (via `insert_data()`) along with the set of inertial retarded times
     315             :  * associated with that data, and interpolates to a set of inertial requested
     316             :  * times (supplied via `insert_target_time()`), multiplying the two
     317             :  * interpolation results together before returning.
     318             :  *
     319             :  * Template parameters:
     320             :  * - `VectorTypeToInterpolate`: the vector type associated with the values to
     321             :  * interpolate.
     322             :  * - `::Tags::Multiplies<MultipliesLhs, MultipliesRhs>`: The tag associated with
     323             :  * the interpolation procedure. This determines the behavior of the
     324             :  * interpolation return value. The default is just interpolation, if `Tag` has
     325             :  * prefix  `::Tags::Multiplies` (this case) or `Tags::Du`, the interpolator
     326             :  * performs the additional multiplication or time derivative as a step in the
     327             :  * interpolation procedure.
     328             :  */
     329             : template <typename VectorTypeToInterpolate, typename MultipliesLhs,
     330             :           typename MultipliesRhs>
     331           1 : struct ScriPlusInterpolationManager<
     332             :     VectorTypeToInterpolate, ::Tags::Multiplies<MultipliesLhs, MultipliesRhs>> {
     333             :  public:
     334           0 :   ScriPlusInterpolationManager() = default;
     335           0 :   ScriPlusInterpolationManager(
     336             :       const size_t target_number_of_points, const size_t vector_size,
     337             :       std::unique_ptr<intrp::SpanInterpolator> interpolator)
     338             :       : interpolation_manager_lhs_{target_number_of_points, vector_size,
     339             :                                    interpolator->get_clone()},
     340             :         interpolation_manager_rhs_{target_number_of_points, vector_size,
     341             :                                    std::move(interpolator)} {}
     342             : 
     343             :   /// \brief provide data to the interpolation manager.
     344             :   ///
     345             :   /// \details `u_bondi` is a vector of inertial times, and `to_interpolate_lhs`
     346             :   /// and `to_interpolate_rhs` are the vector of values that will be
     347             :   /// interpolated to target times. The interpolation result will be the product
     348             :   /// of the interpolated lhs and rhs vectors.
     349           1 :   void insert_data(const DataVector& u_bondi,
     350             :                    const VectorTypeToInterpolate& to_interpolate_lhs,
     351             :                    const VectorTypeToInterpolate& to_interpolate_rhs) {
     352             :     interpolation_manager_lhs_.insert_data(u_bondi, to_interpolate_lhs);
     353             :     interpolation_manager_rhs_.insert_data(u_bondi, to_interpolate_rhs);
     354             :   }
     355             : 
     356             :   /// \brief Request a target time to be interpolated to when enough data has
     357             :   /// been accumulated.
     358             :   ///
     359             :   /// For optimization, we assume that these are inserted in ascending order.
     360           1 :   void insert_target_time(const double time) {
     361             :     interpolation_manager_lhs_.insert_target_time(time);
     362             :     interpolation_manager_rhs_.insert_target_time(time);
     363             :   }
     364             : 
     365             :   /// \brief Determines whether enough data before and after the first time in
     366             :   /// the target time queue has been provided to interpolate.
     367             :   ///
     368             :   /// \details If possible, this function will require that the target time to
     369             :   /// be interpolated is reasonably centered on the range, but will settle for
     370             :   /// non-centered data if the time is too early for the given data, which is
     371             :   /// necessary to get the first couple of times out of the simulation. This
     372             :   /// function always returns false if all of the provided data is earlier than
     373             :   /// the next target time, indicating that the caller should wait until more
     374             :   /// data has been provided before interpolating.
     375           1 :   bool first_time_is_ready_to_interpolate() const {
     376             :     return interpolation_manager_lhs_.first_time_is_ready_to_interpolate() and
     377             :            interpolation_manager_rhs_.first_time_is_ready_to_interpolate();
     378             :   }
     379             : 
     380           0 :   const std::deque<std::pair<double, double>>& get_u_bondi_ranges() const {
     381             :     return interpolation_manager_lhs_.get_u_bondi_ranges();
     382             :   }
     383             : 
     384             :   /// \brief Interpolate to the first target time in the queue, returning both
     385             :   /// the time and the interpolated-and-multiplied data at that time.
     386             :   ///
     387             :   /// \note If this function is not able to interpolate to the full accuracy
     388             :   /// using a centered stencil, it will perform the best interpolation
     389             :   /// available.
     390             :   ///
     391             :   /// \warning This function will extrapolate if the target times are
     392             :   /// outside the range of data the class has been provided. This is intentional
     393             :   /// to support small extrapolation at the end of a simulation when no further
     394             :   /// data is available, but for full accuracy, check
     395             :   /// `first_time_is_ready_to_interpolate` before calling the interpolation
     396             :   /// functions
     397           1 :   std::pair<double, VectorTypeToInterpolate> interpolate_first_time() {
     398             :     const auto lhs_interpolation =
     399             :         interpolation_manager_lhs_.interpolate_first_time();
     400             :     const auto rhs_interpolation =
     401             :         interpolation_manager_rhs_.interpolate_first_time();
     402             :     return std::make_pair(lhs_interpolation.first,
     403             :                           lhs_interpolation.second * rhs_interpolation.second);
     404             :   }
     405             : 
     406             :   /// \brief Interpolate to the first target time in the queue, returning both
     407             :   /// the time and the interpolated-and-multiplied data at that time, and remove
     408             :   /// the first time from the queue.
     409             :   ///
     410             :   /// \note If this function is not able to interpolate to the full accuracy
     411             :   /// using a centered stencil, it will perform the best interpolation
     412             :   /// available.
     413             :   ///
     414             :   /// \warning This function will extrapolate if the target times are
     415             :   /// outside the range of data the class has been provided. This is intentional
     416             :   /// to support small extrapolation at the end of a simulation when no further
     417             :   /// data is available, but for full accuracy, check
     418             :   /// `first_time_is_ready_to_interpolate` before calling the interpolation
     419             :   /// functions
     420           1 :   std::pair<double, VectorTypeToInterpolate> interpolate_and_pop_first_time() {
     421             :     const auto lhs_interpolation =
     422             :         interpolation_manager_lhs_.interpolate_and_pop_first_time();
     423             :     const auto rhs_interpolation =
     424             :         interpolation_manager_rhs_.interpolate_and_pop_first_time();
     425             :     return std::make_pair(lhs_interpolation.first,
     426             :                           lhs_interpolation.second * rhs_interpolation.second);
     427             :   }
     428             : 
     429             :   /// \brief return the number of times in the target times queue
     430           1 :   size_t number_of_target_times() const {
     431             :     return interpolation_manager_lhs_.number_of_target_times();
     432             :   }
     433             : 
     434             :   /// \brief return the number of data points that have been provided to the
     435             :   /// interpolation manager
     436           1 :   size_t number_of_data_points() const {
     437             :     return interpolation_manager_lhs_.number_of_data_points();
     438             :   }
     439             : 
     440             :   // NOLINTNEXTLINE(google-runtime-references)
     441           0 :   void pup(PUP::er& p) {
     442             :     p | interpolation_manager_lhs_;
     443             :     p | interpolation_manager_rhs_;
     444             :   }
     445             : 
     446             :  private:
     447             :   /// \cond
     448             :   ScriPlusInterpolationManager<VectorTypeToInterpolate, MultipliesLhs>
     449             :       interpolation_manager_lhs_;
     450             :   ScriPlusInterpolationManager<VectorTypeToInterpolate, MultipliesRhs>
     451             :       interpolation_manager_rhs_;
     452             :   /// \endcond
     453             : };
     454             : 
     455             : /*!
     456             :  * \brief Stores necessary data and interpolates on to new time points at scri+,
     457             :  * differentiating before supplying the result.
     458             :  *
     459             :  * \details The coordinate time used for the CCE evolution is not the same as
     460             :  * the asymptotic inertial retarded time, which is determined through a separate
     461             :  * evolution equation. This class manages the scri+ data passed in
     462             :  * (via `insert_data()`) along with the set of inertial retarded times
     463             :  * associated with that data, and interpolates to a set of requested times
     464             :  * (supplied via `insert_target_time()`), differentiating the interpolation
     465             :  * before returning.
     466             :  *
     467             :  * Template parameters:
     468             :  * - `VectorTypeToInterpolate`: the vector type associated with the values to
     469             :  * interpolate.
     470             :  * - `Tags::Du<Tag>`: The tag associated with the interpolation procedure. This
     471             :  * determines the behavior of the interpolation return value. The default is
     472             :  * just interpolation, if `Tag` has prefix  `::Tags::Multiplies` or `Tags::Du`
     473             :  * (this case), the interpolator performs the additional multiplication or time
     474             :  * derivative as a step in the interpolation procedure.
     475             :  */
     476             : template <typename VectorTypeToInterpolate, typename Tag>
     477           1 : struct ScriPlusInterpolationManager<VectorTypeToInterpolate, Tags::Du<Tag>> {
     478             :  public:
     479           0 :   ScriPlusInterpolationManager() = default;
     480           0 :   ScriPlusInterpolationManager(
     481             :       const size_t target_number_of_points, const size_t vector_size,
     482             :       std::unique_ptr<intrp::SpanInterpolator> interpolator)
     483             :       : argument_interpolation_manager_{target_number_of_points, vector_size,
     484             :                                         std::move(interpolator)} {}
     485             : 
     486             :   /// \brief provide data to the interpolation manager.
     487             :   ///
     488             :   /// \details `u_bondi` is a vector of
     489             :   /// inertial times, and `to_interpolate_argument` is the vector of values that
     490             :   /// will be interpolated to target times and differentiated.
     491           1 :   void insert_data(const DataVector& u_bondi,
     492             :                    const VectorTypeToInterpolate& to_interpolate_argument) {
     493             :     argument_interpolation_manager_.insert_data(u_bondi,
     494             :                                                 to_interpolate_argument);
     495             :   }
     496             : 
     497             :   /// \brief Request a target time to be interpolated to when enough data has
     498             :   /// been accumulated.
     499             :   ///
     500             :   /// For optimization, we assume that these are inserted in ascending order.
     501           1 :   void insert_target_time(const double time) {
     502             :     argument_interpolation_manager_.insert_target_time(time);
     503             :   }
     504             : 
     505             :   /// \brief Determines whether enough data before and after the first time in
     506             :   /// the target time queue has been provided to interpolate.
     507             :   ///
     508             :   /// \details If possible, this function will require that the target time to
     509             :   /// be interpolated is reasonably centered on the range, but will settle for
     510             :   /// non-centered data if the time is too early for the given data, which is
     511             :   /// necessary to get the first couple of times out of the simulation. This
     512             :   /// function always returns false if all of the provided data is earlier than
     513             :   /// the next target time, indicating that the caller should wait until more
     514             :   /// data has been provided before interpolating.
     515           1 :   bool first_time_is_ready_to_interpolate() const {
     516             :     return argument_interpolation_manager_.first_time_is_ready_to_interpolate();
     517             :   }
     518             : 
     519           0 :   const std::deque<std::pair<double, double>>& get_u_bondi_ranges() const {
     520             :     return argument_interpolation_manager_.get_u_bondi_ranges();
     521             :   }
     522             : 
     523             :   /// \brief Interpolate to the first target time in the queue, returning both
     524             :   /// the time and the interpolated data at that time.
     525             :   ///
     526             :   /// \note If this function is not able to interpolate to the full accuracy
     527             :   /// using a centered stencil, it will perform the best interpolation
     528             :   /// available.
     529             :   ///
     530             :   /// \warning This function will extrapolate if the target times are
     531             :   /// outside the range of data the class has been provided. This is intentional
     532             :   /// to support small extrapolation at the end of a simulation when no further
     533             :   /// data is available, but for full accuracy, check
     534             :   /// `first_time_is_ready_to_interpolate` before calling the interpolation
     535             :   /// functions
     536           1 :   std::pair<double, VectorTypeToInterpolate> interpolate_first_time();
     537             : 
     538             :   /// \brief Interpolate to the first target time in the queue, returning both
     539             :   /// the time and the interpolated data at that time, and remove the first time
     540             :   /// from the queue.
     541             :   ///
     542             :   /// \note If this function is not able to interpolate to the full accuracy
     543             :   /// using a centered stencil, it will perform the best interpolation
     544             :   /// available.
     545             :   ///
     546             :   /// \warning This function will extrapolate if the target times are
     547             :   /// outside the range of data the class has been provided. This is intentional
     548             :   /// to support small extrapolation at the end of a simulation when no further
     549             :   /// data is available, but for full accuracy, check
     550             :   /// `first_time_is_ready_to_interpolate` before calling the interpolation
     551             :   /// functions
     552           1 :   std::pair<double, VectorTypeToInterpolate> interpolate_and_pop_first_time();
     553             : 
     554             :   /// \brief return the number of times in the target times queue
     555           1 :   size_t number_of_target_times() const {
     556             :     return argument_interpolation_manager_.number_of_target_times();
     557             :   }
     558             : 
     559             :   /// \brief return the number of data points that have been provided to the
     560             :   /// interpolation manager
     561           1 :   size_t number_of_data_points() const {
     562             :     return argument_interpolation_manager_.number_of_data_points();
     563             :   }
     564             : 
     565             :   // NOLINTNEXTLINE(google-runtime-references)
     566           0 :   void pup(PUP::er& p) { p | argument_interpolation_manager_; }
     567             : 
     568             :  private:
     569             :   // to avoid code duplication, most of the details are stored in an
     570             :   // interpolation manager for the argument tag, and this class is a friend of
     571             :   // the argument tag class
     572             :   /// \cond
     573             :   ScriPlusInterpolationManager<VectorTypeToInterpolate, Tag>
     574             :       argument_interpolation_manager_;
     575             :   /// \endcond
     576             : };
     577             : 
     578             : template <typename VectorTypeToInterpolate, typename Tag>
     579             : std::pair<double, VectorTypeToInterpolate> ScriPlusInterpolationManager<
     580             :     VectorTypeToInterpolate, Tags::Du<Tag>>::interpolate_first_time() {
     581             :   const size_t target_number_of_points =
     582             :       argument_interpolation_manager_.target_number_of_points_;
     583             :   if (argument_interpolation_manager_.target_times_.empty()) {
     584             :     ERROR("There are no target times to interpolate.");
     585             :   }
     586             :   if (argument_interpolation_manager_.to_interpolate_values_.size() <
     587             :       2 * argument_interpolation_manager_.target_number_of_points_) {
     588             :     ERROR("Insufficient data points to continue interpolation: have "
     589             :           << argument_interpolation_manager_.to_interpolate_values_.size()
     590             :           << ", need at least"
     591             :           << 2 * argument_interpolation_manager_.target_number_of_points_);
     592             :   }
     593             :   // note that because we demand at least a certain number before and at least
     594             :   // a certain number after, we are likely to have a surfeit of points for the
     595             :   // interpolator, but this should not cause significant trouble for a
     596             :   // reasonable method.
     597             :   VectorTypeToInterpolate result{argument_interpolation_manager_.vector_size_};
     598             : 
     599             :   VectorTypeToInterpolate interpolation_values{2 * target_number_of_points};
     600             :   VectorTypeToInterpolate lobatto_collocation_values{2 *
     601             :                                                      target_number_of_points};
     602             :   VectorTypeToInterpolate derivative_lobatto_collocation_values{
     603             :       2 * target_number_of_points};
     604             :   DataVector interpolation_times{2 * target_number_of_points};
     605             :   DataVector collocation_points =
     606             :       Spectral::collocation_points<Spectral::Basis::Legendre,
     607             :                                    Spectral::Quadrature::GaussLobatto>(
     608             :           2 * target_number_of_points);
     609             : 
     610             :   const size_t interpolation_data_size =
     611             :       argument_interpolation_manager_.to_interpolate_values_.size();
     612             : 
     613             :   for (size_t i = 0; i < argument_interpolation_manager_.vector_size_; ++i) {
     614             :     // binary search assumes times placed in sorted order
     615             :     auto upper_bound_offset = static_cast<size_t>(std::distance(
     616             :         argument_interpolation_manager_.u_bondi_values_.begin(),
     617             :         std::upper_bound(
     618             :             argument_interpolation_manager_.u_bondi_values_.begin(),
     619             :             argument_interpolation_manager_.u_bondi_values_.end(),
     620             :             argument_interpolation_manager_.target_times_.front(),
     621             :             [&i](const double rhs, const DataVector& lhs) {
     622             :               return rhs < lhs[i];
     623             :             })));
     624             :     size_t lower_bound_offset =
     625             :         upper_bound_offset == 0 ? 0 : upper_bound_offset - 1;
     626             : 
     627             :     if (upper_bound_offset + target_number_of_points >
     628             :         interpolation_data_size) {
     629             :       upper_bound_offset = interpolation_data_size;
     630             :       lower_bound_offset =
     631             :           interpolation_data_size - 2 * target_number_of_points;
     632             :     } else if (lower_bound_offset < target_number_of_points - 1) {
     633             :       lower_bound_offset = 0;
     634             :       upper_bound_offset = 2 * target_number_of_points;
     635             :     } else {
     636             :       lower_bound_offset = lower_bound_offset + 1 - target_number_of_points;
     637             :       upper_bound_offset = lower_bound_offset + 2 * target_number_of_points;
     638             :     }
     639             :     auto interpolation_values_begin =
     640             :         argument_interpolation_manager_.to_interpolate_values_.begin() +
     641             :         static_cast<ptrdiff_t>(lower_bound_offset);
     642             :     auto interpolation_times_begin =
     643             :         argument_interpolation_manager_.u_bondi_values_.begin() +
     644             :         static_cast<ptrdiff_t>(lower_bound_offset);
     645             :     auto interpolation_times_end =
     646             :         argument_interpolation_manager_.u_bondi_values_.begin() +
     647             :         static_cast<ptrdiff_t>(upper_bound_offset);
     648             : 
     649             :     // interpolate using the data sets in the restricted iterators
     650             :     auto value_it = interpolation_values_begin;
     651             :     size_t vector_position = 0;
     652             :     for (auto time_it = interpolation_times_begin;
     653             :          time_it != interpolation_times_end;
     654             :          ++time_it, ++value_it, ++vector_position) {
     655             :       interpolation_values[vector_position] = (*value_it)[i];
     656             :       interpolation_times[vector_position] = (*time_it)[i];
     657             :     }
     658             :     for (size_t j = 0; j < lobatto_collocation_values.size(); ++j) {
     659             :       lobatto_collocation_values[j] =
     660             :           argument_interpolation_manager_.interpolator_->interpolate(
     661             :               gsl::span<const double>(interpolation_times.data(),
     662             :                                       interpolation_times.size()),
     663             :               gsl::span<const typename VectorTypeToInterpolate::value_type>(
     664             :                   interpolation_values.data(), interpolation_values.size()),
     665             :               // affine transformation between the Gauss-Lobatto collocation
     666             :               // points and the physical times
     667             :               (collocation_points[j] + 1.0) * 0.5 *
     668             :                       (interpolation_times[interpolation_times.size() - 1] -
     669             :                        interpolation_times[0]) +
     670             :                   interpolation_times[0]);
     671             :     }
     672             :     // note the coordinate transformation to and from the Gauss-Lobatto basis
     673             :     // range [-1, 1]
     674             :     apply_matrices(
     675             :         make_not_null(&derivative_lobatto_collocation_values),
     676             :         make_array<1>(
     677             :             Spectral::differentiation_matrix<
     678             :                 Spectral::Basis::Legendre, Spectral::Quadrature::GaussLobatto>(
     679             :                 lobatto_collocation_values.size())),
     680             :         lobatto_collocation_values,
     681             :         Index<1>(lobatto_collocation_values.size()));
     682             : 
     683             :     result[i] =
     684             :         argument_interpolation_manager_.interpolator_->interpolate(
     685             :             gsl::span<const double>(collocation_points.data(),
     686             :                                     collocation_points.size()),
     687             :             gsl::span<const typename VectorTypeToInterpolate::value_type>(
     688             :                 derivative_lobatto_collocation_values.data(),
     689             :                 derivative_lobatto_collocation_values.size()),
     690             :             2.0 *
     691             :                     (argument_interpolation_manager_.target_times_.front() -
     692             :                      interpolation_times[0]) /
     693             :                     (interpolation_times[interpolation_times.size() - 1] -
     694             :                      interpolation_times[0]) -
     695             :                 1.0) *
     696             :         2.0 /
     697             :         (interpolation_times[interpolation_times.size() - 1] -
     698             :          interpolation_times[0]);
     699             :   }
     700             :   return std::make_pair(argument_interpolation_manager_.target_times_.front(),
     701             :                         std::move(result));
     702             : }
     703             : 
     704             : template <typename VectorTypeToInterpolate, typename Tag>
     705             : std::pair<double, VectorTypeToInterpolate> ScriPlusInterpolationManager<
     706             :     VectorTypeToInterpolate, Tags::Du<Tag>>::interpolate_and_pop_first_time() {
     707             :   std::pair<double, VectorTypeToInterpolate> interpolated =
     708             :       interpolate_first_time();
     709             :   argument_interpolation_manager_.target_times_.pop_front();
     710             : 
     711             :   if (not argument_interpolation_manager_.target_times_.empty()) {
     712             :     argument_interpolation_manager_.remove_unneeded_early_times();
     713             :   }
     714             :   return interpolated;
     715             : }
     716             : }  // namespace Cce

Generated by: LCOV version 1.14