SpECTRE Documentation Coverage Report
Current view: top level - ParallelAlgorithms/NonlinearSolver/NewtonRaphson - ResidualMonitorActions.hpp Hit Total Coverage
Commit: 37c384043430860f87787999aa7399d01bb3d213 Lines: 0 1 0.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 <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 1.14