SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/Interpolation - InterpolationTargetDetail.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 0.0 % 1 0
Test Date: 2026-10-22 17:29:42
Legend: Lines:     hit not hit

            Line data    Source code
       1            0 : // Distributed under the MIT License.
       2              : // See LICENSE.txt for details.
       3              : 
       4              : #pragma once
       5              : 
       6              : #include <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 2.0-1