SpECTRE Documentation Coverage Report
Current view: top level - NumericalAlgorithms/LinearSolver - Gmres.hpp Coverage Total Hit
Commit: 0d1d717af84a81fb1284a20c4013708fe85cc2f0 Lines: 8.9 % 45 4
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 <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 2.0-1