Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
FlexibleGMRESSolver.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_fgmres.h (C) 2000-2026 */
9/* */
10/* Flexible GMRES method solver. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_FGMRESSOLVER_H
13#define ARCCORE_ALINA_FGMRESSOLVER_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 FlexibleGMRESSolverParams
41{
42 using params = FlexibleGMRESSolverParams;
43
45 Int32 M = 30;
46
49
51 double tol = 1.0e-8;
52
54 double abstol = std::numeric_limits<double>::min();
55
61 bool ns_search = false;
62
64 bool verbose = false;
65
66 FlexibleGMRESSolverParams() = default;
67
68 FlexibleGMRESSolverParams(const PropertyTree& p)
69 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, M)
70 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, maxiter)
71 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, tol)
72 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, abstol)
73 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, ns_search)
74 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, verbose)
75 {
76 p.check_params({ "M", "maxiter", "tol", "abstol", "ns_search", "verbose" });
77 }
78
79 void get(PropertyTree& p, const std::string& path) const
80 {
81 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, M);
82 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, maxiter);
83 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, tol);
84 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, abstol);
85 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, ns_search);
86 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, verbose);
87 }
88};
89
90/*---------------------------------------------------------------------------*/
91/*---------------------------------------------------------------------------*/
98template <class Backend, class InnerProduct = detail::default_inner_product>
100: public SolverBase
101{
102 public:
103
104 typedef Backend backend_type;
105
106 typedef typename Backend::vector vector;
107 typedef typename Backend::value_type value_type;
108 typedef typename Backend::params backend_params;
109
110 typedef typename math::scalar_of<value_type>::type scalar_type;
111 typedef typename math::rhs_of<value_type>::type rhs_type;
112 typedef typename math::inner_product_impl<rhs_type>::return_type coef_type;
113
114 using params = FlexibleGMRESSolverParams;
115
117 FlexibleGMRESSolver(const AlinaContext& context, size_t n,
118 const params& prm = params(),
119 const backend_params& bprm = backend_params(),
120 const InnerProduct& inner_product = InnerProduct())
121 : SolverBase(context)
122 , prm(prm)
123 , n(n)
124 , H(prm.M + 1, prm.M)
125 , s(prm.M + 1)
126 , cs(prm.M + 1)
127 , sn(prm.M + 1)
128 , r(Backend::create_vector(n, bprm))
129 , inner_product(inner_product)
130 {
131 v.reserve(prm.M + 1);
132 for (unsigned i = 0; i <= prm.M; ++i)
133 v.push_back(Backend::create_vector(n, bprm));
134
135 z.reserve(prm.M);
136 for (unsigned i = 0; i < prm.M; ++i)
137 z.push_back(Backend::create_vector(n, bprm));
138 }
139
155 template <class Matrix, class Precond, class Vec1, class Vec2>
156 SolverResult operator()(Matrix const& A, Precond const& P, Vec1 const& rhs, Vec2& x) const
157 {
158 ScopedStreamModifier ss(std::cout);
159
160 scalar_type norm_rhs = norm(rhs);
161 if (norm_rhs < Alina::detail::eps<scalar_type>(1)) {
162 if (prm.ns_search) {
163 norm_rhs = math::identity<scalar_type>();
164 }
165 else {
166 backend::clear(x);
167 return SolverResult(0, norm_rhs);
168 }
169 }
170
171 scalar_type eps = std::max(prm.tol * norm_rhs, prm.abstol);
172 scalar_type norm_r = math::zero<scalar_type>();
173
174 unsigned iter = 0;
175 while (true) {
176 backend::residual(rhs, A, x, *v[0]);
177
178 // -- Check stopping condition
179 if ((norm_r = norm(*v[0])) < eps || iter >= prm.maxiter)
180 break;
181
182 // -- Inner GMRES iteration
183 std::fill(s.begin(), s.end(), 0);
184 s[0] = norm_r;
185
186 backend::axpby(math::inverse(norm_r), *v[0], math::zero<scalar_type>(), *v[0]);
187
188 unsigned j = 0;
189 while (true) {
190 // -- Arnoldi process
191 //
192 // Build an orthonormal basis V and matrix H such that
193 // A V_{i-1} = V_{i} H
194
195 vector& v_new = *v[j + 1];
196
197 P.apply(*v[j], *z[j]);
198 backend::spmv(math::identity<scalar_type>(), A, *z[j],
199 math::zero<scalar_type>(), v_new);
200
201 for (unsigned k = 0; k <= j; ++k) {
202 H(k, j) = inner_product(v_new, *v[k]);
203 backend::axpby(-H(k, j), *v[k], math::identity<scalar_type>(), v_new);
204 }
205 H(j + 1, j) = norm(v_new);
206
207 backend::axpby(math::inverse(H(j + 1, j)), v_new, math::zero<scalar_type>(), v_new);
208
209 for (unsigned k = 0; k < j; ++k)
210 detail::apply_plane_rotation(H(k, j), H(k + 1, j), cs[k], sn[k]);
211
212 detail::generate_plane_rotation(H(j, j), H(j + 1, j), cs[j], sn[j]);
213 detail::apply_plane_rotation(H(j, j), H(j + 1, j), cs[j], sn[j]);
214 detail::apply_plane_rotation(s[j], s[j + 1], cs[j], sn[j]);
215
216 scalar_type inner_res = std::abs(s[j + 1]);
217
218 if (prm.verbose && iter % 5 == 0)
219 std::cout << iter << "\t" << std::scientific << inner_res / norm_rhs << std::endl;
220
221 // Check for termination
222 ++j, ++iter;
223 if (iter >= prm.maxiter || j >= prm.M || inner_res <= eps)
224 break;
225 }
226
227 // -- GMRES terminated: eval solution
228 for (unsigned i = j; i-- > 0;) {
229 s[i] /= H(i, i);
230 for (unsigned k = 0; k < i; ++k)
231 s[k] -= H(k, i) * s[i];
232 }
233
234 backend::lin_comb(j, s, z, math::identity<scalar_type>(), x);
235 }
236
237 return SolverResult(iter, norm_r / norm_rhs);
238 }
239
250 template <class Precond, class Vec1, class Vec2>
251 SolverResult operator()(Precond const& P, Vec1 const& rhs, Vec2& x) const
252 {
253 return (*this)(P.system_matrix(), P, rhs, x);
254 }
255
256 size_t bytes() const
257 {
258 size_t b = 0;
259
260 b += H.size() * sizeof(coef_type);
261 b += backend::bytes(s);
262 b += backend::bytes(cs);
263 b += backend::bytes(sn);
264 b += backend::bytes(*r);
265
266 for (const auto& x : v)
267 b += backend::bytes(*x);
268 for (const auto& x : z)
269 b += backend::bytes(*x);
270
271 return b;
272 }
273
274 friend std::ostream& operator<<(std::ostream& os, const FlexibleGMRESSolver& s)
275 {
276 return os << "Type: FGMRES(" << s.prm.M << ")"
277 << "\nUnknowns: " << s.n
278 << "\nMemory footprint: " << human_readable_memory(s.bytes())
279 << "\n";
280 }
281
282 public:
283
284 params prm;
285
286 private:
287
288 size_t n;
289
290 mutable multi_array<coef_type, 2> H;
291 mutable std::vector<coef_type> s, cs, sn;
292 std::shared_ptr<vector> r;
293 std::vector<std::shared_ptr<vector>> v;
294 std::vector<std::shared_ptr<vector>> z;
295
296 InnerProduct inner_product;
297
298 template <class Vec>
299 scalar_type norm(const Vec& x) const
300 {
301 return std::abs(sqrt(inner_product(x, x)));
302 }
303};
304
305/*---------------------------------------------------------------------------*/
306/*---------------------------------------------------------------------------*/
307
308} // namespace Arcane::Alina
309
310/*---------------------------------------------------------------------------*/
311/*---------------------------------------------------------------------------*/
312
313#endif
Flexible GMRES method. \rst Flexible version of the GMRES method [Saad03]_. \endrst.
FlexibleGMRESSolver(const AlinaContext &context, size_t n, const params &prm=params(), const backend_params &bprm=backend_params(), const InnerProduct &inner_product=InnerProduct())
Preallocates necessary data structures for the system of size n.
SolverResult operator()(Matrix const &A, Precond const &P, Vec1 const &rhs, Vec2 &x) const
Computes the solution for the given system matrix.
size_t bytes() const
Memory used in bytes.
SolverResult operator()(Precond const &P, Vec1 const &rhs, Vec2 &x) const
Computes the solution for the given right-hand side.
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 FlexibleGMRES solver.
bool ns_search
Ignore the trivial solution x=0 when rhs is zero.
double tol
Target relative residual error.
bool verbose
Verbose output (show iterations and error).
Int32 maxiter
Maximum number of iterations.
Int32 M
Number of inner GMRES iterations per each outer iteration.
double abstol
Target absolute residual error.