Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
GMRESSolver.h
1// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2//-----------------------------------------------------------------------------
3// Copyright 2000-2026 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4// See the top-level COPYRIGHT file for details.
5// SPDX-License-Identifier: Apache-2.0
6//-----------------------------------------------------------------------------
7/*---------------------------------------------------------------------------*/
8/* solver_gmres.h (C) 2000-2026 */
9/* */
10/* GMRES solver method. . */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_GMRESSOLVER_H
13#define ARCCORE_ALINA_GMRESSOLVER_H
14/*---------------------------------------------------------------------------*/
15/*---------------------------------------------------------------------------*/
16/*
17 * This file is based on the work on AMGCL library (version march 2026)
18 * which can be found at https://github.com/ddemidov/amgcl.
19 *
20 * Copyright (c) 2012-2022 Denis Demidov <dennis.demidov@gmail.com>
21 * SPDX-License-Identifier: MIT
22 */
23/*---------------------------------------------------------------------------*/
24/*---------------------------------------------------------------------------*/
25
26#include "arccore/alina/SolverUtils.h"
27#include "arccore/alina/SolverBase.h"
28
29/*---------------------------------------------------------------------------*/
30/*---------------------------------------------------------------------------*/
31
32namespace Arcane::Alina
33{
34
35/*---------------------------------------------------------------------------*/
36/*---------------------------------------------------------------------------*/
40struct GMRESSolverParams
41{
42 using params = GMRESSolverParams;
43
45 Int32 M = 30;
46
48 ePreconditionerSideType pside = ePreconditionerSideType::right;
49
52
54 double tol = 1.0e-8;
55
57 double abstol = std::numeric_limits<double>::min();
58
60 //** Useful for searching for the null-space vectors of the system */
61 bool ns_search = false;
62
64 bool verbose = false;
65
66 GMRESSolverParams() = default;
67
68 GMRESSolverParams(const PropertyTree& p)
69 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, M)
70 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, pside)
71 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, maxiter)
72 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, tol)
73 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, abstol)
74 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, ns_search)
75 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, verbose)
76 {
77 p.check_params({ "M", "pside", "maxiter", "tol", "abstol", "ns_search", "verbose" });
78 }
79
80 void get(PropertyTree& p, const std::string& path) const
81 {
82 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, M);
83 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, pside);
84 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, maxiter);
85 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, tol);
86 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, abstol);
87 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, ns_search);
88 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, verbose);
89 }
90};
91
92/*---------------------------------------------------------------------------*/
93/*---------------------------------------------------------------------------*/
102template <class Backend, class InnerProduct = detail::default_inner_product>
104: public SolverBase
105{
106 public:
107
108 using backend_type = Backend;
109 using BackendType = Backend;
110
111 typedef typename Backend::vector vector;
112 typedef typename Backend::value_type value_type;
113 typedef typename Backend::params backend_params;
114
115 typedef typename math::scalar_of<value_type>::type scalar_type;
116 typedef typename math::rhs_of<value_type>::type rhs_type;
117 typedef typename math::inner_product_impl<rhs_type>::return_type coef_type;
118
119 using params = GMRESSolverParams;
120
122 GMRESSolver(const AlinaContext& context, size_t n,
123 const params& prm = params(),
124 const backend_params& backend_prm = backend_params(),
125 const InnerProduct& inner_product = InnerProduct())
126 : SolverBase(context)
127 , prm(prm)
128 , n(n)
129 , H(prm.M + 1, prm.M)
130 , s(prm.M + 1)
131 , cs(prm.M + 1)
132 , sn(prm.M + 1)
133 , r(Backend::create_vector(n, backend_prm))
134 , inner_product(inner_product)
135 {
136 v.reserve(prm.M + 1);
137 for (unsigned i = 0; i <= prm.M; ++i)
138 v.push_back(Backend::create_vector(n, backend_prm));
139 }
140
156 template <class Matrix, class Precond, class Vec1, class Vec2>
157 SolverResult operator()(Matrix const& A, Precond const& P, Vec1 const& rhs, Vec2& x) const
158 {
159 static const scalar_type zero = math::zero<scalar_type>();
160 static const scalar_type one = math::identity<scalar_type>();
161
162 ScopedStreamModifier ss(std::cout);
163
164 scalar_type norm_rhs = norm(rhs);
165 if (norm_rhs < Alina::detail::eps<scalar_type>(1)) {
166 if (prm.ns_search) {
167 norm_rhs = math::identity<scalar_type>();
168 }
169 else {
170 backend::clear(x);
171 return SolverResult(0, norm_rhs);
172 }
173 }
174
175 scalar_type eps = std::max(prm.tol * norm_rhs, prm.abstol);
176 scalar_type norm_r = zero;
177
178 size_t iter = 0;
179 while (true) {
180 if (prm.pside == ePreconditionerSideType::left) {
181 backend::residual(rhs, A, x, *v[0]);
182 P.apply(*v[0], *r);
183 }
184 else {
185 backend::residual(rhs, A, x, *r);
186 }
187
188 // -- Check stopping condition
189 norm_r = norm(*r);
190 if (norm_r < eps || iter >= prm.maxiter)
191 break;
192
193 // -- Inner GMRES iteration
194 backend::axpby(math::inverse(norm_r), *r, zero, *v[0]);
195
196 std::fill(s.begin(), s.end(), 0);
197 s[0] = norm_r;
198
199 unsigned j = 0;
200 while (true) {
201 // -- Arnoldi process
202 //
203 // Build an orthonormal basis V and matrix H such that
204 // A V_{i-1} = V_{i} H
205 vector& v_new = *v[j + 1];
206
207 preconditioner_spmv(prm.pside, P, A, *v[j], v_new, *r);
208
209 for (unsigned k = 0; k <= j; ++k) {
210 H(k, j) = inner_product(v_new, *v[k]);
211 backend::axpby(-H(k, j), *v[k], one, v_new);
212 }
213 H(j + 1, j) = norm(v_new);
214
215 backend::axpby(math::inverse(H(j + 1, j)), v_new, zero, v_new);
216
217 for (unsigned k = 0; k < j; ++k)
218 detail::apply_plane_rotation(H(k, j), H(k + 1, j), cs[k], sn[k]);
219
220 detail::generate_plane_rotation(H(j, j), H(j + 1, j), cs[j], sn[j]);
221 detail::apply_plane_rotation(H(j, j), H(j + 1, j), cs[j], sn[j]);
222 detail::apply_plane_rotation(s[j], s[j + 1], cs[j], sn[j]);
223
224 scalar_type inner_res = std::abs(s[j + 1]);
225
226 if (prm.verbose && iter % 5 == 0)
227 std::cout << iter << "\t" << std::scientific << inner_res / norm_rhs << std::endl;
228
229 // Check for termination
230 ++j, ++iter;
231 if (iter >= prm.maxiter || j >= prm.M || inner_res <= eps)
232 break;
233 }
234
235 // -- GMRES terminated: eval solution
236 for (unsigned i = j; i-- > 0;) {
237 s[i] /= H(i, i);
238 for (unsigned k = 0; k < i; ++k)
239 s[k] -= H(k, i) * s[i];
240 }
241
242 // -- Apply step
243 vector& dx = *r;
244 backend::lin_comb(j, s, v, zero, dx);
245
246 if (prm.pside == ePreconditionerSideType::left) {
247 backend::axpby(one, dx, one, x);
248 }
249 else {
250 vector& tmp = *v[0];
251 P.apply(dx, tmp);
252 backend::axpby(one, tmp, one, x);
253 }
254 }
255
256 return std::make_tuple(iter, norm_r / norm_rhs);
257 }
258
269 template <class Precond, class Vec1, class Vec2>
270 std::tuple<size_t, scalar_type>
271 operator()(Precond const& P, Vec1 const& rhs, Vec2& x) const
272 {
273 return (*this)(P.system_matrix(), P, rhs, x);
274 }
275
276 friend std::ostream& operator<<(std::ostream& os, const GMRESSolver& s)
277 {
278 return os << "Type: GMRES(" << s.prm.M << ")"
279 << "\nUnknowns: " << s.n
280 << "\nMemory footprint: " << human_readable_memory(s.bytes())
281 << std::endl;
282 }
283
284 public:
285
286 params prm;
287
288 size_t bytes() const
289 {
290 size_t b = 0;
291
292 b += H.size() * sizeof(coef_type);
293 b += backend::bytes(s);
294 b += backend::bytes(cs);
295 b += backend::bytes(sn);
296 b += backend::bytes(*r);
297
298 for (const auto& x : v)
299 b += backend::bytes(*x);
300
301 return b;
302 }
303
304 private:
305
306 size_t n;
307
309 mutable std::vector<coef_type> s, cs, sn;
310 std::shared_ptr<vector> r;
311 std::vector<std::shared_ptr<vector>> v;
312
313 InnerProduct inner_product;
314
315 template <class Vec>
316 scalar_type norm(const Vec& x) const
317 {
318 return std::abs(sqrt(inner_product(x, x)));
319 }
320};
321
322/*---------------------------------------------------------------------------*/
323/*---------------------------------------------------------------------------*/
324
325} // namespace Arcane::Alina
326
327/*---------------------------------------------------------------------------*/
328/*---------------------------------------------------------------------------*/
329
330#endif
Generalized Minimal Residual (GMRES) method.
GMRESSolver(const AlinaContext &context, size_t n, const params &prm=params(), const backend_params &backend_prm=backend_params(), const InnerProduct &inner_product=InnerProduct())
Preallocates necessary data structures for the system of size n.
std::tuple< size_t, scalar_type > operator()(Precond const &P, Vec1 const &rhs, Vec2 &x) const
Computes the solution for the given right-hand side.
SolverResult operator()(Matrix const &A, Precond const &P, Vec1 const &rhs, Vec2 &x) const
size_t bytes() const
Memory used in bytes.
Class to store parameters as a hierarchical key/value tree.
Definition AlinaUtils.h:113
Save ostream flags in constructor, restore in destructor.
Result of a solution.
Definition AlinaUtils.h:56
Matrix class, to be used by user.
std::int32_t Int32
Signed integer type of 32 bits.
Parameters for GMRES solver.
Definition GMRESSolver.h:41
ePreconditionerSideType pside
Preconditioning kind (left/right).
Definition GMRESSolver.h:48
bool verbose
Verbose output (show iterations and error).
Definition GMRESSolver.h:64
Int32 maxiter
Maximum number of iterations.
Definition GMRESSolver.h:51
Int32 M
Number of iterations before restart.
Definition GMRESSolver.h:45
double tol
Target relative residual error.
Definition GMRESSolver.h:54
bool ns_search
Ignore the trivial solution x=0 when rhs is zero.
Definition GMRESSolver.h:61
double abstol
Target absolute residual error.
Definition GMRESSolver.h:57