SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/NonlinearSolver/NewtonRaphson - ResidualMonitorActions.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 <algorithm>
       7              : #include <cmath>
       8              : #include <cstddef>
       9              : #include <string>
      10              : #include <utility>
      11              : #include <variant>
      12              : 
      13              : #include "DataStructures/DataBox/DataBox.hpp"
      14              : #include "DataStructures/DataBox/PrefixHelpers.hpp"
      15              : #include "DataStructures/DataBox/Prefixes.hpp"
      16              : #include "IO/Logging/Verbosity.hpp"
      17              : #include "NumericalAlgorithms/Convergence/HasConverged.hpp"
      18              : #include "NumericalAlgorithms/Convergence/Reason.hpp"
      19              : #include "Parallel/GlobalCache.hpp"
      20              : #include "Parallel/Invoke.hpp"
      21              : #include "Parallel/Printf/Printf.hpp"
      22              : #include "ParallelAlgorithms/NonlinearSolver/NewtonRaphson/LineSearch.hpp"
      23              : #include "ParallelAlgorithms/NonlinearSolver/NewtonRaphson/Tags/InboxTags.hpp"
      24              : #include "ParallelAlgorithms/NonlinearSolver/Observe.hpp"
      25              : #include "Utilities/Gsl.hpp"
      26              : #include "Utilities/PrettyType.hpp"
      27              : 
      28              : /// \cond
      29              : namespace Convergence::Tags {
      30              : template <typename OptionsGroup>
      31              : struct Criteria;
      32              : }  // namespace Convergence::Tags
      33              : namespace LinearSolver::Tags {
      34              : template <typename Tag>
      35              : struct Magnitude;
      36              : template <typename Tag>
      37              : struct MagnitudeSquare;
      38              : }  // namespace LinearSolver::Tags
      39              : namespace NonlinearSolver::Tags {
      40              : template <typename Tag>
      41              : struct Globalization;
      42              : template <typename OptionsGroup>
      43              : struct MaxGlobalizationSteps;
      44              : template <typename Tag>
      45              : struct Residual;
      46              : template <typename OptionsGroup>
      47              : struct StepLength;
      48              : template <typename OptionsGroup>
      49              : struct SufficientDecrease;
      50              : }  // namespace NonlinearSolver::Tags
      51              : namespace logging::Tags {
      52              : template <typename OptionsGroup>
      53              : struct Verbosity;
      54              : }  // namespace logging::Tags
      55              : namespace tuples {
      56              : template <typename...>
      57              : class TaggedTuple;
      58              : }  // namespace tuples
      59              : /// \endcond
      60              : 
      61              : namespace NonlinearSolver::newton_raphson::detail {
      62              : 
      63              : template <typename FieldsTag, typename OptionsGroup, typename BroadcastTarget>
      64              : struct CheckResidualMagnitude {
      65              :   using fields_tag = FieldsTag;
      66              :   using residual_tag =
      67              :       db::add_tag_prefix<NonlinearSolver::Tags::Residual, fields_tag>;
      68              :   using residual_magnitude_square_tag =
      69              :       LinearSolver::Tags::MagnitudeSquare<residual_tag>;
      70              :   using initial_residual_magnitude_tag =
      71              :       ::Tags::Initial<LinearSolver::Tags::Magnitude<residual_tag>>;
      72              :   using prev_residual_magnitude_square_tag =
      73              :       NonlinearSolver::Tags::Globalization<residual_magnitude_square_tag>;
      74              : 
      75              :   template <typename ParallelComponent, typename DataBox,
      76              :             typename Metavariables, typename ArrayIndex, typename... Args>
      77              :   static void apply(DataBox& box, Parallel::GlobalCache<Metavariables>& cache,
      78              :                     const ArrayIndex& /*array_index*/,
      79              :                     const size_t iteration_id,
      80              :                     const size_t globalization_iteration_id,
      81              :                     const double next_residual_magnitude_square,
      82              :                     const double step_length) {
      83              :     const double residual_magnitude = sqrt(next_residual_magnitude_square);
      84              : 
      85              :     NonlinearSolver::observe_detail::contribute_to_reduction_observer<
      86              :         OptionsGroup, ParallelComponent>(
      87              :         iteration_id, globalization_iteration_id, residual_magnitude,
      88              :         step_length, cache);
      89              : 
      90              :     if (UNLIKELY(iteration_id == 0)) {
      91              :       db::mutate<initial_residual_magnitude_tag>(
      92              :           [residual_magnitude](
      93              :               const gsl::not_null<double*> initial_residual_magnitude) {
      94              :             *initial_residual_magnitude = residual_magnitude;
      95              :           },
      96              :           make_not_null(&box));
      97              :     } else {
      98              :       // Make sure we are converging. Far away from the solution the correction
      99              :       // determined by the linear solve might be bad, so we employ a
     100              :       // globalization strategy to guide the solver towards the solution when
     101              :       // the residual doesn't decrease sufficiently. See the `NewtonRaphson`
     102              :       // class documentation for details.
     103              :       const double sufficient_decrease =
     104              :           get<NonlinearSolver::Tags::SufficientDecrease<OptionsGroup>>(box);
     105              :       const double residual_magnitude_square =
     106              :           get<residual_magnitude_square_tag>(box);
     107              :       const double initial_residual_magnitude =
     108              :           get<initial_residual_magnitude_tag>(box);
     109              :       const double abs_tolerance =
     110              :           get<Convergence::Tags::Criteria<OptionsGroup>>(box).absolute_residual;
     111              :       const double rel_tolerance =
     112              :           get<Convergence::Tags::Criteria<OptionsGroup>>(box).relative_residual;
     113              :       // This is the directional derivative of the residual magnitude square
     114              :       // f(x) = |r(x)|^2 in the descent direction
     115              :       const double residual_magnitude_square_slope =
     116              :           -2. * residual_magnitude_square;
     117              :       // Check the sufficient decrease condition. Also make sure the residual
     118              :       // didn't hit the tolerance.
     119              :       if (residual_magnitude > abs_tolerance and
     120              :           residual_magnitude / initial_residual_magnitude > rel_tolerance and
     121              :           next_residual_magnitude_square >
     122              :               residual_magnitude_square + sufficient_decrease * step_length *
     123              :                                               residual_magnitude_square_slope) {
     124              :         // The residual didn't sufficiently decrease. Perform a globalization
     125              :         // step.
     126              :         if (globalization_iteration_id <
     127              :             get<NonlinearSolver::Tags::MaxGlobalizationSteps<OptionsGroup>>(
     128              :                 box)) {
     129              :           const double next_step_length = std::clamp(
     130              :               NonlinearSolver::newton_raphson::next_step_length(
     131              :                   globalization_iteration_id, step_length,
     132              :                   get<NonlinearSolver::Tags::StepLength<OptionsGroup>>(box),
     133              :                   residual_magnitude_square, residual_magnitude_square_slope,
     134              :                   next_residual_magnitude_square,
     135              :                   get<prev_residual_magnitude_square_tag>(box)),
     136              :               0.1 * step_length, 0.5 * step_length);
     137              :           db::mutate<NonlinearSolver::Tags::StepLength<OptionsGroup>,
     138              :                      prev_residual_magnitude_square_tag>(
     139              :               [step_length, next_residual_magnitude_square](
     140              :                   const gsl::not_null<double*> prev_step_length,
     141              :                   const gsl::not_null<double*> prev_residual_magnitude_square) {
     142              :                 *prev_step_length = step_length;
     143              :                 *prev_residual_magnitude_square =
     144              :                     next_residual_magnitude_square;
     145              :               },
     146              :               make_not_null(&box));
     147              :           // Do some logging
     148              :           if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     149              :                        ::Verbosity::Verbose)) {
     150              :             Parallel::printf(
     151              :                 "%s(%zu): Step with length %g didn't sufficiently decrease the "
     152              :                 "residual (possible overshoot). Residual: %e. Next step "
     153              :                 "length: %g.\n",
     154              :                 pretty_type::name<OptionsGroup>(), iteration_id, step_length,
     155              :                 residual_magnitude, next_step_length);
     156              :             if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     157              :                          ::Verbosity::Debug)) {
     158              :               Parallel::printf("Residual magnitude slope: %e\n",
     159              :                                residual_magnitude_square_slope);
     160              :             }
     161              :           }
     162              :           // Broadcast back to the elements signaling that they should perform a
     163              :           // globalization step, then return early.
     164              :           Parallel::receive_data<Tags::GlobalizationResult<OptionsGroup>>(
     165              :               Parallel::get_parallel_component<BroadcastTarget>(cache),
     166              :               iteration_id,
     167              :               std::variant<double, Convergence::HasConverged>{
     168              :                   next_step_length});
     169              :           return;
     170              :         } else {
     171              :           // We have performed the maximum number of globalization steps without
     172              :           // sufficiently decreasing the residual. The step length is so small
     173              :           // now that the algorithm won't be converging anymore. Treat this as
     174              :           // an error and stop the Newton-Raphson algorithm.
     175              :           Convergence::HasConverged convergence_error{
     176              :               Convergence::Reason::Error,
     177              :               "Failed to sufficiently decrease the residual in " +
     178              :                   std::to_string(globalization_iteration_id) +
     179              :                   " globalization steps. This is usually indicative of an "
     180              :                   "ill-posed problem, for example when the linearization of "
     181              :                   "the nonlinear operator is not computed correctly.",
     182              :               iteration_id};
     183              :           if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     184              :                        ::Verbosity::Quiet)) {
     185              :             Parallel::printf("%s(%zu): WARNING: %s\n",
     186              :                              pretty_type::name<OptionsGroup>(), iteration_id,
     187              :                              convergence_error.error_message());
     188              :           }
     189              :           Parallel::receive_data<Tags::GlobalizationResult<OptionsGroup>>(
     190              :               Parallel::get_parallel_component<BroadcastTarget>(cache),
     191              :               iteration_id,
     192              :               std::variant<double, Convergence::HasConverged>{
     193              :                   std::move(convergence_error)});
     194              :           return;
     195              :         }  // min_step_length
     196              :       }    // sufficient decrease condition
     197              :     }      // initial iteration
     198              : 
     199              :     db::mutate<residual_magnitude_square_tag>(
     200              :         [next_residual_magnitude_square](
     201              :             const gsl::not_null<double*> local_residual_magnitude_square) {
     202              :           *local_residual_magnitude_square = next_residual_magnitude_square;
     203              :         },
     204              :         make_not_null(&box));
     205              : 
     206              :     // At this point, the iteration is complete. We proceed with logging and
     207              :     // checking convergence before broadcasting back to the elements.
     208              : 
     209              :     // Determine whether the nonlinear solver has converged
     210              :     Convergence::HasConverged has_converged{
     211              :         get<Convergence::Tags::Criteria<OptionsGroup>>(box), iteration_id,
     212              :         residual_magnitude, get<initial_residual_magnitude_tag>(box)};
     213              : 
     214              :     // Do some logging
     215              :     if (UNLIKELY(get<logging::Tags::Verbosity<OptionsGroup>>(box) >=
     216              :                  ::Verbosity::Quiet)) {
     217              :       if (UNLIKELY(iteration_id == 0)) {
     218              :         Parallel::printf("%s initialized with residual: %e\n",
     219              :                          pretty_type::name<OptionsGroup>(), residual_magnitude);
     220              :       } else {
     221              :         Parallel::printf(
     222              :             "%s(%zu) iteration complete (%zu globalization steps, step length "
     223              :             "%g). Remaining residual: %e\n",
     224              :             pretty_type::name<OptionsGroup>(), iteration_id,
     225              :             globalization_iteration_id, step_length, residual_magnitude);
     226              :       }
     227              :     }
     228              :     if (UNLIKELY(has_converged and get<logging::Tags::Verbosity<OptionsGroup>>(
     229              :                                        box) >= ::Verbosity::Quiet)) {
     230              :       if (UNLIKELY(iteration_id == 0)) {
     231              :         Parallel::printf("%s has converged without any iterations: %s\n",
     232              :                          pretty_type::name<OptionsGroup>(), has_converged);
     233              :       } else {
     234              :         Parallel::printf("%s has converged in %zu iterations: %s\n",
     235              :                          pretty_type::name<OptionsGroup>(), iteration_id,
     236              :                          has_converged);
     237              :       }
     238              :     }
     239              : 
     240              :     Parallel::receive_data<Tags::GlobalizationResult<OptionsGroup>>(
     241              :         Parallel::get_parallel_component<BroadcastTarget>(cache), iteration_id,
     242              :         std::variant<double, Convergence::HasConverged>(
     243              :             // NOLINTNEXTLINE(performance-move-const-arg)
     244              :             std::move(has_converged)));
     245              :   }
     246              : };
     247              : 
     248              : }  // namespace NonlinearSolver::newton_raphson::detail
        

Generated by: LCOV version 2.0-1