SpECTRE Documentation Coverage Report
Current view: top level - Evolution/Systems/Cce - ScriPlusInterpolationManager.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 53.3 % 45 24
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 <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 2.0-1