SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/NonlinearSolver/NewtonRaphson - ElementActions.hpp Hit Total Coverage
Commit: 37c384043430860f87787999aa7399d01bb3d213 Lines: 1 2 50.0 %
Date: 2024-04-20 02:24:02
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 <cstddef>
       8             : #include <limits>
       9             : #include <optional>
      10             : #include <tuple>
      11             : #include <utility>
      12             : #include <variant>
      13             : 
      14             : #include "DataStructures/DataBox/DataBox.hpp"
      15             : #include "DataStructures/DataBox/PrefixHelpers.hpp"
      16             : #include "IO/Logging/Tags.hpp"
      17             : #include "IO/Logging/Verbosity.hpp"
      18             : #include "NumericalAlgorithms/Convergence/HasConverged.hpp"
      19             : #include "NumericalAlgorithms/Convergence/Tags.hpp"
      20             : #include "NumericalAlgorithms/LinearSolver/InnerProduct.hpp"
      21             : #include "Parallel/AlgorithmExecution.hpp"
      22             : #include "Parallel/GetSection.hpp"
      23             : #include "Parallel/GlobalCache.hpp"
      24             : #include "Parallel/Invoke.hpp"
      25             : #include "Parallel/Printf/Printf.hpp"
      26             : #include "Parallel/Reduction.hpp"
      27             : #include "Parallel/Tags/Section.hpp"
      28             : #include "ParallelAlgorithms/Amr/Protocols/Projector.hpp"
      29             : #include "ParallelAlgorithms/Initialization/MutateAssign.hpp"
      30             : #include "ParallelAlgorithms/LinearSolver/Tags.hpp"
      31             : #include "ParallelAlgorithms/NonlinearSolver/NewtonRaphson/ResidualMonitorActions.hpp"
      32             : #include "ParallelAlgorithms/NonlinearSolver/NewtonRaphson/Tags/InboxTags.hpp"
      33             : #include "ParallelAlgorithms/NonlinearSolver/Tags.hpp"
      34             : #include "Utilities/ErrorHandling/Assert.hpp"
      35             : #include "Utilities/Functional.hpp"
      36             : #include "Utilities/GetOutput.hpp"
      37             : #include "Utilities/Gsl.hpp"
      38             : #include "Utilities/MakeWithValue.hpp"
      39             : #include "Utilities/PrettyType.hpp"
      40             : #include "Utilities/ProtocolHelpers.hpp"
      41             : #include "Utilities/TMPL.hpp"
      42             : #include "Utilities/TaggedTuple.hpp"
      43             : 
      44             : /// \cond
      45             : namespace NonlinearSolver::newton_raphson::detail {
      46             : template <typename Metavariables, typename FieldsTag, typename OptionsGroup>
      47             : struct ResidualMonitor;
      48             : template <typename FieldsTag, typename OptionsGroup, typename Label,
      49             :           typename ArraySectionIdTag>
      50             : struct ReceiveInitialHasConverged;
      51             : template <typename FieldsTag, typename OptionsGroup, typename Label,
      52             :           typename ArraySectionIdTag>
      53             : struct PrepareStep;
      54             : template <typename FieldsTag, typename OptionsGroup, typename Label,
      55             :           typename ArraySectionIdTag>
      56             : struct Globalize;
      57             : template <typename FieldsTag, typename OptionsGroup, typename Label,
      58             :           typename ArraySectionIdTag>
      59             : struct CompleteStep;
      60             : }  // namespace NonlinearSolver::newton_raphson::detail
      61             : /// \endcond
      62             : 
      63           1 : namespace NonlinearSolver::newton_raphson::detail {
      64             : 
      65             : using ResidualReductionData = Parallel::ReductionData<
      66             :     // Iteration ID
      67             :     Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,
      68             :     // Globalization iteration ID
      69             :     Parallel::ReductionDatum<size_t, funcl::AssertEqual<>>,
      70             :     // Residual magnitude square
      71             :     Parallel::ReductionDatum<double, funcl::Plus<>>,
      72             :     // Step length
      73             :     Parallel::ReductionDatum<double, funcl::AssertEqual<>>>;
      74             : 
      75             : template <typename FieldsTag, typename OptionsGroup, typename SourceTag>
      76             : struct InitializeElement : tt::ConformsTo<amr::protocols::Projector> {
      77             :  private:
      78             :   using fields_tag = FieldsTag;
      79             :   using source_tag = SourceTag;
      80             :   using nonlinear_operator_applied_to_fields_tag =
      81             :       db::add_tag_prefix<NonlinearSolver::Tags::OperatorAppliedTo, fields_tag>;
      82             :   using correction_tag =
      83             :       db::add_tag_prefix<NonlinearSolver::Tags::Correction, fields_tag>;
      84             :   using globalization_fields_tag =
      85             :       db::add_tag_prefix<NonlinearSolver::Tags::Globalization, fields_tag>;
      86             : 
      87             :  public:  // Iterable action
      88             :   using simple_tags =
      89             :       tmpl::list<Convergence::Tags::IterationId<OptionsGroup>,
      90             :                  Convergence::Tags::HasConverged<OptionsGroup>,
      91             :                  nonlinear_operator_applied_to_fields_tag, correction_tag,
      92             :                  NonlinearSolver::Tags::Globalization<
      93             :                      Convergence::Tags::IterationId<OptionsGroup>>,
      94             :                  NonlinearSolver::Tags::StepLength<OptionsGroup>,
      95             :                  globalization_fields_tag>;
      96             :   using compute_tags = tmpl::list<
      97             :       NonlinearSolver::Tags::ResidualCompute<fields_tag, source_tag>>;
      98             : 
      99             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     100             :             typename ArrayIndex, typename ActionList,
     101             :             typename ParallelComponent>
     102             :   static Parallel::iterable_action_return_t apply(
     103             :       db::DataBox<DbTagsList>& box,
     104             :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     105             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     106             :       const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
     107             :       const ParallelComponent* const /*meta*/) {
     108             :     ::Initialization::mutate_assign<
     109             :         tmpl::list<Convergence::Tags::IterationId<OptionsGroup>,
     110             :                    NonlinearSolver::Tags::Globalization<
     111             :                        Convergence::Tags::IterationId<OptionsGroup>>,
     112             :                    NonlinearSolver::Tags::StepLength<OptionsGroup>>>(
     113             :         make_not_null(&box), std::numeric_limits<size_t>::max(),
     114             :         std::numeric_limits<size_t>::max(),
     115             :         std::numeric_limits<double>::signaling_NaN());
     116             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     117             :   }
     118             : 
     119             :  public:  // amr::protocols::Projector
     120             :   using argument_tags = tmpl::list<>;
     121             :   using return_tags = simple_tags;
     122             : 
     123             :   template <typename... AmrData>
     124             :   static void apply(const gsl::not_null<size_t*> /*unused*/,
     125             :                     const AmrData&... /*all_items*/) {
     126             :     // No need to reset or initialize any of the items during AMR because they
     127             :     // will be set in `PrepareSolve` below. AMR can't happen _during_ a solve.
     128             :   }
     129             : };
     130             : 
     131             : // Reset to the initial state of the algorithm.
     132             : template <typename FieldsTag, typename OptionsGroup, typename Label,
     133             :           typename ArraySectionIdTag>
     134             : struct PrepareSolve {
     135             :  private:
     136             :   using fields_tag = FieldsTag;
     137             :   using nonlinear_residual_tag =
     138             :       db::add_tag_prefix<NonlinearSolver::Tags::Residual, fields_tag>;
     139             : 
     140             :  public:
     141             :   using const_global_cache_tags =
     142             :       tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;
     143             : 
     144             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     145             :             typename ArrayIndex, typename ActionList,
     146             :             typename ParallelComponent>
     147             :   static Parallel::iterable_action_return_t apply(
     148             :       db::DataBox<DbTagsList>& box,
     149             :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     150             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     151             :       const ArrayIndex& array_index, const ActionList /*meta*/,
     152             :       const ParallelComponent* const /*meta*/) {
     153             :     db::mutate<Convergence::Tags::IterationId<OptionsGroup>>(
     154             :         [](const gsl::not_null<size_t*> iteration_id) { *iteration_id = 0; },
     155             :         make_not_null(&box));
     156             : 
     157             :     // Skip the initial reduction on elements that are not part of the section
     158             :     if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {
     159             :       if (not db::get<Parallel::Tags::Section<ParallelComponent,
     160             :                                               ArraySectionIdTag>>(box)
     161             :                   .has_value()) {
     162             :         constexpr size_t receive_initial_index = tmpl::index_of<
     163             :             ActionList,
     164             :             ReceiveInitialHasConverged<FieldsTag, OptionsGroup, Label,
     165             :                                        ArraySectionIdTag>>::value;
     166             :         return {Parallel::AlgorithmExecution::Continue, receive_initial_index};
     167             :       }
     168             :     }
     169             : 
     170             :     if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     171             :                  ::Verbosity::Debug)) {
     172             :       Parallel::printf("%s %s: Prepare solve\n", get_output(array_index),
     173             :                        pretty_type::name<OptionsGroup>());
     174             :     }
     175             : 
     176             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     177             :   }
     178             : };
     179             : 
     180             : // To determine the initial residual magnitude and to check if the algorithm has
     181             : // already converged, we perform a global reduction to the `ResidualMonitor`.
     182             : template <typename FieldsTag, typename OptionsGroup, typename Label,
     183             :           typename ArraySectionIdTag>
     184             : struct SendInitialResidualMagnitude {
     185             :  private:
     186             :   using fields_tag = FieldsTag;
     187             :   using nonlinear_residual_tag =
     188             :       db::add_tag_prefix<NonlinearSolver::Tags::Residual, fields_tag>;
     189             : 
     190             :  public:
     191             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     192             :             typename ArrayIndex, typename ActionList,
     193             :             typename ParallelComponent>
     194             :   static Parallel::iterable_action_return_t apply(
     195             :       db::DataBox<DbTagsList>& box,
     196             :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     197             :       Parallel::GlobalCache<Metavariables>& cache,
     198             :       const ArrayIndex& array_index, const ActionList /*meta*/,
     199             :       const ParallelComponent* const /*meta*/) {
     200             :     // Perform a global reduction to compute the initial residual magnitude
     201             :     const auto& residual = db::get<nonlinear_residual_tag>(box);
     202             :     const double local_residual_magnitude_square =
     203             :         LinearSolver::inner_product(residual, residual);
     204             :     ResidualReductionData reduction_data{0, 0, local_residual_magnitude_square,
     205             :                                          1.};
     206             :     auto& section = Parallel::get_section<ParallelComponent, ArraySectionIdTag>(
     207             :         make_not_null(&box));
     208             :     Parallel::contribute_to_reduction<
     209             :         CheckResidualMagnitude<FieldsTag, OptionsGroup, ParallelComponent>>(
     210             :         std::move(reduction_data),
     211             :         Parallel::get_parallel_component<ParallelComponent>(cache)[array_index],
     212             :         Parallel::get_parallel_component<
     213             :             ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>(cache),
     214             :         make_not_null(&section));
     215             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     216             :   }
     217             : };
     218             : 
     219             : // Wait for the broadcast from the `ResidualMonitor` to complete the preparation
     220             : // for the solve. We skip the solve altogether if the algorithm has already
     221             : // converged.
     222             : template <typename FieldsTag, typename OptionsGroup, typename Label,
     223             :           typename ArraySectionIdTag>
     224             : struct ReceiveInitialHasConverged {
     225             :   using inbox_tags = tmpl::list<Tags::GlobalizationResult<OptionsGroup>>;
     226             : 
     227             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     228             :             typename ArrayIndex, typename ActionList,
     229             :             typename ParallelComponent>
     230             :   static Parallel::iterable_action_return_t apply(
     231             :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     232             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     233             :       const ArrayIndex& /*array_index*/, const ActionList /*meta*/,
     234             :       const ParallelComponent* const /*meta*/) {
     235             :     const size_t iteration_id =
     236             :         db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);
     237             :     auto& inbox = get<Tags::GlobalizationResult<OptionsGroup>>(inboxes);
     238             :     if (inbox.find(db::get<Convergence::Tags::IterationId<OptionsGroup>>(
     239             :             box)) == inbox.end()) {
     240             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     241             :     }
     242             : 
     243             :     // Retrieve reduction data from inbox
     244             :     auto globalization_result = std::move(inbox.extract(iteration_id).mapped());
     245             :     ASSERT(
     246             :         std::holds_alternative<Convergence::HasConverged>(globalization_result),
     247             :         "No globalization should occur for the initial residual. This is a "
     248             :         "bug, so please file an issue.");
     249             :     auto& has_converged = get<Convergence::HasConverged>(globalization_result);
     250             :     db::mutate<Convergence::Tags::HasConverged<OptionsGroup>>(
     251             :         [&has_converged](const gsl::not_null<Convergence::HasConverged*>
     252             :                              local_has_converged) {
     253             :           *local_has_converged = std::move(has_converged);
     254             :         },
     255             :         make_not_null(&box));
     256             : 
     257             :     // Skip steps entirely if the solve has already converged
     258             :     constexpr size_t complete_step_index =
     259             :         tmpl::index_of<ActionList, CompleteStep<FieldsTag, OptionsGroup, Label,
     260             :                                                 ArraySectionIdTag>>::value;
     261             :     if (get<Convergence::Tags::HasConverged<OptionsGroup>>(box)) {
     262             :       return {Parallel::AlgorithmExecution::Continue, complete_step_index + 1};
     263             :     }
     264             : 
     265             :     // Skip the solve entirely on elements that are not part of the section. To
     266             :     // do so, we skip ahead to the `SolveLinearization` action list between
     267             :     // `PrepareStep` and `PerformStep`. Those actions need a chance to run even
     268             :     // on elements that are not part of the section, because they may take part
     269             :     // in preconditioning (see Multigrid preconditioner).
     270             :     if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {
     271             :       if (not db::get<Parallel::Tags::Section<ParallelComponent,
     272             :                                               ArraySectionIdTag>>(box)
     273             :                   .has_value()) {
     274             :         db::mutate<Convergence::Tags::IterationId<OptionsGroup>>(
     275             :             [](const gsl::not_null<size_t*> local_iteration_id) {
     276             :               *local_iteration_id = 1;
     277             :             },
     278             :             make_not_null(&box));
     279             :         constexpr size_t prepare_step_index =
     280             :             tmpl::index_of<ActionList,
     281             :                            PrepareStep<FieldsTag, OptionsGroup, Label,
     282             :                                        ArraySectionIdTag>>::value;
     283             :         return {Parallel::AlgorithmExecution::Continue, prepare_step_index + 1};
     284             :       }
     285             :     }
     286             : 
     287             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     288             :   }
     289             : };
     290             : 
     291             : // Prepare the next Newton-Raphson step. In particular, we prepare the DataBox
     292             : // for the linear solver which will run after this action to solve the
     293             : // linearized problem for the `correction_tag`. The source for the linear
     294             : // solver is the residual, which is in the DataBox as a compute tag so needs no
     295             : // preparation. We only need to set the initial guess.
     296             : //
     297             : // We also prepare the line-search globalization here. Since we don't know if
     298             : // a step will be sufficient before taking it, we have to store the original
     299             : // field values.
     300             : //
     301             : // The algorithm jumps back to this action from `CompleteStep` to continue
     302             : // iterating the nonlinear solve.
     303             : template <typename FieldsTag, typename OptionsGroup, typename Label,
     304             :           typename ArraySectionIdTag>
     305             : struct PrepareStep {
     306             :  private:
     307             :   using fields_tag = FieldsTag;
     308             :   using correction_tag =
     309             :       db::add_tag_prefix<NonlinearSolver::Tags::Correction, fields_tag>;
     310             :   using linear_operator_applied_to_correction_tag =
     311             :       db::add_tag_prefix<LinearSolver::Tags::OperatorAppliedTo, correction_tag>;
     312             :   using globalization_fields_tag =
     313             :       db::add_tag_prefix<NonlinearSolver::Tags::Globalization, fields_tag>;
     314             : 
     315             :  public:
     316             :   using const_global_cache_tags =
     317             :       tmpl::list<NonlinearSolver::Tags::DampingFactor<OptionsGroup>,
     318             :                  logging::Tags::Verbosity<OptionsGroup>>;
     319             : 
     320             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     321             :             typename ArrayIndex, typename ActionList,
     322             :             typename ParallelComponent>
     323             :   static Parallel::iterable_action_return_t apply(
     324             :       db::DataBox<DbTagsList>& box,
     325             :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     326             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     327             :       const ArrayIndex& array_index, const ActionList /*meta*/,
     328             :       const ParallelComponent* const /*meta*/) {
     329             :     if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     330             :                  ::Verbosity::Debug)) {
     331             :       Parallel::printf(
     332             :           "%s %s(%zu): Prepare step\n", get_output(array_index),
     333             :           pretty_type::name<OptionsGroup>(),
     334             :           db::get<Convergence::Tags::IterationId<OptionsGroup>>(box) + 1);
     335             :     }
     336             : 
     337             :     db::mutate<Convergence::Tags::IterationId<OptionsGroup>, correction_tag,
     338             :                linear_operator_applied_to_correction_tag,
     339             :                NonlinearSolver::Tags::Globalization<
     340             :                    Convergence::Tags::IterationId<OptionsGroup>>,
     341             :                NonlinearSolver::Tags::StepLength<OptionsGroup>,
     342             :                globalization_fields_tag>(
     343             :         [](const gsl::not_null<size_t*> iteration_id, const auto correction,
     344             :            const auto linear_operator_applied_to_correction,
     345             :            const gsl::not_null<size_t*> globalization_iteration_id,
     346             :            const gsl::not_null<double*> step_length,
     347             :            const auto globalization_fields, const auto& fields,
     348             :            const double damping_factor) {
     349             :           ++(*iteration_id);
     350             :           // Begin the linear solve with a zero initial guess
     351             :           *correction =
     352             :               make_with_value<typename correction_tag::type>(fields, 0.);
     353             :           // Since the initial guess is zero, we don't need to apply the linear
     354             :           // operator to it but can just set it to zero as well. Linear things
     355             :           // are nice :)
     356             :           *linear_operator_applied_to_correction = make_with_value<
     357             :               typename linear_operator_applied_to_correction_tag::type>(fields,
     358             :                                                                         0.);
     359             :           // Prepare line search globalization
     360             :           *globalization_iteration_id = 0;
     361             :           *step_length = damping_factor;
     362             :           *globalization_fields = fields;
     363             :         },
     364             :         make_not_null(&box), db::get<fields_tag>(box),
     365             :         db::get<NonlinearSolver::Tags::DampingFactor<OptionsGroup>>(box));
     366             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     367             :   }
     368             : };
     369             : 
     370             : // Between `PrepareStep` and this action the linear solver has run, so the
     371             : // `correction_tag` is now filled with the solution to the linearized problem.
     372             : // We just take a step in the direction of the correction.
     373             : //
     374             : // The `Globalize` action will jump back here in case the step turned out to not
     375             : // decrease the residual sufficiently. It will have adjusted the step length, so
     376             : // we can just try again with a step of that length.
     377             : template <typename FieldsTag, typename OptionsGroup, typename Label,
     378             :           typename ArraySectionIdTag>
     379             : struct PerformStep {
     380             :  private:
     381             :   using fields_tag = FieldsTag;
     382             :   using correction_tag =
     383             :       db::add_tag_prefix<NonlinearSolver::Tags::Correction, fields_tag>;
     384             :   using globalization_fields_tag =
     385             :       db::add_tag_prefix<NonlinearSolver::Tags::Globalization, fields_tag>;
     386             : 
     387             :  public:
     388             :   using const_global_cache_tags =
     389             :       tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;
     390             : 
     391             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     392             :             typename ArrayIndex, typename ActionList,
     393             :             typename ParallelComponent>
     394             :   static Parallel::iterable_action_return_t apply(
     395             :       db::DataBox<DbTagsList>& box,
     396             :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     397             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     398             :       const ArrayIndex& array_index, const ActionList /*meta*/,
     399             :       const ParallelComponent* const /*meta*/) {
     400             :     // Skip to the end of the step on elements that are not part of the section
     401             :     if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {
     402             :       if (not db::get<
     403             :               Parallel::Tags::Section<ParallelComponent, ArraySectionIdTag>>(
     404             :               box)) {
     405             :         constexpr size_t globalize_index =
     406             :             tmpl::index_of<ActionList, Globalize<FieldsTag, OptionsGroup, Label,
     407             :                                                  ArraySectionIdTag>>::value;
     408             :         return {Parallel::AlgorithmExecution::Continue, globalize_index};
     409             :       }
     410             :     }
     411             : 
     412             :     if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     413             :                  ::Verbosity::Debug)) {
     414             :       Parallel::printf(
     415             :           "%s %s(%zu): Perform step with length: %g\n", get_output(array_index),
     416             :           pretty_type::name<OptionsGroup>(),
     417             :           db::get<Convergence::Tags::IterationId<OptionsGroup>>(box),
     418             :           db::get<NonlinearSolver::Tags::StepLength<OptionsGroup>>(box));
     419             :     }
     420             : 
     421             :     // Apply the correction that the linear solve has determined to attempt
     422             :     // improving the nonlinear solution
     423             :     db::mutate<fields_tag>(
     424             :         [](const auto fields, const auto& correction, const double step_length,
     425             :            const auto& globalization_fields) {
     426             :           *fields = globalization_fields + step_length * correction;
     427             :         },
     428             :         make_not_null(&box), db::get<correction_tag>(box),
     429             :         db::get<NonlinearSolver::Tags::StepLength<OptionsGroup>>(box),
     430             :         db::get<globalization_fields_tag>(box));
     431             : 
     432             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     433             :   }
     434             : };
     435             : 
     436             : // Between `PerformStep` and this action the nonlinear operator has been applied
     437             : // to the updated fields. The residual is up-to-date because it is a compute
     438             : // tag, so at this point we need to check if the step has sufficiently reduced
     439             : // the residual. We perform a global reduction to the `ResidualMonitor` for this
     440             : // purpose.
     441             : template <typename FieldsTag, typename OptionsGroup, typename Label,
     442             :           typename ArraySectionIdTag>
     443             : struct ContributeToResidualMagnitudeReduction {
     444             :  private:
     445             :   using fields_tag = FieldsTag;
     446             :   using nonlinear_residual_tag =
     447             :       db::add_tag_prefix<NonlinearSolver::Tags::Residual, fields_tag>;
     448             : 
     449             :  public:
     450             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     451             :             typename ArrayIndex, typename ActionList,
     452             :             typename ParallelComponent>
     453             :   static Parallel::iterable_action_return_t apply(
     454             :       db::DataBox<DbTagsList>& box,
     455             :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     456             :       Parallel::GlobalCache<Metavariables>& cache,
     457             :       const ArrayIndex& array_index, const ActionList /*meta*/,
     458             :       const ParallelComponent* const /*meta*/) {
     459             :     const auto& residual = db::get<nonlinear_residual_tag>(box);
     460             :     const double local_residual_magnitude_square =
     461             :         LinearSolver::inner_product(residual, residual);
     462             :     ResidualReductionData reduction_data{
     463             :         db::get<Convergence::Tags::IterationId<OptionsGroup>>(box),
     464             :         db::get<NonlinearSolver::Tags::Globalization<
     465             :             Convergence::Tags::IterationId<OptionsGroup>>>(box),
     466             :         local_residual_magnitude_square,
     467             :         db::get<NonlinearSolver::Tags::StepLength<OptionsGroup>>(box)};
     468             :     auto& section = Parallel::get_section<ParallelComponent, ArraySectionIdTag>(
     469             :         make_not_null(&box));
     470             :     Parallel::contribute_to_reduction<
     471             :         CheckResidualMagnitude<FieldsTag, OptionsGroup, ParallelComponent>>(
     472             :         std::move(reduction_data),
     473             :         Parallel::get_parallel_component<ParallelComponent>(cache)[array_index],
     474             :         Parallel::get_parallel_component<
     475             :             ResidualMonitor<Metavariables, FieldsTag, OptionsGroup>>(cache),
     476             :         make_not_null(&section));
     477             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     478             :   }
     479             : };
     480             : 
     481             : // Wait for the `ResidualMonitor` to broadcast whether or not it has determined
     482             : // the step has sufficiently decreased the residual. If it has, we just proceed
     483             : // to `CompleteStep`. If it hasn't, the `ResidualMonitor` has also sent the new
     484             : // step length along, so we mutate it in the DataBox and jump back to
     485             : // `PerformStep` to try again with the updated step length.
     486             : template <typename FieldsTag, typename OptionsGroup, typename Label,
     487             :           typename ArraySectionIdTag>
     488             : struct Globalize {
     489             :   using const_global_cache_tags =
     490             :       tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;
     491             :   using inbox_tags = tmpl::list<Tags::GlobalizationResult<OptionsGroup>>;
     492             : 
     493             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     494             :             typename ArrayIndex, typename ActionList,
     495             :             typename ParallelComponent>
     496             :   static Parallel::iterable_action_return_t apply(
     497             :       db::DataBox<DbTagsList>& box, tuples::TaggedTuple<InboxTags...>& inboxes,
     498             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     499             :       const ArrayIndex& array_index, const ActionList /*meta*/,
     500             :       const ParallelComponent* const /*meta*/) {
     501             :     auto& inbox = get<Tags::GlobalizationResult<OptionsGroup>>(inboxes);
     502             :     const size_t iteration_id =
     503             :         db::get<Convergence::Tags::IterationId<OptionsGroup>>(box);
     504             :     if (inbox.find(iteration_id) == inbox.end()) {
     505             :       return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     506             :     }
     507             : 
     508             :     // Retrieve reduction data from inbox
     509             :     auto globalization_result = std::move(inbox.extract(iteration_id).mapped());
     510             : 
     511             :     // Elements that are not part of the section jump directly to the
     512             :     // `SolveLinearization` for the next step.
     513             :     constexpr size_t complete_step_index =
     514             :         tmpl::index_of<ActionList, CompleteStep<FieldsTag, OptionsGroup, Label,
     515             :                                                 ArraySectionIdTag>>::value;
     516             :     constexpr size_t prepare_step_index =
     517             :         tmpl::index_of<ActionList, PrepareStep<FieldsTag, OptionsGroup, Label,
     518             :                                                ArraySectionIdTag>>::value;
     519             :     if constexpr (not std::is_same_v<ArraySectionIdTag, void>) {
     520             :       if (not db::get<
     521             :               Parallel::Tags::Section<ParallelComponent, ArraySectionIdTag>>(
     522             :               box)) {
     523             :         const bool globalization_is_complete =
     524             :             std::holds_alternative<Convergence::HasConverged>(
     525             :                 globalization_result);
     526             :         // Wait until globalization is complete
     527             :         if (not globalization_is_complete) {
     528             :           return {Parallel::AlgorithmExecution::Retry, std::nullopt};
     529             :         }
     530             :         auto& has_converged =
     531             :             get<Convergence::HasConverged>(globalization_result);
     532             : 
     533             :         db::mutate<Convergence::Tags::HasConverged<OptionsGroup>,
     534             :                    Convergence::Tags::IterationId<OptionsGroup>>(
     535             :             [&has_converged](const gsl::not_null<Convergence::HasConverged*>
     536             :                                  local_has_converged,
     537             :                              const gsl::not_null<size_t*> local_iteration_id) {
     538             :               *local_has_converged = std::move(has_converged);
     539             :               ++(*local_iteration_id);
     540             :             },
     541             :             make_not_null(&box));
     542             : 
     543             :         return {Parallel::AlgorithmExecution::Continue,
     544             :                 get<Convergence::Tags::HasConverged<OptionsGroup>>(box)
     545             :                     ? (complete_step_index + 1)
     546             :                     : (prepare_step_index + 1)};
     547             :       }
     548             :     }
     549             : 
     550             :     if (std::holds_alternative<double>(globalization_result)) {
     551             :       if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     552             :                    ::Verbosity::Debug)) {
     553             :         Parallel::printf(
     554             :             "%s %s(%zu): Globalize(%zu)\n", get_output(array_index),
     555             :             pretty_type::name<OptionsGroup>(),
     556             :             db::get<Convergence::Tags::IterationId<OptionsGroup>>(box),
     557             :             db::get<NonlinearSolver::Tags::Globalization<
     558             :                 Convergence::Tags::IterationId<OptionsGroup>>>(box));
     559             :       }
     560             : 
     561             :       // Update the step length
     562             :       db::mutate<NonlinearSolver::Tags::StepLength<OptionsGroup>,
     563             :                  NonlinearSolver::Tags::Globalization<
     564             :                      Convergence::Tags::IterationId<OptionsGroup>>>(
     565             :           [&globalization_result](
     566             :               const gsl::not_null<double*> step_length,
     567             :               const gsl::not_null<size_t*> globalization_iteration_id) {
     568             :             *step_length = get<double>(globalization_result);
     569             :             ++(*globalization_iteration_id);
     570             :           },
     571             :           make_not_null(&box));
     572             :       // Continue globalization by taking a step with the updated step length
     573             :       // and checking the residual again
     574             :       constexpr size_t perform_step_index =
     575             :           tmpl::index_of<ActionList, PerformStep<FieldsTag, OptionsGroup, Label,
     576             :                                                  ArraySectionIdTag>>::value;
     577             :       return {Parallel::AlgorithmExecution::Continue, perform_step_index};
     578             :     }
     579             : 
     580             :     // At this point globalization is complete, so we proceed with the algorithm
     581             :     auto& has_converged = get<Convergence::HasConverged>(globalization_result);
     582             : 
     583             :     db::mutate<Convergence::Tags::HasConverged<OptionsGroup>>(
     584             :         [&has_converged](const gsl::not_null<Convergence::HasConverged*>
     585             :                              local_has_converged) {
     586             :           *local_has_converged = std::move(has_converged);
     587             :         },
     588             :         make_not_null(&box));
     589             : 
     590             :     return {Parallel::AlgorithmExecution::Continue, std::nullopt};
     591             :   }
     592             : };
     593             : 
     594             : // Jump back to `PrepareStep` to continue iterating if the algorithm has not yet
     595             : // converged, or complete the solve and proceed with the action list if it has
     596             : // converged. This is a separate action because the user has the opportunity to
     597             : // insert actions before the step completes, for example to do observations.
     598             : template <typename FieldsTag, typename OptionsGroup, typename Label,
     599             :           typename ArraySectionIdTag>
     600             : struct CompleteStep {
     601             :   using const_global_cache_tags =
     602             :       tmpl::list<logging::Tags::Verbosity<OptionsGroup>>;
     603             : 
     604             :   template <typename DbTagsList, typename... InboxTags, typename Metavariables,
     605             :             typename ArrayIndex, typename ActionList,
     606             :             typename ParallelComponent>
     607             :   static Parallel::iterable_action_return_t apply(
     608             :       db::DataBox<DbTagsList>& box,
     609             :       const tuples::TaggedTuple<InboxTags...>& /*inboxes*/,
     610             :       const Parallel::GlobalCache<Metavariables>& /*cache*/,
     611             :       const ArrayIndex& array_index, const ActionList /*meta*/,
     612             :       const ParallelComponent* const /*meta*/) {
     613             :     if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     614             :                  ::Verbosity::Debug)) {
     615             :       Parallel::printf(
     616             :           "%s %s(%zu): Complete step\n", get_output(array_index),
     617             :           pretty_type::name<OptionsGroup>(),
     618             :           db::get<Convergence::Tags::IterationId<OptionsGroup>>(box));
     619             :     }
     620             : 
     621             :     // Repeat steps until the solve has converged
     622             :     constexpr size_t prepare_step_index =
     623             :         tmpl::index_of<ActionList, PrepareStep<FieldsTag, OptionsGroup, Label,
     624             :                                                ArraySectionIdTag>>::value;
     625             :     constexpr size_t this_action_index =
     626             :         tmpl::index_of<ActionList, CompleteStep>::value;
     627             :     return {Parallel::AlgorithmExecution::Continue,
     628             :             get<Convergence::Tags::HasConverged<OptionsGroup>>(box)
     629             :                 ? (this_action_index + 1)
     630             :                 : prepare_step_index};
     631             :   }
     632             : };
     633             : 
     634             : }  // namespace NonlinearSolver::newton_raphson::detail

Generated by: LCOV version 1.14