SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/Interpolation - InterpolationTargetDetail.hpp Hit Total Coverage
Commit: 1f2210958b4f38fdc0400907ee7c6d5af5111418 Lines: 0 1 0.0 %
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 <cstddef>
       7             : #include <ios>
       8             : #include <limits>
       9             : #include <memory>
      10             : #include <sstream>
      11             : #include <type_traits>
      12             : #include <unordered_map>
      13             : #include <unordered_set>
      14             : #include <utility>
      15             : #include <vector>
      16             : 
      17             : #include "DataStructures/DataBox/DataBox.hpp"
      18             : #include "DataStructures/DataVector.hpp"
      19             : #include "DataStructures/LinkedMessageId.hpp"
      20             : #include "DataStructures/Tensor/Metafunctions.hpp"
      21             : #include "DataStructures/Tensor/TypeAliases.hpp"
      22             : #include "DataStructures/VariablesTag.hpp"
      23             : #include "Domain/BlockLogicalCoordinates.hpp"
      24             : #include "Domain/CoordinateMaps/Composition.hpp"
      25             : #include "Domain/CoordinateMaps/Distribution.hpp"
      26             : #include "Domain/Creators/Tags/Domain.hpp"
      27             : #include "Domain/ElementToBlockLogicalMap.hpp"
      28             : #include "Domain/Structure/ElementId.hpp"
      29             : #include "Domain/TagsTimeDependent.hpp"
      30             : #include "IO/Logging/Verbosity.hpp"
      31             : #include "Parallel/GlobalCache.hpp"
      32             : #include "Parallel/Invoke.hpp"
      33             : #include "Parallel/ParallelComponentHelpers.hpp"
      34             : #include "Parallel/Printf/Printf.hpp"
      35             : #include "ParallelAlgorithms/Interpolation/Tags.hpp"
      36             : #include "ParallelAlgorithms/Interpolation/TagsMetafunctions.hpp"
      37             : #include "Utilities/Algorithm.hpp"
      38             : #include "Utilities/ErrorHandling/Assert.hpp"
      39             : #include "Utilities/Gsl.hpp"
      40             : #include "Utilities/Literals.hpp"
      41             : #include "Utilities/PrettyType.hpp"
      42             : #include "Utilities/Requires.hpp"
      43             : #include "Utilities/System/ParallelInfo.hpp"
      44             : #include "Utilities/TMPL.hpp"
      45             : #include "Utilities/TaggedTuple.hpp"
      46             : #include "Utilities/TypeTraits.hpp"
      47             : #include "Utilities/TypeTraits/CreateHasStaticMemberVariable.hpp"
      48             : #include "Utilities/TypeTraits/CreateHasTypeAlias.hpp"
      49             : #include "Utilities/TypeTraits/CreateIsCallable.hpp"
      50             : #include "Utilities/TypeTraits/IsA.hpp"
      51             : 
      52             : /// \cond
      53             : 
      54             : namespace intrp {
      55             : template <class Metavariables>
      56             : struct Interpolator;
      57             : template <typename Metavariables, typename InterpolationTargetTag>
      58             : class InterpolationTarget;
      59             : namespace Actions {
      60             : template <typename InterpolationTargetTag>
      61             : struct CleanUpInterpolator;
      62             : template <typename InterpolationTargetTag>
      63             : struct ReceivePoints;
      64             : }  // namespace Actions
      65             : namespace Tags {
      66             : template <typename TemporalId>
      67             : struct IndicesOfFilledInterpPoints;
      68             : template <typename TemporalId>
      69             : struct IndicesOfInvalidInterpPoints;
      70             : template <typename InterpolationTargetTag, typename TemporalId>
      71             : struct InterpolatedVars;
      72             : template <typename TemporalId>
      73             : struct CompletedTemporalIds;
      74             : template <typename TemporalId>
      75             : struct PendingTemporalIds;
      76             : template <typename TemporalId>
      77             : struct CurrentTemporalId;
      78             : template <typename TemporalId>
      79             : struct TemporalIds;
      80             : }  // namespace Tags
      81             : namespace TargetPoints {
      82             : template <typename InterpolationTargetTag, typename Frame>
      83             : struct Sphere;
      84             : }  // namespace TargetPoints
      85             : }  // namespace intrp
      86             : template <typename Id>
      87             : struct LinkedMessageId;
      88             : class TimeStepId;
      89             : template <typename TagsList>
      90             : struct Variables;
      91             : /// \endcond
      92             : 
      93             : namespace intrp::InterpolationTarget_detail {
      94             : template <typename InterpolationTarget>
      95             : struct get_interpolation_target_tag {
      96             :   using type = typename InterpolationTarget::interpolation_target_tag;
      97             : };
      98             : 
      99             : template <typename InterpolationTargetTag>
     100             : struct is_sequential
     101             :     : InterpolationTargetTag::compute_target_points::is_sequential {};
     102             : 
     103             : template <typename InterpolationTargetTag>
     104             : struct is_non_sequential
     105             :     : std::bool_constant<not InterpolationTargetTag::compute_target_points::
     106             :                              is_sequential::value> {};
     107             : 
     108             : CREATE_GET_TYPE_ALIAS_OR_DEFAULT(component_being_mocked)
     109             : 
     110             : template <typename Metavariables>
     111             : using get_sequential_target_tags = tmpl::filter<
     112             :     tmpl::transform<
     113             :         tmpl::filter<tmpl::transform<typename Metavariables::component_list,
     114             :                                      get_component_being_mocked_or_default<
     115             :                                          tmpl::_1, tmpl::_1>>,
     116             :                      tt::is_a<intrp::InterpolationTarget, tmpl::_1>>,
     117             :         get_interpolation_target_tag<tmpl::_1>>,
     118             :     is_sequential<tmpl::_1>>;
     119             : 
     120             : template <typename AllInterpolationTargetTags>
     121             : using get_non_sequential_target_tags =
     122             :     tmpl::filter<AllInterpolationTargetTags, is_non_sequential<tmpl::_1>>;
     123             : 
     124             : CREATE_IS_CALLABLE(substep_time)
     125             : CREATE_IS_CALLABLE_V(substep_time)
     126             : template <typename T>
     127             : double get_temporal_id_value(const T& id) {
     128             :   if constexpr (std::is_same_v<T, LinkedMessageId<double>>) {
     129             :     return id.id;
     130             :   } else if constexpr (is_substep_time_callable_v<T>) {
     131             :     return id.substep_time();
     132             :   } else {
     133             :     static_assert(std::is_same_v<T, double>,
     134             :                   "get_temporal_id_value only supports 'double', "
     135             :                   "'LinkedMessageId<double>', or 'TimeStepId'.");
     136             :     return id;
     137             :   }
     138             : }
     139             : 
     140             : template <typename InterpolationTargetTag, typename TemporalId>
     141             : std::string interpolator_output_prefix(const TemporalId& temporal_id) {
     142             :   std::stringstream ss{};
     143             :   ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)
     144             :      << std::scientific;
     145             :   ss << "Interpolator, " << pretty_type::name<InterpolationTargetTag>() << ", "
     146             :      << temporal_id << ", WC " << sys::wall_time();
     147             : 
     148             :   return ss.str();
     149             : }
     150             : 
     151             : template <size_t Dim, typename TemporalId>
     152             : std::string interpolator_output_prefix(const ElementId<Dim>& element_id,
     153             :                                        const TemporalId& temporal_id) {
     154             :   std::stringstream ss{};
     155             :   ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)
     156             :      << std::scientific;
     157             :   ss << "Interpolator, " << element_id << ", " << temporal_id << ", WC "
     158             :      << sys::wall_time();
     159             : 
     160             :   return ss.str();
     161             : }
     162             : 
     163             : template <typename Action, typename InterpolationTargetTag, typename TemporalId>
     164             : std::string target_output_prefix(const TemporalId& temporal_id) {
     165             :   std::stringstream ss{};
     166             :   ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)
     167             :      << std::scientific;
     168             :   ss << pretty_type::name<Action>() << ", "
     169             :      << pretty_type::name<InterpolationTargetTag>() << ", " << temporal_id
     170             :      << ", WC " << sys::wall_time();
     171             : 
     172             :   return ss.str();
     173             : }
     174             : 
     175             : template <typename Action, typename InterpolationTargetTag>
     176             : std::string target_output_prefix() {
     177             :   std::stringstream ss{};
     178             :   ss << std::setprecision(std::numeric_limits<double>::digits10 + 4)
     179             :      << std::scientific;
     180             :   ss << pretty_type::name<Action>() << ", "
     181             :      << pretty_type::name<InterpolationTargetTag>() << ", WC "
     182             :      << sys::wall_time();
     183             : 
     184             :   return ss.str();
     185             : }
     186             : 
     187             : CREATE_IS_CALLABLE(apply)
     188             : 
     189             : template <typename T, typename DbTags, typename Metavariables,
     190             :           typename TemporalId>
     191             : bool apply_callbacks(
     192             :     const gsl::not_null<db::DataBox<DbTags>*> box,
     193             :     const gsl::not_null<Parallel::GlobalCache<Metavariables>*> cache,
     194             :     const TemporalId& temporal_id) {
     195             :   using callbacks = typename T::post_interpolation_callbacks;
     196             :   constexpr bool all_simple_callbacks =
     197             :       tmpl::all<callbacks,
     198             :                 is_apply_callable<tmpl::_1, tmpl::pin<decltype(*box)>,
     199             :                                   tmpl::pin<decltype(*cache)>,
     200             :                                   tmpl::pin<decltype(temporal_id)>>>::value;
     201             :   if constexpr (all_simple_callbacks) {
     202             :     tmpl::for_each<callbacks>([&](const auto callback_v) {
     203             :       using callback = tmpl::type_from<decltype(callback_v)>;
     204             :       callback::apply(*box, *cache, temporal_id);
     205             :     });
     206             : 
     207             :     // For the simpler callback function, we will always clean up volume data,
     208             :     // so we return true here.
     209             :     return true;
     210             :   } else {
     211             :     static_assert(tmpl::size<callbacks>::value == 1,
     212             :                   "Can only have 1 post_interpolation_callbacks that mutates "
     213             :                   "the target box.");
     214             :     return tmpl::front<callbacks>::apply(box, cache, temporal_id);
     215             :   }
     216             : }
     217             : 
     218             : CREATE_HAS_STATIC_MEMBER_VARIABLE(fill_invalid_points_with)
     219             : CREATE_HAS_STATIC_MEMBER_VARIABLE_V(fill_invalid_points_with)
     220             : 
     221             : template <typename T, bool B>
     222             : struct fill_value_or_no_such_type;
     223             : 
     224             : template <typename T>
     225             : struct fill_value_or_no_such_type<T, false> {
     226             :   using type = NoSuchType;
     227             : };
     228             : 
     229             : template <typename T>
     230             : struct DoubleWrapper {
     231             :   static constexpr double value = T::fill_invalid_points_with;
     232             : };
     233             : 
     234             : template <typename T>
     235             : struct fill_value_or_no_such_type<T, true> {
     236             :   using type = DoubleWrapper<T>;
     237             : };
     238             : 
     239             : template <typename Callback>
     240             : struct get_fill_value {
     241             :   using type = typename fill_value_or_no_such_type<
     242             :       Callback, has_fill_invalid_points_with_v<Callback>>::type;
     243             : };
     244             : 
     245             : template <typename Callbacks>
     246             : struct get_invalid_fill {
     247             :   using type =
     248             :       tmpl::filter<tmpl::transform<Callbacks, get_fill_value<tmpl::_1>>,
     249             :                    tt::is_a<DoubleWrapper, tmpl::_1>>;
     250             : };
     251             : 
     252             : template <typename InterpolationTargetTag, typename TemporalId, typename DbTags>
     253             : void fill_invalid_points(const gsl::not_null<db::DataBox<DbTags>*> box,
     254             :                          const TemporalId& temporal_id) {
     255             :   using callbacks =
     256             :       typename InterpolationTargetTag::post_interpolation_callbacks;
     257             :   using invalid_fill = typename get_invalid_fill<callbacks>::type;
     258             :   if constexpr (tmpl::size<invalid_fill>::value > 0) {
     259             :     const auto& invalid_indices =
     260             :         db::get<Tags::IndicesOfInvalidInterpPoints<TemporalId>>(*box);
     261             :     if (invalid_indices.find(temporal_id) != invalid_indices.end() and
     262             :         not invalid_indices.at(temporal_id).empty()) {
     263             :       db::mutate<Tags::IndicesOfInvalidInterpPoints<TemporalId>,
     264             :                  Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>(
     265             :           [&temporal_id](
     266             :               const gsl::not_null<
     267             :                   std::unordered_map<TemporalId, std::unordered_set<size_t>>*>
     268             :                   indices_of_invalid_points,
     269             :               const gsl::not_null<std::unordered_map<
     270             :                   TemporalId, Variables<typename InterpolationTargetTag::
     271             :                                             vars_to_interpolate_to_target>>*>
     272             :                   vars_dest_all_times) {
     273             :             auto& vars_dest = vars_dest_all_times->at(temporal_id);
     274             :             const size_t npts_dest = vars_dest.number_of_grid_points();
     275             :             const size_t nvars = vars_dest.number_of_independent_components;
     276             :             for (auto index : indices_of_invalid_points->at(temporal_id)) {
     277             :               for (size_t v = 0; v < nvars; ++v) {
     278             :                 // clang-tidy: no pointer arithmetic
     279             :                 vars_dest.data()[index + v * npts_dest] =  // NOLINT
     280             :                     tmpl::front<invalid_fill>::value;
     281             :               }
     282             :             }
     283             :             // Further functions may test if there are invalid points.
     284             :             // Clear the invalid points now, since we have filled them.
     285             :             indices_of_invalid_points->erase(temporal_id);
     286             :           },
     287             :           box);
     288             :     }
     289             :   } else {
     290             :     (void)box;
     291             :     (void)temporal_id;
     292             :   }
     293             : }
     294             : 
     295             : /// Wraps calling the callback function on an InterpolationTarget.
     296             : ///
     297             : /// First prepares for the callback, then calls the callback,
     298             : /// and returns true if the callback is done and
     299             : /// false if the callback is not done (e.g. if the callback is an
     300             : /// apparent horizon search and it needs another iteration).
     301             : ///
     302             : /// call_callbacks is called by an Action of InterpolationTarget.
     303             : ///
     304             : /// Currently two Actions call call_callbacks:
     305             : /// - InterpolationTargetReceiveVars (called by Interpolator ParallelComponent)
     306             : /// - InterpolationTargetVarsFromElement (called by DgElementArray)
     307             : template <typename InterpolationTargetTag, typename DbTags,
     308             :           typename Metavariables, typename TemporalId>
     309             : bool call_callbacks(
     310             :     const gsl::not_null<db::DataBox<DbTags>*> box,
     311             :     const gsl::not_null<Parallel::GlobalCache<Metavariables>*> cache,
     312             :     const TemporalId& temporal_id) {
     313             :   // Before doing anything else, deal with the possibility that some
     314             :   // of the points might be outside of the Domain.
     315             :   fill_invalid_points<InterpolationTargetTag>(box, temporal_id);
     316             : 
     317             :   // Fill ::Tags::Variables<typename
     318             :   //      InterpolationTargetTag::vars_to_interpolate_to_target>
     319             :   // with variables from correct temporal_id.
     320             :   db::mutate_apply<
     321             :       tmpl::list<::Tags::Variables<
     322             :           typename InterpolationTargetTag::vars_to_interpolate_to_target>>,
     323             :       tmpl::list<Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>>(
     324             :       [&temporal_id](
     325             :           const gsl::not_null<Variables<
     326             :               typename InterpolationTargetTag::vars_to_interpolate_to_target>*>
     327             :               vars,
     328             :           const std::unordered_map<
     329             :               TemporalId, Variables<typename InterpolationTargetTag::
     330             :                                         vars_to_interpolate_to_target>>&
     331             :               vars_at_all_times) { *vars = vars_at_all_times.at(temporal_id); },
     332             :       box);
     333             : 
     334             :   // apply_callbacks should return true if we are done with this
     335             :   // temporal_id.  It should return false only if the callback
     336             :   // calls another `intrp::Action` that needs the volume data at this
     337             :   // same temporal_id.
     338             :   return apply_callbacks<InterpolationTargetTag>(box, cache, temporal_id);
     339             : }
     340             : 
     341             : /// Frees InterpolationTarget's memory associated with the supplied
     342             : /// temporal_id.
     343             : ///
     344             : /// clean_up_interpolation_target is called by an Action of InterpolationTarget.
     345             : ///
     346             : /// Currently two Actions call clean_up_interpolation_target:
     347             : /// - InterpolationTargetReceiveVars (called by Interpolator ParallelComponent)
     348             : /// - InterpolationTargetVarsFromElement (called by DgElementArray)
     349             : template <typename InterpolationTargetTag, typename DbTags, typename TemporalId>
     350             : void clean_up_interpolation_target(
     351             :     const gsl::not_null<db::DataBox<DbTags>*> box,
     352             :     const TemporalId& temporal_id) {
     353             :   // We are now done with this temporal_id, so we can remove it and
     354             :   // clean up data associated with it.
     355             :   if constexpr (InterpolationTargetTag::compute_target_points::is_sequential::
     356             :                     value) {
     357             :     db::mutate<Tags::CurrentTemporalId<TemporalId>>(
     358             :         [&temporal_id](const gsl::not_null<std::optional<TemporalId>*> id) {
     359             :           using ::operator<<;
     360             :           ASSERT(id->has_value(),
     361             :                  "Cleaning up the interpolation target but the current "
     362             :                  "temporal id doesn't have a value.");
     363             :           ASSERT(id->value() == temporal_id,
     364             :                  "Cleaning up the interpolation target, but the current "
     365             :                  "temporal id "
     366             :                      << id->value() << " isn't the same as the one passed in "
     367             :                      << temporal_id);
     368             :           id->reset();
     369             :         },
     370             :         box);
     371             :   } else {
     372             :     db::mutate<Tags::TemporalIds<TemporalId>>(
     373             :         [&temporal_id](
     374             :             const gsl::not_null<std::unordered_set<TemporalId>*> ids) {
     375             :           ASSERT(ids->contains(temporal_id),
     376             :                  "Temporal id " << temporal_id << " does not exist in ids "
     377             :                                 << *ids);
     378             :           ids->erase(temporal_id);
     379             :         },
     380             :         box);
     381             :   }
     382             :   db::mutate<Tags::CompletedTemporalIds<TemporalId>,
     383             :              Tags::Dependencies<TemporalId>,
     384             :              Tags::IndicesOfFilledInterpPoints<TemporalId>,
     385             :              Tags::IndicesOfInvalidInterpPoints<TemporalId>,
     386             :              Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>(
     387             :       [&temporal_id](
     388             :           const gsl::not_null<std::deque<TemporalId>*> completed_ids,
     389             :           const gsl::not_null<
     390             :               std::unordered_map<TemporalId, std::optional<std::string>>*>
     391             :               dependencies,
     392             :           const gsl::not_null<
     393             :               std::unordered_map<TemporalId, std::unordered_set<size_t>>*>
     394             :               indices_of_filled,
     395             :           const gsl::not_null<
     396             :               std::unordered_map<TemporalId, std::unordered_set<size_t>>*>
     397             :               indices_of_invalid,
     398             :           const gsl::not_null<std::unordered_map<
     399             :               TemporalId, Variables<typename InterpolationTargetTag::
     400             :                                         vars_to_interpolate_to_target>>*>
     401             :               interpolated_vars) {
     402             :         completed_ids->push_back(temporal_id);
     403             :         // We want to keep track of all completed temporal_ids to deal with
     404             :         // the possibility of late calls to
     405             :         // AddTemporalIdsToInterpolationTarget.  We could keep all
     406             :         // completed_ids forever, but we probably don't want it to get too
     407             :         // large, so we limit its size.  We assume that
     408             :         // asynchronous calls to AddTemporalIdsToInterpolationTarget do not span
     409             :         // more than 1000 temporal_ids.
     410             :         if (completed_ids->size() > 1000) {
     411             :           completed_ids->pop_front();
     412             :         }
     413             :         dependencies->erase(temporal_id);
     414             :         indices_of_filled->erase(temporal_id);
     415             :         indices_of_invalid->erase(temporal_id);
     416             :         interpolated_vars->erase(temporal_id);
     417             :       },
     418             :       box);
     419             : }
     420             : 
     421             : struct HaveDataAtAllPoints {};
     422             : /// Returns true if this InterpolationTarget has received data
     423             : /// at all its points.
     424             : ///
     425             : /// have_data_at_all_points is called by an Action of InterpolationTarget.
     426             : ///
     427             : /// Since this will be called often, output is only printed to stdout if the
     428             : /// verbosity is greater than or equal to `::Verbosity::Debug`.
     429             : ///
     430             : /// Currently two Actions call have_data_at_all_points:
     431             : /// - InterpolationTargetReceiveVars (called by Interpolator ParallelComponent)
     432             : /// - InterpolationTargetVarsFromElement (called by DgElementArray)
     433             : template <typename InterpolationTargetTag, typename DbTags, typename TemporalId>
     434             : bool have_data_at_all_points(
     435             :     const db::DataBox<DbTags>& box, const TemporalId& temporal_id,
     436             :     const ::Verbosity verbosity = ::Verbosity::Silent) {
     437             :   const auto& filled_indices =
     438             :       db::get<Tags::IndicesOfFilledInterpPoints<TemporalId>>(box);
     439             : 
     440             :   const size_t filled_size = filled_indices.count(temporal_id) > 0
     441             :                                  ? filled_indices.at(temporal_id).size()
     442             :                                  : 0;
     443             :   const size_t invalid_size = [&box, &temporal_id]() {
     444             :     const auto& invalid_indices =
     445             :         db::get<Tags::IndicesOfInvalidInterpPoints<TemporalId>>(box);
     446             :     if (invalid_indices.count(temporal_id) > 0) {
     447             :       return invalid_indices.at(temporal_id).size();
     448             :     }
     449             :     return 0_st;
     450             :   }();
     451             :   const size_t interp_size =
     452             :       db::get<Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>(box)
     453             :           .at(temporal_id)
     454             :           .number_of_grid_points();
     455             :   if (verbosity >= ::Verbosity::Debug) {
     456             :     Parallel::printf(
     457             :         "%s, Total expected points = %d, valid points received = %d, "
     458             :         "invalid points received = %d\n",
     459             :         target_output_prefix<HaveDataAtAllPoints, InterpolationTargetTag>(
     460             :             temporal_id),
     461             :         interp_size, filled_size, invalid_size);
     462             :   }
     463             :   return (invalid_size + filled_size == interp_size);
     464             : }
     465             : 
     466             : /// Tells an InterpolationTarget that it should interpolate at
     467             : /// the supplied temporal_id.  Changes the InterpolationTarget's DataBox
     468             : /// accordingly.
     469             : ///
     470             : /// Returns a bool indicating if the passed in id needs to be set up
     471             : ///
     472             : /// flag_temporal_id_for_interpolation is called by an Action
     473             : /// of InterpolationTarget
     474             : ///
     475             : /// Currently one Action calls flag_temporal_id_for_interpolation:
     476             : /// - InterpolationTargetVarsFromElement (called by DgElementArray)
     477             : template <typename InterpolationTargetTag, typename DbTags, typename TemporalId>
     478             : bool flag_temporal_id_for_interpolation(
     479             :     const gsl::not_null<db::DataBox<DbTags>*> box,
     480             :     const TemporalId& temporal_id) {
     481             :   // We allow this function to be called multiple times with the same
     482             :   // temporal_ids (e.g. from each element, or from each node of a
     483             :   // NodeGroup ParallelComponent such as Interpolator). If multiple
     484             :   // calls occur, we care only about the first one, and ignore the
     485             :   // others.  The first call will often begin interpolation.  So if
     486             :   // multiple calls occur, it is possible that some of them may arrive
     487             :   // late, even after interpolation has been completed on one or more
     488             :   // of the temporal_ids (and after that id has already been removed
     489             :   // from `ids`).  If this happens, we don't want to add the
     490             :   // temporal_ids again. For that reason we keep track of the
     491             :   // temporal_ids that we have already completed interpolation on.  So
     492             :   // here we do not add any temporal_ids that are already present in
     493             :   // `ids` or `completed_ids`.
     494             :   bool id_has_been_flagged = false;
     495             : 
     496             :   db::mutate_apply<tmpl::list<Tags::TemporalIds<TemporalId>>,
     497             :                    tmpl::list<Tags::CompletedTemporalIds<TemporalId>>>(
     498             :       [&temporal_id, &id_has_been_flagged](
     499             :           const gsl::not_null<std::unordered_set<TemporalId>*> ids,
     500             :           const std::deque<TemporalId>& completed_ids) {
     501             :         if (not(alg::found(completed_ids, temporal_id) or
     502             :                 ids->contains(temporal_id))) {
     503             :           ids->insert(temporal_id);
     504             :           id_has_been_flagged = true;
     505             :         }
     506             :       },
     507             :       box);
     508             : 
     509             :   return id_has_been_flagged;
     510             : }
     511             : 
     512             : /// Tells an InterpolationTarget that it should interpolate at
     513             : /// the supplied temporal_ids.  Changes the InterpolationTarget's DataBox
     514             : /// accordingly. Also adds a dependency to the box at this time if there isn't
     515             : /// one already. If there's already one, ASSERTs that they are the same.
     516             : ///
     517             : /// flag_temporal_ids_as_pending is called by an Action
     518             : /// of InterpolationTarget
     519             : ///
     520             : /// Currently one Action calls flag_temporal_ids_as_pending:
     521             : /// - AddTemporalIdsToInterpolationTarget (called by Events::Interpolate)
     522             : template <typename InterpolationTargetTag, typename DbTags, typename TemporalId>
     523             : void flag_temporal_id_as_pending(const gsl::not_null<db::DataBox<DbTags>*> box,
     524             :                                  const TemporalId& temporal_id,
     525             :                                  std::optional<std::string> dependency) {
     526             :   // We allow this function to be called multiple times with the same
     527             :   // temporal_ids (e.g. from each element, or from each node of a
     528             :   // NodeGroup ParallelComponent such as Interpolator). If multiple
     529             :   // calls occur, we care only about the first one, and ignore the
     530             :   // others.  The first call will often begin interpolation.  So if
     531             :   // multiple calls occur, it is possible that some of them may arrive
     532             :   // late, even after interpolation has been completed on one or more
     533             :   // of the temporal_ids (and after that id has already been removed
     534             :   // from `ids`).  If this happens, we don't want to add the
     535             :   // temporal_id again. For that reason we keep track of the
     536             :   // temporal_ids that we have already completed interpolation on.  So
     537             :   // here we do not add any temporal_ids that are already present in
     538             :   // `id` or `completed_ids`.
     539             :   db::mutate_apply<tmpl::list<Tags::PendingTemporalIds<TemporalId>,
     540             :                               Tags::Dependencies<TemporalId>>,
     541             :                    tmpl::list<Tags::CurrentTemporalId<TemporalId>,
     542             :                               Tags::CompletedTemporalIds<TemporalId>>>(
     543             :       [&temporal_id, &dependency](
     544             :           const gsl::not_null<std::deque<TemporalId>*> pending_ids,
     545             :           const gsl::not_null<
     546             :               std::unordered_map<TemporalId, std::optional<std::string>>*>
     547             :               dependencies,
     548             :           const std::optional<TemporalId>& current_id,
     549             :           const std::deque<TemporalId>& completed_ids) {
     550             :         if (not(alg::found(completed_ids, temporal_id) or
     551             :                 (current_id.has_value() and
     552             :                  current_id.value() == temporal_id) or
     553             :                 alg::found(*pending_ids, temporal_id))) {
     554             :           pending_ids->push_back(temporal_id);
     555             : 
     556             :           alg::sort(*pending_ids);
     557             :         }
     558             : 
     559             :         if (dependencies->contains(temporal_id)) {
     560             :           ASSERT(dependencies->at(temporal_id) == dependency,
     561             :                  "Already have dependency at time "
     562             :                      << get_temporal_id_value(temporal_id) << ": "
     563             :                      << dependencies->at(temporal_id)
     564             :                      << ", which is not the same as the incoming one "
     565             :                      << dependency);
     566             :         } else {
     567             :           (*dependencies)[temporal_id] = std::move(dependency);
     568             :         }
     569             :       },
     570             :       box);
     571             : }
     572             : 
     573             : /// Adds the supplied interpolated variables and offsets to the
     574             : /// InterpolationTarget's internal DataBox.
     575             : ///
     576             : /// Note that the template argument to Variables in vars_src is called
     577             : /// InterpolationTargetTag::vars_to_interpolate_to_target.  This is a list
     578             : /// of tags, and is used for both the interpolated variables (in
     579             : /// this function add_received_variables) and for the source variables
     580             : /// (in other functions). The source and interpolated quantities are
     581             : /// the same set of variables (but at different points).
     582             : ///
     583             : /// add_received_variables is called by an Action of InterpolationTarget.
     584             : ///
     585             : /// Currently two Actions call add_received_variables:
     586             : /// - InterpolationTargetReceiveVars (called by Interpolator ParallelComponent)
     587             : /// - InterpolationTargetVarsFromElement (called by DgElementArray)
     588             : template <typename InterpolationTargetTag, typename DbTags, typename TemporalId>
     589             : void add_received_variables(
     590             :     const gsl::not_null<db::DataBox<DbTags>*> box,
     591             :     const std::vector<Variables<
     592             :         typename InterpolationTargetTag::vars_to_interpolate_to_target>>&
     593             :         vars_src,
     594             :     const std::vector<std::vector<size_t>>& global_offsets,
     595             :     const TemporalId& temporal_id) {
     596             :   db::mutate<Tags::IndicesOfFilledInterpPoints<TemporalId>,
     597             :              Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>(
     598             :       [&temporal_id, &vars_src, &global_offsets](
     599             :           const gsl::not_null<
     600             :               std::unordered_map<TemporalId, std::unordered_set<size_t>>*>
     601             :               indices_of_filled,
     602             :           const gsl::not_null<std::unordered_map<
     603             :               TemporalId, Variables<typename InterpolationTargetTag::
     604             :                                         vars_to_interpolate_to_target>>*>
     605             :               vars_dest_all_times) {
     606             :         auto& vars_dest = vars_dest_all_times->at(temporal_id);
     607             :         // Here we assume that vars_dest has been allocated to the correct
     608             :         // size (but could contain garbage, since below we are filling it).
     609             :         const size_t npts_dest = vars_dest.number_of_grid_points();
     610             :         const size_t nvars = vars_dest.number_of_independent_components;
     611             :         for (size_t j = 0; j < global_offsets.size(); ++j) {
     612             :           const size_t npts_src = global_offsets[j].size();
     613             :           for (size_t i = 0; i < npts_src; ++i) {
     614             :             // If a point is on the boundary of two (or more)
     615             :             // elements, it is possible that we have received data
     616             :             // for this point from more than one Interpolator.
     617             :             // This will rarely occur, but it does occur, e.g. when
     618             :             // a point is exactly on some symmetry
     619             :             // boundary (such as the x-y plane) and this symmetry
     620             :             // boundary is exactly the boundary between two
     621             :             // elements.  If this happens, we accept the first
     622             :             // duplicated point, and we ignore subsequent
     623             :             // duplicated points.  The points are easy to keep track
     624             :             // of because global_offsets uniquely identifies them.
     625             :             if ((*indices_of_filled)[temporal_id]
     626             :                     .insert(global_offsets[j][i])
     627             :                     .second) {
     628             :               for (size_t v = 0; v < nvars; ++v) {
     629             :                 // clang-tidy: no pointer arithmetic
     630             :                 vars_dest.data()[global_offsets[j][i] +    // NOLINT
     631             :                                  v * npts_dest] =          // NOLINT
     632             :                     vars_src[j].data()[i + v * npts_src];  // NOLINT
     633             :               }
     634             :             }
     635             :           }
     636             :         }
     637             :       },
     638             :       box);
     639             : }
     640             : 
     641             : /// Computes the block logical coordinates of an InterpolationTarget.
     642             : ///
     643             : /// block_logical_coords is called by an Action of InterpolationTarget.
     644             : ///
     645             : /// Currently one Action directly calls this version of block_logical_coords:
     646             : /// - InterpolationTargetSendTimeIndepPointsToElements
     647             : ///   (in InterpolationTarget ActionList)
     648             : /// and one Action indirectly calls this version of block_logical_coords:
     649             : /// - SendPointsToInterpolator (called by AddTemporalIdsToInterpolationTarget
     650             : ///                             and by FindApparentHorizon)
     651             : template <typename InterpolationTargetTag, typename Metavariables,
     652             :           typename TemporalId>
     653             : auto block_logical_coords(
     654             :     const Parallel::GlobalCache<Metavariables>& cache,
     655             :     const tnsr::I<
     656             :         DataVector, Metavariables::volume_dim,
     657             :         typename InterpolationTargetTag::compute_target_points::frame>& coords,
     658             :     const TemporalId& temporal_id) {
     659             :   const auto& domain =
     660             :       get<domain::Tags::Domain<Metavariables::volume_dim>>(cache);
     661             :   if constexpr (std::is_same_v<typename InterpolationTargetTag::
     662             :                                    compute_target_points::frame,
     663             :                                ::Frame::Grid>) {
     664             :     // Frame is grid frame, so don't need any FunctionsOfTime,
     665             :     // whether or not the maps are time_dependent.
     666             :     return ::block_logical_coordinates(domain, coords);
     667             :   }
     668             : 
     669             :   if (domain.is_time_dependent()) {
     670             :     if constexpr (Parallel::is_in_global_cache<Metavariables,
     671             :                                                domain::Tags::FunctionsOfTime>) {
     672             :       // Whoever calls block_logical_coords when the maps are
     673             :       // time-dependent is responsible for ensuring
     674             :       // that functions_of_time are up to date at temporal_id.
     675             :       const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);
     676             :       return ::block_logical_coordinates(
     677             :           domain, coords,
     678             :           InterpolationTarget_detail::get_temporal_id_value(temporal_id),
     679             :           functions_of_time);
     680             :     } else {
     681             :       // We error here because the maps are time-dependent, yet
     682             :       // the cache does not contain FunctionsOfTime.  It would be
     683             :       // nice to make this a compile-time error; however, we want
     684             :       // the code to compile for the completely time-independent
     685             :       // case where there are no FunctionsOfTime in the cache at
     686             :       // all.  Unfortunately, checking whether the maps are
     687             :       // time-dependent is currently not constexpr.
     688             :       ERROR(
     689             :           "There is a time-dependent CoordinateMap in at least one "
     690             :           "of the Blocks, but FunctionsOfTime are not in the "
     691             :           "GlobalCache.  If you intend to use a time-dependent "
     692             :           "CoordinateMap, please add FunctionsOfTime to the GlobalCache.");
     693             :     }
     694             :   }
     695             : 
     696             :   // Time-independent case.
     697             :   return ::block_logical_coordinates(domain, coords);
     698             : }
     699             : 
     700             : /// Version of block_logical_coords that computes the interpolation
     701             : /// points on the fly.  This version is called when the interpolation
     702             : /// points are (or might be) time-dependent in the
     703             : /// InterpolationTargetTag's frame.
     704             : ///
     705             : /// This version of block_logical_coordinates is called when there
     706             : /// is an Interpolator ParallelComponent.
     707             : ///
     708             : /// Currently one Action directly calls this version of block_logical_coords:
     709             : /// - SendPointsToInterpolator (called by AddTemporalIdsToInterpolationTarget
     710             : ///                             and by FindApparentHorizon)
     711             : template <typename InterpolationTargetTag, typename DbTags,
     712             :           typename Metavariables, typename TemporalId>
     713             : auto block_logical_coords(db::DataBox<DbTags>& box,
     714             :                           const Parallel::GlobalCache<Metavariables>& cache,
     715             :                           const TemporalId& temporal_id) {
     716             :   return block_logical_coords<InterpolationTargetTag>(
     717             :       cache,
     718             :       InterpolationTargetTag::compute_target_points::points(
     719             :           box, tmpl::type_<Metavariables>{}, temporal_id),
     720             :       temporal_id);
     721             : }
     722             : 
     723             : /// Version of block_logical_coords for when the coords are
     724             : /// time-independent.
     725             : template <typename InterpolationTargetTag, typename DbTags,
     726             :           typename Metavariables>
     727             : auto block_logical_coords(const db::DataBox<DbTags>& box,
     728             :                           const tmpl::type_<Metavariables>& meta) {
     729             :   const auto& domain =
     730             :       db::get<domain::Tags::Domain<Metavariables::volume_dim>>(box);
     731             :   return ::block_logical_coordinates(
     732             :       domain, InterpolationTargetTag::compute_target_points::points(box, meta));
     733             : }
     734             : 
     735             : /// Initializes InterpolationTarget's variables storage and lists of indices
     736             : /// corresponding to the supplied block logical coordinates and `temporal_id`.
     737             : ///
     738             : /// set_up_interpolation is called by an Action of InterpolationTarget.
     739             : ///
     740             : /// Currently two Actions call set_up_interpolation:
     741             : /// - SendPointsToInterpolator (called by AddTemporalIdsToInterpolationTarget
     742             : ///                             and by FindApparentHorizon)
     743             : /// - InterpolationTargetVarsFromElement (called by DgElementArray)
     744             : template <typename InterpolationTargetTag, typename DbTags, size_t VolumeDim,
     745             :           typename TemporalId>
     746             : void set_up_interpolation(
     747             :     const gsl::not_null<db::DataBox<DbTags>*> box,
     748             :     const TemporalId& temporal_id,
     749             :     const std::vector<BlockLogicalCoords<VolumeDim>>& block_logical_coords) {
     750             :   db::mutate<Tags::IndicesOfFilledInterpPoints<TemporalId>,
     751             :              Tags::IndicesOfInvalidInterpPoints<TemporalId>,
     752             :              Tags::InterpolatedVars<InterpolationTargetTag, TemporalId>>(
     753             :       [&block_logical_coords, &temporal_id](
     754             :           const gsl::not_null<
     755             :               std::unordered_map<TemporalId, std::unordered_set<size_t>>*>
     756             :               indices_of_filled,
     757             :           const gsl::not_null<
     758             :               std::unordered_map<TemporalId, std::unordered_set<size_t>>*>
     759             :               indices_of_invalid_points,
     760             :           const gsl::not_null<std::unordered_map<
     761             :               TemporalId, Variables<typename InterpolationTargetTag::
     762             :                                         vars_to_interpolate_to_target>>*>
     763             :               vars_dest_all_times) {
     764             :         // Because we are sending new points to the interpolator,
     765             :         // we know that none of these points have been interpolated to,
     766             :         // so clear the list.
     767             :         indices_of_filled->erase(temporal_id);
     768             : 
     769             :         // Set the indices of invalid points.
     770             :         indices_of_invalid_points->erase(temporal_id);
     771             :         for (size_t i = 0; i < block_logical_coords.size(); ++i) {
     772             :           // The sphere target is optimized specially. Because of this, a
     773             :           // nullopt in block_logical_coords from the sphere target doesn't
     774             :           // actually mean the point is invalid. Therefore we ignore this check
     775             :           // for the sphere target. The downside of this is that we don't catch
     776             :           // invalid points here.
     777             :           constexpr bool is_sphere = tt::is_a_v<
     778             :               TargetPoints::Sphere,
     779             :               typename InterpolationTargetTag::compute_target_points>;
     780             :           if (not is_sphere and not block_logical_coords[i].has_value()) {
     781             :             (*indices_of_invalid_points)[temporal_id].insert(i);
     782             :           }
     783             :         }
     784             : 
     785             :         // At this point we don't know if vars_dest exists in the map;
     786             :         // if it doesn't then we want to default construct it.
     787             :         auto& vars_dest = (*vars_dest_all_times)[temporal_id];
     788             : 
     789             :         // We will be filling vars_dest with interpolated data.
     790             :         // Here we make sure it is allocated to the correct size.
     791             :         if (vars_dest.number_of_grid_points() != block_logical_coords.size()) {
     792             :           vars_dest = Variables<
     793             :               typename InterpolationTargetTag::vars_to_interpolate_to_target>(
     794             :               block_logical_coords.size());
     795             :         }
     796             :       },
     797             :       box);
     798             : }
     799             : 
     800             : CREATE_HAS_TYPE_ALIAS(compute_vars_to_interpolate)
     801             : CREATE_HAS_TYPE_ALIAS_V(compute_vars_to_interpolate)
     802             : 
     803             : namespace detail {
     804             : template <typename T>
     805             : struct is_an_invjac_impl : std::false_type {};
     806             : 
     807             : template <typename Arg1, size_t Arg2, typename Arg3, typename Arg4>
     808             : struct is_an_invjac_impl<InverseJacobian<Arg1, Arg2, Arg3, Arg4>>
     809             :     : std::true_type {};
     810             : 
     811             : template <typename Tag>
     812             : struct is_an_invjac : is_an_invjac_impl<typename Tag::type> {};
     813             : 
     814             : template <typename Tag, typename Frame>
     815             : using any_index_in_frame_impl =
     816             :     TensorMetafunctions::any_index_in_frame<typename Tag::type, Frame>;
     817             : }  // namespace detail
     818             : 
     819             : /// Returns true if any of the tensors in TagList have any of their
     820             : /// indices in the given frame. We don't count InverseJacobians because these
     821             : /// mess up the frames
     822             : template <typename TagList, typename Frame>
     823             : constexpr bool any_index_in_frame_v = tmpl::any<
     824             :     tmpl::filter<TagList, tmpl::not_<detail::is_an_invjac<tmpl::_1>>>,
     825             :     tmpl::bind<detail::any_index_in_frame_impl, tmpl::_1, Frame>>::value;
     826             : 
     827             : /// Calls compute_vars_to_interpolate to compute
     828             : /// InterpolationTargetTag::vars_to_interpolate_to_target from the source
     829             : /// variables.  Does any frame transformations needed.
     830             : template <typename InterpolationTargetTag, typename SourceTags,
     831             :           typename Metavariables, typename ElementId>
     832             : void compute_dest_vars_from_source_vars(
     833             :     const gsl::not_null<Variables<
     834             :         typename InterpolationTargetTag::vars_to_interpolate_to_target>*>
     835             :         dest_vars,
     836             :     const Variables<SourceTags>& source_vars,
     837             :     const Domain<Metavariables::volume_dim>& domain,
     838             :     const Mesh<Metavariables::volume_dim>& mesh, const ElementId& element_id,
     839             :     const Parallel::GlobalCache<Metavariables>& cache,
     840             :     const typename InterpolationTargetTag::temporal_id::type& temporal_id) {
     841             :   const auto& block = domain.blocks().at(element_id.block_id());
     842             :   if (block.is_time_dependent()) {
     843             :     // The functions of time are always guaranteed to be
     844             :     // up-to-date here.
     845             :     // For interpolation without an Interpolator ParallelComponent,
     846             :     // this is because the InterpWithoutInterpComponent event will be called
     847             :     // after the Action that keeps functions of time up to date.
     848             :     // For interpolation with an Interpolator ParallelComponent,
     849             :     // this is because the functions of time are made up to date before
     850             :     // calling SendPointsToInterpolator.
     851             :     if constexpr (any_index_in_frame_v<SourceTags, Frame::Inertial> and
     852             :                   any_index_in_frame_v<typename InterpolationTargetTag::
     853             :                                            vars_to_interpolate_to_target,
     854             :                                        Frame::Grid>) {
     855             :       // Need to do frame transformations to Grid frame.
     856             :       const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);
     857             :       ElementMap<3, ::Frame::Grid> map_logical_to_grid{
     858             :           element_id, block.moving_mesh_logical_to_grid_map().get_clone()};
     859             :       const auto logical_coords = logical_coordinates(mesh);
     860             :       const auto time =
     861             :           InterpolationTarget_detail::get_temporal_id_value(temporal_id);
     862             :       const auto jac_logical_to_grid =
     863             :           map_logical_to_grid.jacobian(logical_coords);
     864             :       const auto invjac_logical_to_grid =
     865             :           map_logical_to_grid.inv_jacobian(logical_coords);
     866             :       const auto [inertial_coords, invjac_grid_to_inertial,
     867             :                   jac_grid_to_inertial, inertial_mesh_velocity] =
     868             :           block.moving_mesh_grid_to_inertial_map()
     869             :               .coords_frame_velocity_jacobians(
     870             :                   map_logical_to_grid(logical_coords), time, functions_of_time);
     871             :       // Create an identity jacobian since the transformation from grid to grid
     872             :       // is the identity
     873             :       InverseJacobian<DataVector, 3, Frame::Grid, Frame::Grid>
     874             :           invjac_grid_to_grid{
     875             :               DataVector{get<0, 0>(jac_logical_to_grid).size(), 0.0}};
     876             :       get<0, 0>(invjac_grid_to_grid) = 1.0;
     877             :       get<1, 1>(invjac_grid_to_grid) = 1.0;
     878             :       get<2, 2>(invjac_grid_to_grid) = 1.0;
     879             :       InterpolationTargetTag::compute_vars_to_interpolate::apply(
     880             :           dest_vars, source_vars, mesh, jac_grid_to_inertial,
     881             :           invjac_grid_to_inertial, jac_logical_to_grid, invjac_logical_to_grid,
     882             :           invjac_grid_to_grid, inertial_mesh_velocity,
     883             :           tnsr::I<DataVector, 3, Frame::Grid>{
     884             :               DataVector{get<0, 0>(jac_logical_to_grid).size(), 0.0}});
     885             :     } else if constexpr (any_index_in_frame_v<SourceTags, Frame::Inertial> and
     886             :                          any_index_in_frame_v<typename InterpolationTargetTag::
     887             :                                                   vars_to_interpolate_to_target,
     888             :                                               Frame::Distorted>) {
     889             :       // Need to do frame transformations to Distorted frame.
     890             :       const auto& functions_of_time = get<domain::Tags::FunctionsOfTime>(cache);
     891             :       ASSERT(block.has_distorted_frame(),
     892             :              "Cannot interpolate to distorted frame in a block that does not "
     893             :              "have a distorted frame");
     894             :       const domain::CoordinateMaps::Composition
     895             :           element_logical_to_distorted_map{
     896             :               domain::element_to_block_logical_map(element_id),
     897             :               block.moving_mesh_logical_to_grid_map().get_clone(),
     898             :               block.moving_mesh_grid_to_distorted_map().get_clone()};
     899             :       const domain::CoordinateMaps::Composition element_logical_to_grid_map{
     900             :           domain::element_to_block_logical_map(element_id),
     901             :           block.moving_mesh_logical_to_grid_map().get_clone()};
     902             :       const auto logical_coords = logical_coordinates(mesh);
     903             :       const auto time =
     904             :           InterpolationTarget_detail::get_temporal_id_value(temporal_id);
     905             :       const auto [inertial_coords, invjac_distorted_to_inertial,
     906             :                   jac_distorted_to_inertial,
     907             :                   distorted_to_inertial_mesh_velocity] =
     908             :           block.moving_mesh_distorted_to_inertial_map()
     909             :               .coords_frame_velocity_jacobians(
     910             :                   element_logical_to_distorted_map(logical_coords, time,
     911             :                                                    functions_of_time),
     912             :                   time, functions_of_time);
     913             :       const auto [distorted_coords, invjac_grid_to_distorted,
     914             :                   jac_grid_to_distorted, grid_to_distorted_mesh_velocity] =
     915             :           block.moving_mesh_grid_to_distorted_map()
     916             :               .coords_frame_velocity_jacobians(
     917             :                   element_logical_to_grid_map(logical_coords, time,
     918             :                                               functions_of_time),
     919             :                   time, functions_of_time);
     920             :       InterpolationTargetTag::compute_vars_to_interpolate::apply(
     921             :           dest_vars, source_vars, mesh, jac_distorted_to_inertial,
     922             :           invjac_distorted_to_inertial,
     923             :           element_logical_to_distorted_map.jacobian(logical_coords, time,
     924             :                                                     functions_of_time),
     925             :           element_logical_to_distorted_map.inv_jacobian(logical_coords, time,
     926             :                                                         functions_of_time),
     927             :           invjac_grid_to_distorted, distorted_to_inertial_mesh_velocity,
     928             :           grid_to_distorted_mesh_velocity);
     929             :     } else {
     930             :       // No frame transformations needed.
     931             :       InterpolationTargetTag::compute_vars_to_interpolate::apply(
     932             :           dest_vars, source_vars, mesh);
     933             :     }
     934             :   } else {
     935             :     // No frame transformations needed, since the maps are time-independent
     936             :     // and therefore all the frames are the same.
     937             :     //
     938             :     // Sometimes dest_vars and source_vars have different Frame tags
     939             :     // in the time-independent case, even though the frames are really
     940             :     // the same.  The source vars should all be in the Inertial frame,
     941             :     // so we create a new non-owning Variables called
     942             :     // dest_vars_in_inertial_frame that points to dest_vars but is
     943             :     // tagged as the inertial frame, and we pass
     944             :     // dest_vars_in_inertial_frame to
     945             :     // compute_vars_to_interpolate::apply.
     946             :     using dest_vars_tags =
     947             :         typename InterpolationTargetTag::vars_to_interpolate_to_target;
     948             :     using dest_tags_in_inertial_frame =
     949             :         TensorMetafunctions::replace_frame_in_taglist<dest_vars_tags,
     950             :                                                       ::Frame::Inertial>;
     951             :     Variables<dest_tags_in_inertial_frame> dest_vars_in_inertial_frame(
     952             :         dest_vars->data(), dest_vars->size());
     953             :     InterpolationTargetTag::compute_vars_to_interpolate::apply(
     954             :         make_not_null(&dest_vars_in_inertial_frame), source_vars, mesh);
     955             :   }
     956             : }
     957             : }  // namespace intrp::InterpolationTarget_detail

Generated by: LCOV version 1.14