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

Generated by: LCOV version 1.14