SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/ApparentHorizonFinder/Callbacks - FindApparentHorizon.hpp Hit Total Coverage
Commit: d81bf95812223031ba86327aeeb7a9f7bbe4a667 Lines: 1 4 25.0 %
Date: 2025-05-21 02:05:58
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 <cmath>
       7             : #include <deque>
       8             : #include <utility>
       9             : 
      10             : #include "DataStructures/DataBox/DataBox.hpp"
      11             : #include "DataStructures/VariablesTag.hpp"
      12             : #include "Domain/Creators/Tags/Domain.hpp"
      13             : #include "Domain/FunctionsOfTime/Tags.hpp"
      14             : #include "Domain/StrahlkorperTransformations.hpp"
      15             : #include "IO/Logging/Tags.hpp"
      16             : #include "IO/Logging/Verbosity.hpp"
      17             : #include "NumericalAlgorithms/SphericalHarmonics/Spherepack.hpp"
      18             : #include "NumericalAlgorithms/SphericalHarmonics/Strahlkorper.hpp"
      19             : #include "NumericalAlgorithms/SphericalHarmonics/Tags.hpp"
      20             : #include "Parallel/GlobalCache.hpp"
      21             : #include "Parallel/Invoke.hpp"
      22             : #include "Parallel/ParallelComponentHelpers.hpp"
      23             : #include "Parallel/Printf/Printf.hpp"
      24             : #include "ParallelAlgorithms/ApparentHorizonFinder/FastFlow.hpp"
      25             : #include "ParallelAlgorithms/ApparentHorizonFinder/InterpolationTarget.hpp"
      26             : #include "ParallelAlgorithms/ApparentHorizonFinder/Tags.hpp"
      27             : #include "ParallelAlgorithms/Interpolation/Actions/SendPointsToInterpolator.hpp"
      28             : #include "ParallelAlgorithms/Interpolation/InterpolationTargetDetail.hpp"
      29             : #include "ParallelAlgorithms/Interpolation/Protocols/PostInterpolationCallback.hpp"
      30             : #include "PointwiseFunctions/GeneralRelativity/Surfaces/Tags.hpp"
      31             : #include "PointwiseFunctions/GeneralRelativity/Tags.hpp"
      32             : #include "Utilities/ErrorHandling/Error.hpp"
      33             : #include "Utilities/Gsl.hpp"
      34             : #include "Utilities/PrettyType.hpp"
      35             : #include "Utilities/ProtocolHelpers.hpp"
      36             : #include "Utilities/TMPL.hpp"
      37             : 
      38             : /// \cond
      39             : namespace ylm::Tags {
      40             : template <typename Frame>
      41             : struct Strahlkorper;
      42             : }  // namespace ylm::Tags
      43             : namespace db {
      44             : template <typename TagsList>
      45             : class DataBox;
      46             : }  // namespace db
      47             : namespace intrp {
      48             : template <class Metavariables, typename InterpolationTargetTag>
      49             : struct InterpolationTarget;
      50             : namespace Tags {
      51             : template <typename Metavariables>
      52             : struct TemporalIds;
      53             : }  // namespace Tags
      54             : }  // namespace intrp
      55             : /// \endcond
      56             : 
      57             : namespace intrp::callbacks {
      58             : 
      59             : /// \brief post interpolation callback (see
      60             : /// intrp::protocols::PostInterpolationCallback) that does a FastFlow iteration
      61             : /// and triggers another one until convergence.
      62             : ///
      63             : /// Assumes that InterpolationTargetTag contains an additional
      64             : /// type alias called `post_horizon_find_callbacks`, which is a list of
      65             : /// structs, each of which has a function
      66             : ///
      67             : /// \snippet ParallelAlgorithms/ApparentHorizonFinder/Test_ApparentHorizonFinder.cpp post_horizon_find_callback_example
      68             : ///
      69             : /// that is called if the FastFlow iteration has converged.
      70             : /// InterpolationTargetTag also is assumed to contain an additional
      71             : /// type alias called `horizon_find_failure_callbacks`, which is a list of
      72             : /// structs, each of which has a function
      73             : ///
      74             : /// \snippet ParallelAlgorithms/ApparentHorizonFinder/Test_ApparentHorizonFinder.cpp horizon_find_failure_callbacks_example
      75             : ///
      76             : /// that is called if the FastFlow iteration or the interpolation has
      77             : /// failed.
      78             : ///
      79             : /// Uses:
      80             : /// - Metavariables:
      81             : ///   - `temporal_id`
      82             : /// - DataBox:
      83             : ///   - `logging::Tags::Verbosity<InterpolationTargetTag>`
      84             : ///   - `::gr::Tags::InverseSpatialMetric<DataVector, 3, Frame>`
      85             : ///   - `::gr::Tags::ExtrinsicCurvature<DataVector, 3, Frame>`
      86             : ///   - `::gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, Frame>`
      87             : ///   - `::ah::Tags::FastFlow`
      88             : ///   - `ylm::Tags::Strahlkorper<Frame>`
      89             : ///
      90             : /// Modifies:
      91             : /// - DataBox:
      92             : ///   - `::ah::Tags::FastFlow`
      93             : ///   - `ylm::Tags::Strahlkorper<Frame>`
      94             : ///
      95             : /// This can be used in InterpolationTargetTag::post_interpolation_callbacks;
      96             : /// see intrp::protocols::InterpolationTargetTag for details on
      97             : /// InterpolationTargetTag.
      98             : ///
      99             : /// ### Output
     100             : ///
     101             : /// Optionally, a single line of output is printed to stdout either on each
     102             : /// iteration (if verbosity > Verbosity::Quiet) or on convergence
     103             : /// (if verbosity > Verbosity::Silent).  The output consists of the
     104             : /// following labels, and values associated with each label:
     105             : ///  - t        = time given by value of `temporal_id` argument to `apply`
     106             : ///  - its      = current iteration number.
     107             : ///  - R        = min and max of residual over all prolonged grid points.
     108             : ///  - |R|      = L2 norm of residual, counting only L modes solved for.
     109             : ///  - |R_mesh| = L2 norm of residual over prolonged grid points.
     110             : ///  - r        = min and max radius of trial horizon surface.
     111             : ///
     112             : /// #### Difference between |R| and |R_mesh|:
     113             : ///  The horizon is represented in a \f$Y_{lm}\f$ expansion up
     114             : ///  to \f$l=l_{\mathrm{surface}}\f$;
     115             : ///  the residual |R| represents the failure of that surface to satisfy
     116             : ///  the apparent horizon equation.
     117             : ///
     118             : ///  However, at each iteration we also interpolate the horizon surface
     119             : ///  to a higher resolution ("prolongation").  The prolonged surface
     120             : ///  includes \f$Y_{lm}\f$ coefficients up to \f$l=l_{\mathrm{mesh}}\f$,
     121             : ///  where \f$l_{\mathrm{mesh}} > l_{\mathrm{surface}}\f$.
     122             : ///  The residual computed on this higher-resolution surface is |R_mesh|.
     123             : ///
     124             : ///  As iterations proceed, |R| should decrease until it reaches
     125             : ///  numerical roundoff error, because we are varying all the \f$Y_{lm}\f$
     126             : ///  coefficients up to  \f$l=l_{\mathrm{surface}}\f$
     127             : ///  to minimize the residual.  However,
     128             : ///  |R_mesh| should eventually stop decreasing as iterations proceed,
     129             : ///  hitting a floor that represents the numerical truncation error of
     130             : ///  the solution.
     131             : ///
     132             : ///  The convergence criterion looks at both |R| and |R_mesh|:  Once
     133             : ///  |R| is small enough and |R_mesh| has stabilized, it is pointless
     134             : ///  to keep iterating (even though one could iterate until |R| reaches
     135             : ///  roundoff).
     136             : ///
     137             : ///  Problems with convergence of the apparent horizon finder can often
     138             : ///  be diagnosed by looking at the behavior of |R| and |R_mesh| as a
     139             : ///  function of iteration.
     140             : ///
     141             : template <typename InterpolationTargetTag, typename Frame>
     142           1 : struct FindApparentHorizon
     143             :     : tt::ConformsTo<intrp::protocols::PostInterpolationCallback> {
     144           0 :   using const_global_cache_tags =
     145             :       Parallel::get_const_global_cache_tags_from_actions<
     146             :           typename InterpolationTargetTag::post_horizon_find_callbacks>;
     147             :   template <typename DbTags, typename Metavariables, typename TemporalId>
     148           0 :   static bool apply(
     149             :       const gsl::not_null<db::DataBox<DbTags>*> box,
     150             :       const gsl::not_null<Parallel::GlobalCache<Metavariables>*> cache,
     151             :       const TemporalId& temporal_id) {
     152             :     bool horizon_finder_failed = false;
     153             : 
     154             :     const size_t current_iteration =
     155             :         get<::ah::Tags::FastFlow>(*box).current_iteration();
     156             : 
     157             :     if (current_iteration == 0) {
     158             :       // If we get here, we are in a new apparent horizon search, as
     159             :       // opposed to a subsequent iteration of the same horizon search.
     160             :       //
     161             :       // So put new initial guess into ylm::Tags::Strahlkorper<Frame>.
     162             :       // We need to do this now, and not at the end of the previous horizon
     163             :       // search, because only now do we know the temporal_id of this horizon
     164             :       // search.
     165             :       db::mutate<ylm::Tags::Strahlkorper<Frame>,
     166             :                  ylm::Tags::PreviousStrahlkorpers<Frame>,
     167             :                  ah::Tags::PreviousIterationStrahlkorper<Frame>,
     168             :                  ah::Tags::FailedInterpolationIterations>(
     169             :           [&temporal_id](
     170             :               const gsl::not_null<ylm::Strahlkorper<Frame>*> strahlkorper,
     171             :               const gsl::not_null<
     172             :                   std::deque<std::pair<double, ylm::Strahlkorper<Frame>>>*>
     173             :                   previous_strahlkorpers,
     174             :               const gsl::not_null<ylm::Strahlkorper<Frame>*>
     175             :                   previous_fast_flow_strahlkorper,
     176             :               const gsl::not_null<size_t*> failed_interpolation_iterations) {
     177             :             // Reset to zero. Will only be used starting from the second
     178             :             // iteration
     179             :             *failed_interpolation_iterations = 0;
     180             : 
     181             :             // If we have zero previous_strahlkorpers, then the
     182             :             // initial guess is already in strahlkorper, so do
     183             :             // nothing.
     184             :             //
     185             :             // If we have one previous_strahlkorper, then we have had
     186             :             // a successful horizon find, and the initial guess for the
     187             :             // next horizon find is already in strahlkorper, so
     188             :             // again we do nothing.
     189             :             //
     190             :             // If we have 2 previous_strahlkorpers and the time of the second
     191             :             // one is a NaN, this means that the corresponding
     192             :             // previous_strahlkorper is the original initial guess, so
     193             :             // again we do nothing.
     194             :             //
     195             :             // If we have 2 valid previous_strahlkorpers, then
     196             :             // we set the initial guess by linear extrapolation in time
     197             :             // using the last 2 previous_strahlkorpers.
     198             :             //
     199             :             // If we have 3 valid previous_strahlkorpers, then
     200             :             // we set the initial guess by quadratic extrapolation in time
     201             :             // using the last 3 previous_strahlkorpers.
     202             :             //
     203             :             // For extrapolation, we assume that
     204             :             // * Expansion center of all the Strahlkorpers are equal.
     205             :             // * Maximum L of all the Strahlkorpers are equal.
     206             :             // It is easy to relax the max L assumption once we start
     207             :             // adaptively changing the L of the strahlkorpers.
     208             :             if (previous_strahlkorpers->size() > 1 and
     209             :                 not std::isnan((*previous_strahlkorpers)[1].first)) {
     210             :               if (previous_strahlkorpers->size() > 2 and
     211             :                   not std::isnan((*previous_strahlkorpers)[2].first)) {
     212             :                 // Quadratic extrapolation
     213             :                 const double new_time =
     214             :                     InterpolationTarget_detail::get_temporal_id_value(
     215             :                         temporal_id);
     216             :                 const double dt_0 =
     217             :                     (*previous_strahlkorpers)[0].first - new_time;
     218             :                 const double dt_1 =
     219             :                     (*previous_strahlkorpers)[1].first - new_time;
     220             :                 const double dt_2 =
     221             :                     (*previous_strahlkorpers)[2].first - new_time;
     222             :                 const double fac_0 =
     223             :                     dt_1 * dt_2 / ((dt_1 - dt_0) * (dt_2 - dt_0));
     224             :                 const double fac_1 =
     225             :                     dt_0 * dt_2 / ((dt_2 - dt_1) * (dt_0 - dt_1));
     226             :                 const double fac_2 = 1.0 - fac_0 - fac_1;
     227             :                 strahlkorper->coefficients() =
     228             :                     fac_0 * (*previous_strahlkorpers)[0].second.coefficients() +
     229             :                     fac_1 * (*previous_strahlkorpers)[1].second.coefficients() +
     230             :                     fac_2 * (*previous_strahlkorpers)[2].second.coefficients();
     231             :               } else {
     232             :                 // Linear extrapolation
     233             :                 const double new_time =
     234             :                     InterpolationTarget_detail::get_temporal_id_value(
     235             :                         temporal_id);
     236             :                 const double dt_0 =
     237             :                     (*previous_strahlkorpers)[0].first - new_time;
     238             :                 const double dt_1 =
     239             :                     (*previous_strahlkorpers)[1].first - new_time;
     240             :                 const double fac_0 = dt_1 / (dt_1 - dt_0);
     241             :                 const double fac_1 = 1.0 - fac_0;
     242             :                 strahlkorper->coefficients() =
     243             :                     fac_0 * (*previous_strahlkorpers)[0].second.coefficients() +
     244             :                     fac_1 * (*previous_strahlkorpers)[1].second.coefficients();
     245             :               }
     246             :             }
     247             : 
     248             :             // First iteration the previous is just the initial guess
     249             :             (*previous_fast_flow_strahlkorper) = *strahlkorper;
     250             :           },
     251             :           box);
     252             :     }
     253             : 
     254             :     // Deal with the possibility that some of the points might be
     255             :     // outside of the Domain.
     256             :     const auto& indices_of_invalid_pts =
     257             :         db::get<Tags::IndicesOfInvalidInterpPoints<TemporalId>>(*box);
     258             :     size_t& failed_interpolation_iterations =
     259             :         db::get_mutable_reference<ah::Tags::FailedInterpolationIterations>(box);
     260             :     if (indices_of_invalid_pts.count(temporal_id) > 0 and
     261             :         not indices_of_invalid_pts.at(temporal_id).empty()) {
     262             :       ++failed_interpolation_iterations;
     263             : 
     264             :       const auto& options = Parallel::get<
     265             :           intrp::Tags::ApparentHorizon<InterpolationTargetTag, Frame>>(*cache);
     266             : 
     267             :       // Can't recover from the first iteration or if we've exceeded our number
     268             :       // of attempts
     269             :       if (current_iteration > 0 and failed_interpolation_iterations <=
     270             :                                         options.max_interpolation_retries) {
     271             :         // Move the new trial surface halfway between the current surface and
     272             :         // the previous surface
     273             :         db::mutate<ylm::Tags::Strahlkorper<Frame>>(
     274             :             [](const gsl::not_null<ylm::Strahlkorper<Frame>*> strahlkorper,
     275             :                const ylm::Strahlkorper<Frame>&
     276             :                    previous_strahlkorper_iteration) {
     277             :               strahlkorper->coefficients() +=
     278             :                   0.5 * (previous_strahlkorper_iteration.coefficients() -
     279             :                          strahlkorper->coefficients());
     280             :             },
     281             :             box, db::get<ah::Tags::PreviousIterationStrahlkorper<Frame>>(*box));
     282             : 
     283             :         // Resend the new coords at the same time and iteration. Use 0 for the
     284             :         // array index because it's always 0 due to the targets being
     285             :         // singletons. No need to worry about the invalid points since those get
     286             :         // reset within SendPointsToInterpolator
     287             :         Actions::SendPointsToInterpolator<InterpolationTargetTag>::
     288             :             template apply<::intrp::InterpolationTarget<
     289             :                 Metavariables, InterpolationTargetTag>>(
     290             :                 *box, *cache, 0, temporal_id, current_iteration,
     291             :                 failed_interpolation_iterations);
     292             : 
     293             :         // We return false because we don't want this iteration to clean
     294             :         // up the volume data, since we still need it.
     295             :         return false;
     296             :       } else {
     297             :         tmpl::for_each<
     298             :             typename InterpolationTargetTag::horizon_find_failure_callbacks>(
     299             :             [&box, &cache, &temporal_id](auto callback_v) {
     300             :               using callback = tmpl::type_from<decltype(callback_v)>;
     301             :               callback::template apply<InterpolationTargetTag>(
     302             :                   *box, *cache, temporal_id,
     303             :                   FastFlow::Status::InterpolationFailure);
     304             :             });
     305             :         horizon_finder_failed = true;
     306             :       }
     307             :     }
     308             : 
     309             :     // Reset now that we were able to interpolate (or if we are abandoning this
     310             :     // horizon find)
     311             :     failed_interpolation_iterations = 0;
     312             : 
     313             :     if (not horizon_finder_failed) {
     314             :       const auto& verbosity =
     315             :           db::get<logging::Tags::Verbosity<InterpolationTargetTag>>(*box);
     316             :       const auto& inv_g =
     317             :           db::get<::gr::Tags::InverseSpatialMetric<DataVector, 3, Frame>>(*box);
     318             :       const auto& ex_curv =
     319             :           db::get<::gr::Tags::ExtrinsicCurvature<DataVector, 3, Frame>>(*box);
     320             :       const auto& christoffel = db::get<
     321             :           ::gr::Tags::SpatialChristoffelSecondKind<DataVector, 3, Frame>>(*box);
     322             : 
     323             :       std::pair<FastFlow::Status, FastFlow::IterInfo> status_and_info;
     324             : 
     325             :       // Do a FastFlow iteration.
     326             :       db::mutate<::ah::Tags::FastFlow, ylm::Tags::Strahlkorper<Frame>,
     327             :                  ah::Tags::PreviousIterationStrahlkorper<Frame>>(
     328             :           [&inv_g, &ex_curv, &christoffel, &status_and_info](
     329             :               const gsl::not_null<::FastFlow*> fast_flow,
     330             :               const gsl::not_null<ylm::Strahlkorper<Frame>*> strahlkorper,
     331             :               const gsl::not_null<ylm::Strahlkorper<Frame>*>
     332             :                   previous_fast_flow_strahlkorper) {
     333             :             // Set this before we iterate the fast flow. Note that this will
     334             :             // have to be updated once we have adaptive horizon finding
     335             :             (*previous_fast_flow_strahlkorper) = *strahlkorper;
     336             : 
     337             :             status_and_info = fast_flow->template iterate_horizon_finder<Frame>(
     338             :                 strahlkorper, inv_g, ex_curv, christoffel);
     339             :           },
     340             :           box);
     341             : 
     342             :       // Determine whether we have converged, whether we need another step,
     343             :       // or whether we have encountered an error.
     344             : 
     345             :       const auto& status = status_and_info.first;
     346             :       const auto& info = status_and_info.second;
     347             :       const auto has_converged = converged(status);
     348             : 
     349             :       if (verbosity >= ::Verbosity::Verbose or
     350             :           (verbosity >= ::Verbosity::Quiet and has_converged)) {
     351             :         Parallel::printf(
     352             :             "%s: t=%.6g: its=%d: %.1e<R<%.0e, |R|=%.1g, "
     353             :             "|R_grid|=%.1g, %.4g<r<%.4g\n",
     354             :             pretty_type::name<InterpolationTargetTag>(),
     355             :             InterpolationTarget_detail::get_temporal_id_value(temporal_id),
     356             :             info.iteration, info.min_residual, info.max_residual,
     357             :             info.residual_ylm, info.residual_mesh, info.r_min, info.r_max);
     358             :       }
     359             : 
     360             :       if (status == FastFlow::Status::SuccessfulIteration) {
     361             :         // Do another iteration of the same horizon search.
     362             :         const auto& current_id =
     363             :             db::get<intrp::Tags::CurrentTemporalId<TemporalId>>(*box);
     364             :         using ::operator<<;
     365             :         ASSERT(current_id.has_value(),
     366             :                "While horizon finding, the current temporal id doesn't have a "
     367             :                "value, but it should.");
     368             :         // The iteration of these new coords is the fast flow iteration + 1
     369             :         // because the zeroth iteration was the initial guess. Use 0 for the
     370             :         // array index because it's always 0 due to the targets being singletons
     371             :         Actions::SendPointsToInterpolator<InterpolationTargetTag>::
     372             :             template apply<::intrp::InterpolationTarget<
     373             :                 Metavariables, InterpolationTargetTag>>(
     374             :                 *box, *cache, 0, current_id.value(), info.iteration + 1);
     375             :         // We return false because we don't want this iteration to clean
     376             :         // up the volume data, since we are using it for the next iteration
     377             :         // (i.e. the simple_action that we just called).
     378             :         return false;
     379             :       } else if (not has_converged) {
     380             :         tmpl::for_each<
     381             :             typename InterpolationTargetTag::horizon_find_failure_callbacks>(
     382             :             [&box, &cache, &temporal_id, &status](auto callback_v) {
     383             :               using callback = tmpl::type_from<decltype(callback_v)>;
     384             :               callback::template apply<InterpolationTargetTag>(
     385             :                   *box, *cache, temporal_id, status);
     386             :             });
     387             :         horizon_finder_failed = true;
     388             :       }
     389             :     }
     390             : 
     391             :     // If it failed, don't update any variables, just reset the Strahlkorper to
     392             :     // it's previous value
     393             :     if (horizon_finder_failed) {
     394             :       db::mutate<ylm::Tags::Strahlkorper<Frame>>(
     395             :           [](const gsl::not_null<ylm::Strahlkorper<Frame>*> strahlkorper,
     396             :              const std::deque<std::pair<double, ylm::Strahlkorper<Frame>>>&
     397             :                  previous_strahlkorpers) {
     398             :             // Don't keep a partially-converged strahlkorper in the
     399             :             // DataBox.  Reset to either the original initial guess or
     400             :             // to the last-found Strahlkorper (whichever one happens
     401             :             // to be in previous_strahlkorpers).
     402             :             *strahlkorper = previous_strahlkorpers.front().second;
     403             :           },
     404             :           box, db::get<ylm::Tags::PreviousStrahlkorpers<Frame>>(*box));
     405             :     } else {
     406             :       // The interpolated variables
     407             :       // Tags::Variables<InterpolationTargetTag::vars_to_interpolate_to_target>
     408             :       // have been interpolated from the volume to the points on the
     409             :       // prolonged_strahlkorper, not to the points on the actual
     410             :       // strahlkorper.  So here we do a restriction of these
     411             :       // quantities onto the actual strahlkorper.
     412             : 
     413             :       // Type alias to make code more understandable.
     414             :       using vars_tags =
     415             :           typename InterpolationTargetTag::vars_to_interpolate_to_target;
     416             :       db::mutate_apply<
     417             :           tmpl::list<::Tags::Variables<vars_tags>>,
     418             :           tmpl::list<ylm::Tags::Strahlkorper<Frame>, ::ah::Tags::FastFlow>>(
     419             :           [](const gsl::not_null<Variables<vars_tags>*> vars,
     420             :              const ylm::Strahlkorper<Frame>& strahlkorper,
     421             :              const FastFlow& fast_flow) {
     422             :             const size_t L_mesh = fast_flow.current_l_mesh(strahlkorper);
     423             :             const auto prolonged_strahlkorper =
     424             :                 ylm::Strahlkorper<Frame>(L_mesh, L_mesh, strahlkorper);
     425             :             auto new_vars = ::Variables<vars_tags>(
     426             :                 strahlkorper.ylm_spherepack().physical_size());
     427             : 
     428             :             tmpl::for_each<vars_tags>([&strahlkorper, &prolonged_strahlkorper,
     429             :                                        &vars, &new_vars](auto tag_v) {
     430             :               using tag = typename decltype(tag_v)::type;
     431             :               const auto& old_var = get<tag>(*vars);
     432             :               auto& new_var = get<tag>(new_vars);
     433             :               auto old_iter = old_var.begin();
     434             :               auto new_iter = new_var.begin();
     435             :               for (; old_iter != old_var.end() and new_iter != new_var.end();
     436             :                    ++old_iter, ++new_iter) {
     437             :                 *new_iter = strahlkorper.ylm_spherepack().spec_to_phys(
     438             :                     prolonged_strahlkorper.ylm_spherepack().prolong_or_restrict(
     439             :                         prolonged_strahlkorper.ylm_spherepack().phys_to_spec(
     440             :                             *old_iter),
     441             :                         strahlkorper.ylm_spherepack()));
     442             :               }
     443             :             });
     444             :             *vars = std::move(new_vars);
     445             :           },
     446             :           box);
     447             : 
     448             :       // Compute Strahlkorper Cartesian coordinates in Inertial frame
     449             :       // if the current frame is not inertial.
     450             :       if constexpr (not std::is_same_v<Frame, ::Frame::Inertial>) {
     451             :         db::mutate_apply<
     452             :             tmpl::list<ylm::Tags::CartesianCoords<::Frame::Inertial>>,
     453             :             tmpl::list<ylm::Tags::Strahlkorper<Frame>,
     454             :                        domain::Tags::Domain<Metavariables::volume_dim>>>(
     455             :             [&cache, &temporal_id](
     456             :                 const gsl::not_null<tnsr::I<DataVector, 3, ::Frame::Inertial>*>
     457             :                     inertial_strahlkorper_coords,
     458             :                 const ylm::Strahlkorper<Frame>& strahlkorper,
     459             :                 const Domain<Metavariables::volume_dim>& domain) {
     460             :               // Note that functions_of_time must already be up to
     461             :               // date at temporal_id because they were used in the AH
     462             :               // search above.
     463             :               const auto& functions_of_time =
     464             :                   get<domain::Tags::FunctionsOfTime>(*cache);
     465             :               strahlkorper_coords_in_different_frame(
     466             :                   inertial_strahlkorper_coords, strahlkorper, domain,
     467             :                   functions_of_time,
     468             :                   InterpolationTarget_detail::get_temporal_id_value(
     469             :                       temporal_id));
     470             :             },
     471             :             box);
     472             :       }
     473             : 
     474             :       // Update the previous strahlkorpers. We do this before the callbacks
     475             :       // in case any of the callbacks need the previous strahlkorpers with the
     476             :       // current strahlkorper already in it.
     477             :       db::mutate<ylm::Tags::Strahlkorper<Frame>,
     478             :                  ylm::Tags::PreviousStrahlkorpers<Frame>>(
     479             :           [&temporal_id](
     480             :               const gsl::not_null<ylm::Strahlkorper<Frame>*> strahlkorper,
     481             :               const gsl::not_null<
     482             :                   std::deque<std::pair<double, ylm::Strahlkorper<Frame>>>*>
     483             :                   previous_strahlkorpers) {
     484             :             // This is the number of previous strahlkorpers that we
     485             :             // keep around.
     486             :             const size_t num_previous_strahlkorpers = 3;
     487             : 
     488             :             // Save a new previous_strahlkorper.
     489             :             previous_strahlkorpers->emplace_front(
     490             :                 InterpolationTarget_detail::get_temporal_id_value(temporal_id),
     491             :                 *strahlkorper);
     492             : 
     493             :             // Remove old previous_strahlkorpers that are no longer relevant.
     494             :             while (previous_strahlkorpers->size() >
     495             :                    num_previous_strahlkorpers) {
     496             :               previous_strahlkorpers->pop_back();
     497             :             }
     498             :           },
     499             :           box);
     500             : 
     501             :       // Finally call callbacks
     502             :       tmpl::for_each<
     503             :           typename InterpolationTargetTag::post_horizon_find_callbacks>(
     504             :           [&box, &cache, &temporal_id](auto callback_v) {
     505             :             using callback = tmpl::type_from<decltype(callback_v)>;
     506             :             callback::apply(*box, *cache, temporal_id);
     507             :           });
     508             :     }
     509             : 
     510             :     // Prepare for finding horizon at a new time. Regardless of if we failed or
     511             :     // not, we reset fast flow.
     512             :     db::mutate<::ah::Tags::FastFlow>(
     513             :         [](const gsl::not_null<::FastFlow*> fast_flow) {
     514             :           fast_flow->reset_for_next_find();
     515             :         },
     516             :         box);
     517             : 
     518             :     // We return true because we are now done with all the volume data
     519             :     // at this temporal_id, so we want it cleaned up.
     520             :     return true;
     521             :   }
     522             : };
     523             : }  // namespace intrp::callbacks

Generated by: LCOV version 1.14