SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/LinearSolver - Gmres.hpp Hit Total Coverage
Commit: de0084593a37be7727c6c4da0be2184b0f8d9ed4 Lines: 4 45 8.9 %
Date: 2025-11-04 23:26:01
Legend: Lines: hit not hit

          Line data    Source code
       1           0 : // Distributed under the MIT License.
       2             : // See LICENSE.txt for details.
       3             : 
       4             : #pragma once
       5             : 
       6             : #include <cstddef>
       7             : #include <memory>
       8             : #include <optional>
       9             : #include <pup.h>
      10             : #include <type_traits>
      11             : #include <utility>
      12             : 
      13             : #include "DataStructures/DynamicMatrix.hpp"
      14             : #include "DataStructures/DynamicVector.hpp"
      15             : #include "IO/Logging/Verbosity.hpp"
      16             : #include "NumericalAlgorithms/Convergence/Criteria.hpp"
      17             : #include "NumericalAlgorithms/Convergence/HasConverged.hpp"
      18             : #include "NumericalAlgorithms/Convergence/Reason.hpp"
      19             : #include "NumericalAlgorithms/LinearSolver/InnerProduct.hpp"
      20             : #include "NumericalAlgorithms/LinearSolver/LinearSolver.hpp"
      21             : #include "Options/Auto.hpp"
      22             : #include "Options/String.hpp"
      23             : #include "Utilities/EqualWithinRoundoff.hpp"
      24             : #include "Utilities/Gsl.hpp"
      25             : #include "Utilities/Registration.hpp"
      26             : #include "Utilities/Serialization/CharmPupable.hpp"
      27             : #include "Utilities/TMPL.hpp"
      28             : #include "Utilities/TypeTraits/GetFundamentalType.hpp"
      29             : 
      30             : namespace LinearSolver {
      31           1 : namespace gmres::detail {
      32             : 
      33             : // Perform an Arnoldi orthogonalization to find a new `operand` that is
      34             : // orthogonal to all vectors in `basis_history`. Appends a new column to the
      35             : // `orthogonalization_history` that holds the inner product of the intermediate
      36             : // `operand` with each vector in the `basis_history` and itself.
      37             : template <typename VarsType>
      38             : void arnoldi_orthogonalize(
      39             :     const gsl::not_null<VarsType*> operand,
      40             :     const gsl::not_null<
      41             :         blaze::DynamicMatrix<tt::get_complex_or_fundamental_type_t<VarsType>>*>
      42             :         orthogonalization_history,
      43             :     const std::vector<VarsType>& basis_history, const size_t iteration) {
      44             :   // Resize matrix and make sure the new entries that are not being filled below
      45             :   // are zero.
      46             :   orthogonalization_history->resize(iteration + 2, iteration + 1);
      47             :   for (size_t j = 0; j < iteration; ++j) {
      48             :     (*orthogonalization_history)(iteration + 1, j) = 0.;
      49             :   }
      50             :   // Arnoldi orthogonalization
      51             :   for (size_t j = 0; j < iteration + 1; ++j) {
      52             :     const auto orthogonalization = inner_product(basis_history[j], *operand);
      53             :     (*orthogonalization_history)(j, iteration) = orthogonalization;
      54             :     *operand -= orthogonalization * basis_history[j];
      55             :   }
      56             :   const double normalization = sqrt(magnitude_square(*operand));
      57             :   (*orthogonalization_history)(iteration + 1, iteration) = normalization;
      58             :   // Avoid an FPE if the new operand norm is exactly zero. In that case the
      59             :   // problem is solved and the algorithm will terminate (see Proposition 9.3 in
      60             :   // \cite Saad2003). Since there will be no next iteration we don't need to
      61             :   // normalize the operand.
      62             :   if (UNLIKELY(normalization == 0.)) {
      63             :     return;
      64             :   }
      65             :   *operand /= normalization;
      66             : }
      67             : 
      68             : // Solve the linear least-squares problem `||beta - H * y||` for `y`, where `H`
      69             : // is the Hessenberg matrix given by `orthogonalization_history` and `beta` is
      70             : // the vector `(initial_residual, 0, 0, ...)` by updating the QR decomposition
      71             : // of `H` from the previous iteration with a Givens rotation.
      72             : template <typename ValueType>
      73             : void solve_minimal_residual(
      74             :     gsl::not_null<blaze::DynamicMatrix<ValueType>*> orthogonalization_history,
      75             :     gsl::not_null<blaze::DynamicVector<ValueType>*> residual_history,
      76             :     gsl::not_null<blaze::DynamicVector<double>*> givens_sine_history,
      77             :     gsl::not_null<blaze::DynamicVector<ValueType>*> givens_cosine_history,
      78             :     size_t iteration);
      79             : 
      80             : // Find the vector that minimizes the residual by inverting the upper
      81             : // triangular matrix obtained above.
      82             : template <typename ValueType>
      83             : blaze::DynamicVector<ValueType> minimal_residual_vector(
      84             :     const blaze::DynamicMatrix<ValueType>& orthogonalization_history,
      85             :     const blaze::DynamicVector<ValueType>& residual_history);
      86             : 
      87             : }  // namespace gmres::detail
      88             : 
      89             : namespace Serial {
      90             : 
      91             : /// Disables the iteration callback at compile-time
      92           1 : struct NoIterationCallback {};
      93             : 
      94             : /// \cond
      95             : template <typename VarsType, typename Preconditioner,
      96             :           typename LinearSolverRegistrars>
      97             : struct Gmres;
      98             : /// \endcond
      99             : 
     100             : namespace Registrars {
     101             : 
     102             : /// Registers the `LinearSolver::Serial::Gmres` linear solver.
     103             : template <typename VarsType>
     104           1 : struct Gmres {
     105             :   template <typename LinearSolverRegistrars>
     106           0 :   using f = Serial::Gmres<VarsType, LinearSolver<LinearSolverRegistrars>,
     107             :                           LinearSolverRegistrars>;
     108             : };
     109             : }  // namespace Registrars
     110             : 
     111             : /*!
     112             :  * \brief A serial GMRES iterative solver for nonsymmetric linear systems of
     113             :  * equations.
     114             :  *
     115             :  * This is an iterative algorithm to solve general linear equations \f$Ax=b\f$
     116             :  * where \f$A\f$ is a linear operator. See \cite Saad2003, chapter 6.5 for a
     117             :  * description of the GMRES algorithm, Algorithm 9.6 for this implementation,
     118             :  * and Sec. 6.5.9 for the extension to complex arithmetic.
     119             :  * The GMRES method is matrix-free, which means the operator \f$A\f$ needs not
     120             :  * be provided explicitly as a matrix but only the operator action \f$A(x)\f$
     121             :  * must be provided for an argument \f$x\f$.
     122             :  *
     123             :  * The GMRES algorithm does not require the operator \f$A\f$ to be symmetric or
     124             :  * positive-definite. Note that other algorithms such as conjugate gradients may
     125             :  * be more efficient for symmetric positive-definite operators.
     126             :  *
     127             :  * \par Convergence:
     128             :  * Given a set of \f$N_A\f$ equations (e.g. through an \f$N_A\times N_A\f$
     129             :  * matrix) the GMRES algorithm will converge to numerical precision in at most
     130             :  * \f$N_A\f$ iterations. However, depending on the properties of the linear
     131             :  * operator, an approximate solution can ideally be obtained in only a few
     132             :  * iterations. See \cite Saad2003, section 6.11.4 for details on the convergence
     133             :  * of the GMRES algorithm.
     134             :  *
     135             :  * \par Restarting:
     136             :  * This implementation of the GMRES algorithm supports restarting, as detailed
     137             :  * in \cite Saad2003, section 6.5.5. Since the GMRES algorithm iteratively
     138             :  * builds up an orthogonal basis of the solution space the cost of each
     139             :  * iteration increases linearly with the number of iterations. Therefore it is
     140             :  * sometimes helpful to restart the algorithm every \f$N_\mathrm{restart}\f$
     141             :  * iterations, discarding the set of basis vectors and starting again from the
     142             :  * current solution estimate. This strategy can improve the performance of the
     143             :  * solver, but note that the solver can stagnate for non-positive-definite
     144             :  * operators and is not guaranteed to converge within \f$N_A\f$ iterations
     145             :  * anymore. Set the `restart` argument of the constructor to
     146             :  * \f$N_\mathrm{restart}\f$ to activate restarting, or set it to 'None' to
     147             :  * deactivate restarting.
     148             :  *
     149             :  * \par Preconditioning:
     150             :  * This implementation of the GMRES algorithm also supports preconditioning.
     151             :  * You can provide a linear operator \f$P\f$ that approximates the inverse of
     152             :  * the operator \f$A\f$ to accelerate the convergence of the linear solve.
     153             :  * The algorithm is right-preconditioned, which allows the preconditioner to
     154             :  * change in every iteration ("flexible" variant). See \cite Saad2003, sections
     155             :  * 9.3.2 and 9.4.1 for details. This implementation follows Algorithm 9.6 in
     156             :  * \cite Saad2003.
     157             :  *
     158             :  * \par Improvements:
     159             :  * Further improvements can potentially be implemented for this algorithm, see
     160             :  * e.g. \cite Ayachour2003.
     161             :  *
     162             :  * \example
     163             :  * \snippet NumericalAlgorithms/LinearSolver/Test_Gmres.cpp gmres_example
     164             :  *
     165             :  * \tparam VarsType The type of the vectors `x` and `b` in the linear equation
     166             :  * $Ax=b$. This type must support addition, subtraction, scalar multiplication,
     167             :  * and an inner product. It must expose an `ElementType` alias that is the type
     168             :  * of the components of the vector, e.g. `double` or `std::complex<double>`.
     169             :  */
     170             : template <typename VarsType, typename Preconditioner = NoPreconditioner,
     171             :           typename LinearSolverRegistrars =
     172             :               tmpl::list<Registrars::Gmres<VarsType>>>
     173           1 : class Gmres final : public PreconditionedLinearSolver<Preconditioner,
     174             :                                                       LinearSolverRegistrars> {
     175             :  private:
     176           0 :   using Base =
     177             :       PreconditionedLinearSolver<Preconditioner, LinearSolverRegistrars>;
     178           0 :   using ValueType = tt::get_complex_or_fundamental_type_t<VarsType>;
     179             : 
     180           0 :   struct ConvergenceCriteria {
     181           0 :     using type = Convergence::Criteria;
     182           0 :     static constexpr Options::String help =
     183             :         "Determine convergence of the algorithm";
     184             :   };
     185           0 :   struct Restart {
     186           0 :     using type = Options::Auto<size_t, Options::AutoLabel::None>;
     187           0 :     static constexpr Options::String help =
     188             :         "Iterations to run before restarting, or 'None' to disable restarting. "
     189             :         "Note that the solver is not guaranteed to converge anymore if you "
     190             :         "enable restarting.";
     191           0 :     static type suggested_value() { return {}; }
     192             :   };
     193           0 :   struct Verbosity {
     194           0 :     using type = ::Verbosity;
     195           0 :     static constexpr Options::String help = "Logging verbosity";
     196             :   };
     197             : 
     198             :  public:
     199           0 :   static constexpr Options::String help =
     200             :       "A serial GMRES iterative solver for nonsymmetric linear systems of\n"
     201             :       "equations Ax=b. It will converge to numerical precision in at most N_A\n"
     202             :       "iterations, where N_A is the number of equations represented by the\n"
     203             :       "linear operator A, but will ideally converge to a reasonable\n"
     204             :       "approximation of the solution x in only a few iterations.\n"
     205             :       "\n"
     206             :       "Preconditioning: Specify a preconditioner to run in every GMRES "
     207             :       "iteration to accelerate the solve, or 'None' to disable "
     208             :       "preconditioning. The choice of preconditioner can be crucial to obtain "
     209             :       "good convergence.\n"
     210             :       "\n"
     211             :       "Restarting: It is sometimes helpful to restart the algorithm every\n"
     212             :       "N_restart iterations to speed it up. Note that it can stagnate for\n"
     213             :       "non-positive-definite matrices and is not guaranteed to converge\n"
     214             :       "within N_A iterations anymore when restarting is activated.\n"
     215             :       "Activate restarting by setting the 'Restart' option to N_restart, or\n"
     216             :       "deactivate restarting by setting it to 'None'.";
     217           0 :   using options = tmpl::flatten<tmpl::list<
     218             :       ConvergenceCriteria, Verbosity, Restart,
     219             :       tmpl::conditional_t<std::is_same_v<Preconditioner, NoPreconditioner>,
     220             :                           tmpl::list<>, typename Base::PreconditionerOption>>>;
     221             : 
     222           0 :   Gmres(Convergence::Criteria convergence_criteria, ::Verbosity verbosity,
     223             :         std::optional<size_t> restart = std::nullopt,
     224             :         std::optional<typename Base::PreconditionerType> local_preconditioner =
     225             :             std::nullopt,
     226             :         const Options::Context& context = {});
     227             : 
     228           0 :   Gmres() = default;
     229           0 :   Gmres(Gmres&&) = default;
     230           0 :   Gmres& operator=(Gmres&&) = default;
     231           0 :   ~Gmres() override = default;
     232             : 
     233           0 :   Gmres(const Gmres& rhs);
     234           0 :   Gmres& operator=(const Gmres& rhs);
     235             : 
     236           0 :   std::unique_ptr<LinearSolver<LinearSolverRegistrars>> get_clone()
     237             :       const override {
     238             :     return std::make_unique<Gmres>(*this);
     239             :   }
     240             : 
     241             :   /// \cond
     242             :   explicit Gmres(CkMigrateMessage* m);
     243             :   using PUP::able::register_constructor;
     244             :   WRAPPED_PUPable_decl_template(Gmres);  // NOLINT
     245             :   /// \endcond
     246             : 
     247           0 :   void initialize() {
     248             :     orthogonalization_history_.reserve(restart_ + 1);
     249             :     residual_history_.reserve(restart_ + 1);
     250             :     givens_sine_history_.reserve(restart_);
     251             :     givens_cosine_history_.reserve(restart_);
     252             :     basis_history_.resize(restart_ + 1);
     253             :     preconditioned_basis_history_.resize(restart_);
     254             :   }
     255             : 
     256           0 :   const Convergence::Criteria& convergence_criteria() const {
     257             :     return convergence_criteria_;
     258             :   }
     259           0 :   ::Verbosity verbosity() const { return verbosity_; }
     260           0 :   size_t restart() const { return restart_; }
     261             : 
     262           0 :   void pup(PUP::er& p) override {  // NOLINT
     263             :     Base::pup(p);
     264             :     p | convergence_criteria_;
     265             :     p | verbosity_;
     266             :     p | restart_;
     267             :     if (p.isUnpacking()) {
     268             :       initialize();
     269             :     }
     270             :   }
     271             : 
     272             :   template <typename LinearOperator, typename SourceType,
     273             :             typename... OperatorArgs,
     274             :             typename IterationCallback = NoIterationCallback>
     275           0 :   Convergence::HasConverged solve(
     276             :       gsl::not_null<VarsType*> initial_guess_in_solution_out,
     277             :       const LinearOperator& linear_operator, const SourceType& source,
     278             :       const std::tuple<OperatorArgs...>& operator_args = std::tuple{},
     279             :       const IterationCallback& iteration_callback =
     280             :           NoIterationCallback{}) const;
     281             : 
     282           0 :   void reset() override {
     283             :     // Nothing to reset. Only call into base class to reset preconditioner.
     284             :     Base::reset();
     285             :   }
     286             : 
     287             :  private:
     288           0 :   Convergence::Criteria convergence_criteria_{};
     289           0 :   ::Verbosity verbosity_{::Verbosity::Verbose};
     290           0 :   size_t restart_{};
     291             : 
     292             :   // Memory buffers to avoid re-allocating memory for successive solves:
     293             :   // The `orthogonalization_history_` is built iteratively from inner products
     294             :   // between existing and potential basis vectors and then Givens-rotated to
     295             :   // become upper-triangular.
     296             :   // NOLINTNEXTLINE(spectre-mutable)
     297           0 :   mutable blaze::DynamicMatrix<ValueType> orthogonalization_history_{};
     298             :   // The `residual_history_` holds the remaining residual in its last entry, and
     299             :   // the other entries `g` "source" the minimum residual vector `y` in
     300             :   // `R * y = g` where `R` is the upper-triangular `orthogonalization_history_`.
     301             :   // NOLINTNEXTLINE(spectre-mutable)
     302           0 :   mutable blaze::DynamicVector<ValueType> residual_history_{};
     303             :   // These represent the accumulated Givens rotations up to the current
     304             :   // iteration.
     305             :   // NOLINTNEXTLINE(spectre-mutable)
     306           0 :   mutable blaze::DynamicVector<double> givens_sine_history_{};
     307             :   // NOLINTNEXTLINE(spectre-mutable)
     308           0 :   mutable blaze::DynamicVector<ValueType> givens_cosine_history_{};
     309             :   // These represent the orthogonal Krylov-subspace basis that is constructed
     310             :   // iteratively by Arnoldi-orthogonalizing a new vector in each iteration and
     311             :   // appending it to the `basis_history_`.
     312             :   // NOLINTNEXTLINE(spectre-mutable)
     313           0 :   mutable std::vector<VarsType> basis_history_{};
     314             :   // When a preconditioner is used it is applied to each new basis vector. The
     315             :   // preconditioned basis is used to construct the solution when the algorithm
     316             :   // has converged.
     317             :   // NOLINTNEXTLINE(spectre-mutable)
     318           0 :   mutable std::vector<VarsType> preconditioned_basis_history_{};
     319             : };
     320             : 
     321             : template <typename VarsType, typename Preconditioner,
     322             :           typename LinearSolverRegistrars>
     323             : Gmres<VarsType, Preconditioner, LinearSolverRegistrars>::Gmres(
     324             :     Convergence::Criteria convergence_criteria, ::Verbosity verbosity,
     325             :     std::optional<size_t> restart,
     326             :     std::optional<typename Base::PreconditionerType> local_preconditioner,
     327             :     const Options::Context& context)
     328             :     // clang-tidy: trivially copyable
     329             :     : Base(std::move(local_preconditioner)),
     330             :       convergence_criteria_(std::move(convergence_criteria)),  // NOLINT
     331             :       verbosity_(std::move(verbosity)),                        // NOLINT
     332             :       restart_(restart.value_or(convergence_criteria_.max_iterations)) {
     333             :   if (restart_ == 0) {
     334             :     PARSE_ERROR(context,
     335             :                 "Can't restart every '0' iterations. Set to a nonzero "
     336             :                 "number, or to 'None' if you meant to disable restarting.");
     337             :   }
     338             :   initialize();
     339             : }
     340             : 
     341             : // Define copy constructors. They don't have to copy the memory buffers but
     342             : // only resize them. They take care of copying the preconditioner by calling
     343             : // into the base class.
     344             : template <typename VarsType, typename Preconditioner,
     345             :           typename LinearSolverRegistrars>
     346             : Gmres<VarsType, Preconditioner, LinearSolverRegistrars>::Gmres(const Gmres& rhs)
     347             :     : Base(rhs),
     348             :       convergence_criteria_(rhs.convergence_criteria_),
     349             :       verbosity_(rhs.verbosity_),
     350             :       restart_(rhs.restart_) {
     351             :   initialize();
     352             : }
     353             : template <typename VarsType, typename Preconditioner,
     354             :           typename LinearSolverRegistrars>
     355             : Gmres<VarsType, Preconditioner, LinearSolverRegistrars>&
     356             : Gmres<VarsType, Preconditioner, LinearSolverRegistrars>::operator=(
     357             :     const Gmres& rhs) {
     358             :   Base::operator=(rhs);
     359             :   convergence_criteria_ = rhs.convergence_criteria_;
     360             :   verbosity_ = rhs.verbosity_;
     361             :   restart_ = rhs.restart_;
     362             :   initialize();
     363             :   return *this;
     364             : }
     365             : 
     366             : /// \cond
     367             : template <typename VarsType, typename Preconditioner,
     368             :           typename LinearSolverRegistrars>
     369             : Gmres<VarsType, Preconditioner, LinearSolverRegistrars>::Gmres(
     370             :     CkMigrateMessage* m)
     371             :     : Base(m) {}
     372             : /// \endcond
     373             : 
     374             : template <typename VarsType, typename Preconditioner,
     375             :           typename LinearSolverRegistrars>
     376             : template <typename LinearOperator, typename SourceType,
     377             :           typename... OperatorArgs, typename IterationCallback>
     378             : Convergence::HasConverged
     379           0 : Gmres<VarsType, Preconditioner, LinearSolverRegistrars>::solve(
     380             :     const gsl::not_null<VarsType*> initial_guess_in_solution_out,
     381             :     const LinearOperator& linear_operator, const SourceType& source,
     382             :     const std::tuple<OperatorArgs...>& operator_args,
     383             :     const IterationCallback& iteration_callback) const {
     384             :   constexpr bool use_preconditioner =
     385             :       not std::is_same_v<Preconditioner, NoPreconditioner>;
     386             :   constexpr bool use_iteration_callback =
     387             :       not std::is_same_v<IterationCallback, NoIterationCallback>;
     388             : 
     389             :   // Could pre-allocate memory for the basis-history vectors here. Not doing
     390             :   // that for now because we don't know how many iterations we'll need.
     391             :   // Estimating the number of iterations and pre-allocating memory is a possible
     392             :   // performance optimization.
     393             : 
     394             :   auto& solution = *initial_guess_in_solution_out;
     395             :   Convergence::HasConverged has_converged{};
     396             :   size_t iteration = 0;
     397             : 
     398             :   while (not has_converged) {
     399             :     const auto& initial_guess = *initial_guess_in_solution_out;
     400             :     auto& initial_operand = basis_history_[0];
     401             :     // Apply the linear operator to the initial guess. This can be skipped if
     402             :     // the initial guess is zero, because then the linear operator applied to it
     403             :     // is also zero.
     404             :     if (equal_within_roundoff(initial_guess, 0.)) {
     405             :       initial_operand = source;
     406             :     } else {
     407             :       std::apply(
     408             :           linear_operator,
     409             :           std::tuple_cat(std::forward_as_tuple(make_not_null(&initial_operand),
     410             :                                                initial_guess),
     411             :                          operator_args));
     412             :       initial_operand *= -1.;
     413             :       initial_operand += source;
     414             :     }
     415             :     const double initial_residual_magnitude =
     416             :         sqrt(magnitude_square(initial_operand));
     417             :     has_converged = Convergence::HasConverged{convergence_criteria_, iteration,
     418             :                                               initial_residual_magnitude,
     419             :                                               initial_residual_magnitude};
     420             :     if constexpr (use_iteration_callback) {
     421             :       iteration_callback(has_converged);
     422             :     }
     423             :     if (UNLIKELY(has_converged)) {
     424             :       break;
     425             :     }
     426             :     initial_operand /= initial_residual_magnitude;
     427             :     residual_history_.resize(1);
     428             :     residual_history_[0] = initial_residual_magnitude;
     429             :     for (size_t k = 0; k < restart_; ++k) {
     430             :       auto& operand = basis_history_[k + 1];
     431             :       if constexpr (use_preconditioner) {
     432             :         if (this->has_preconditioner()) {
     433             :           // Begin the preconditioner at an initial guess of 0. Not all
     434             :           // preconditioners take the initial guess into account.
     435             :           preconditioned_basis_history_[k] =
     436             :               make_with_value<VarsType>(initial_operand, 0.);
     437             :           this->preconditioner().solve(
     438             :               make_not_null(&preconditioned_basis_history_[k]), linear_operator,
     439             :               basis_history_[k], operator_args);
     440             :         }
     441             :       }
     442             :       std::apply(linear_operator,
     443             :                  std::tuple_cat(std::forward_as_tuple(
     444             :                                     make_not_null(&operand),
     445             :                                     this->has_preconditioner()
     446             :                                         ? preconditioned_basis_history_[k]
     447             :                                         : basis_history_[k]),
     448             :                                 operator_args));
     449             :       // Find a new orthogonal basis vector of the Krylov subspace
     450             :       gmres::detail::arnoldi_orthogonalize(
     451             :           make_not_null(&operand), make_not_null(&orthogonalization_history_),
     452             :           basis_history_, k);
     453             :       // Least-squares solve for the minimal residual
     454             :       gmres::detail::solve_minimal_residual(
     455             :           make_not_null(&orthogonalization_history_),
     456             :           make_not_null(&residual_history_),
     457             :           make_not_null(&givens_sine_history_),
     458             :           make_not_null(&givens_cosine_history_), k);
     459             :       ++iteration;
     460             :       has_converged = Convergence::HasConverged{
     461             :           convergence_criteria_, iteration, abs(residual_history_[k + 1]),
     462             :           initial_residual_magnitude};
     463             :       if constexpr (use_iteration_callback) {
     464             :         iteration_callback(has_converged);
     465             :       }
     466             :       if (UNLIKELY(has_converged)) {
     467             :         break;
     468             :       }
     469             :     }
     470             :     // Find the vector w.r.t. the constructed orthogonal basis of the Krylov
     471             :     // subspace that minimizes the residual
     472             :     const auto minres = gmres::detail::minimal_residual_vector(
     473             :         orthogonalization_history_, residual_history_);
     474             :     // Construct the solution from the orthogonal basis and the minimal residual
     475             :     // vector
     476             :     for (size_t i = 0; i < minres.size(); ++i) {
     477             :       solution += minres[i] * gsl::at(this->has_preconditioner()
     478             :                                           ? preconditioned_basis_history_
     479             :                                           : basis_history_,
     480             :                                       i);
     481             :     }
     482             :   }
     483             :   return has_converged;
     484             : }
     485             : 
     486             : /// \cond
     487             : template <typename VarsType, typename Preconditioner,
     488             :           typename LinearSolverRegistrars>
     489             : // NOLINTNEXTLINE
     490             : PUP::able::PUP_ID
     491             :     Gmres<VarsType, Preconditioner, LinearSolverRegistrars>::my_PUP_ID = 0;
     492             : /// \endcond
     493             : 
     494             : }  // namespace Serial
     495             : }  // namespace LinearSolver

Generated by: LCOV version 1.14