Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
BiCGStabSolver.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/* BiCGStabSolver.h (C) 2000-2026 */
9/* */
10/* BiCGStab iterative method. . */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_BICGSTAB_H
13#define ARCCORE_ALINA_BICGSTAB_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 BiCGStabSolverParams
41{
42 using params = BiCGStabSolverParams;
43
45 ePreconditionerSideType pside = ePreconditionerSideType::right;
46
49
51 double tol = 1.0e-8;
52
54 double abstol = std::numeric_limits<double>::min();
55
57 bool check_after = false;
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 BiCGStabSolverParams() = default;
67
68 BiCGStabSolverParams(const PropertyTree& p)
69 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, pside)
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, check_after)
74 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, ns_search)
75 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, verbose)
76 {
77 p.check_params( { "pside", "maxiter", "tol", "abstol", "check_after", "ns_search", "verbose" });
78 }
79
80 void get(PropertyTree& p, const std::string& path) const
81 {
82 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, pside);
83 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, maxiter);
84 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, tol);
85 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, abstol);
86 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, check_after);
87 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, ns_search);
88 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, verbose);
89 }
90};
91
92/*---------------------------------------------------------------------------*/
93/*---------------------------------------------------------------------------*/
101template <class Backend, class InnerProduct = detail::default_inner_product>
103: public SolverBase
104{
105 public:
106
107 using backend_type = Backend;
108 using BackendType = Backend;
109
110 typedef typename Backend::vector vector;
111 typedef typename Backend::value_type value_type;
112 typedef typename Backend::params backend_params;
113
114 typedef typename math::scalar_of<value_type>::type scalar_type;
115
116 typedef typename math::inner_product_impl<
117 typename math::rhs_of<value_type>::type>::return_type coef_type;
118
119 using params = BiCGStabSolverParams;
120
123 size_t n,
124 const params& prm = params(),
125 const backend_params& backend_prm = backend_params(),
126 const InnerProduct& inner_product = InnerProduct())
127 : SolverBase(context)
128 , prm(prm)
129 , n(n)
130 , r(Backend::create_vector(n, backend_prm))
131 , p(Backend::create_vector(n, backend_prm))
132 , v(Backend::create_vector(n, backend_prm))
133 , s(Backend::create_vector(n, backend_prm))
134 , t(Backend::create_vector(n, backend_prm))
135 , rh(Backend::create_vector(n, backend_prm))
136 , T(Backend::create_vector(n, backend_prm))
137 , inner_product(inner_product)
138 {}
139
140 /* Computes the solution for the given system matrix \p A and the
141 * right-hand side \p rhs. Returns the number of iterations made and
142 * the achieved residual as a ``std::tuple``. The solution vector
143 * \p x provides initial approximation in input and holds the computed
144 * solution on output.
145 *
146 * The system matrix may differ from the matrix used during
147 * initialization. This may be used for the solution of non-stationary
148 * problems with slowly changing coefficients. There is a strong chance
149 * that a preconditioner built for a time step will act as a reasonably
150 * good preconditioner for several subsequent time steps [DeSh12]_.
151 */
152 template <class Matrix, class Precond, class Vec1, class Vec2>
153 SolverResult operator()(const Matrix& A, const Precond& P, const Vec1& rhs, Vec2&& x) const
154 {
155 static const coef_type one = math::identity<coef_type>();
156 static const coef_type zero = math::zero<coef_type>();
157
158 ScopedStreamModifier ss(std::cout);
159
160 scalar_type norm_rhs = norm(rhs);
161 if (prm.verbose)
162 std::cout << "BiCGStab solver rhs_norm=" << std::scientific << norm_rhs << "\n";
163
164 if (norm_rhs < Alina::detail::eps<scalar_type>(1)) {
165 if (prm.ns_search) {
166 norm_rhs = math::identity<scalar_type>();
167 }
168 else {
169 backend::clear(x);
170 return SolverResult(0, norm_rhs);
171 }
172 }
173
174 if (prm.pside == ePreconditionerSideType::left) {
175 backend::residual(rhs, A, x, *rh);
176 P.apply(*rh, *r);
177 }
178 else {
179 backend::residual(rhs, A, x, *r);
180 }
181 backend::copy(*r, *rh);
182
183 scalar_type eps = std::max(norm_rhs * prm.tol, prm.abstol);
184 scalar_type res = prm.check_after ? 2 * eps : norm(*r);
185
186 if (prm.verbose)
187 std::cout << "BiCGStab eps=" << std::scientific << eps << " res=" << res << "\n";
188
189 coef_type rho1 = zero;
190 coef_type rho2 = zero;
191 coef_type alpha = zero;
192 coef_type omega = zero;
193
194 Int32 iter = 0;
195 for (bool first = true; res > eps && iter < prm.maxiter; ++iter) {
196
197 rho2 = rho1;
198 rho1 = inner_product(*r, *rh);
199
200 if (first) {
201 backend::copy(*r, *p);
202 first = false;
203 }
204 else {
205 precondition(!math::is_zero(rho2), "Zero rho in BiCGStab");
206 coef_type beta = (rho1 * alpha) / (rho2 * omega);
207 backend::axpbypcz(one, *r, -beta * omega, *v, beta, *p);
208 }
209
210 preconditioner_spmv(prm.pside, P, A, *p, *v, *T);
211
212 alpha = rho1 / inner_product(*rh, *v);
213
214 if (prm.pside == ePreconditionerSideType::left) {
215 backend::axpby(alpha, *p, one, x);
216 }
217 else {
218 backend::axpby(alpha, *T, one, x);
219 }
220
221 backend::axpbypcz(one, *r, -alpha, *v, zero, *s);
222
223 if ((res = norm(*s)) > eps) {
224 preconditioner_spmv(prm.pside, P, A, *s, *t, *T);
225
226 omega = inner_product(*t, *s) / inner_product(*t, *t);
227
228 precondition(!math::is_zero(omega), "Zero omega in BiCGStab");
229
230 if (prm.pside == ePreconditionerSideType::left) {
231 backend::axpby(omega, *s, one, x);
232 }
233 else {
234 backend::axpby(omega, *T, one, x);
235 }
236
237 backend::axpbypcz(one, *s, -omega, *t, zero, *r);
238
239 res = norm(*r);
240 }
241
242 if (prm.verbose)
243 std::cout << iter << " res/norm_rhs=" << std::scientific << res / norm_rhs << " res=" << res << "\n";
244 }
245
246 return SolverResult(iter, res / norm_rhs);
247 }
248
249 /*
250 * Computes the solution for the given right-hand side \p rhs. The
251 * system matrix is the same that was used for the setup of the
252 * preconditioner \p P. Returns the number of iterations made and the
253 * achieved residual as a ``std::tuple``. The solution vector \p x
254 * provides initial approximation in input and holds the computed
255 * solution on output.
256 */
257 template <class Precond, class Vec1, class Vec2>
258 SolverResult operator()(const Precond& P, const Vec1& rhs, Vec2&& x) const
259 {
260 return (*this)(P.system_matrix(), P, rhs, x);
261 }
262
263 size_t bytes() const
264 {
265 return backend::bytes(*r) +
266 backend::bytes(*p) +
267 backend::bytes(*v) +
268 backend::bytes(*s) +
269 backend::bytes(*t) +
270 backend::bytes(*rh) +
271 backend::bytes(*T);
272 }
273
274 friend std::ostream& operator<<(std::ostream& os, const BiCGStabSolver& s)
275 {
276 return os << "Type: BiCGStab"
277 << "\nUnknowns: " << s.n
278 << "\nMemory footprint: " << human_readable_memory(s.bytes())
279 << std::endl;
280 }
281
282 public:
283
284 params prm;
285
286 private:
287
288 size_t n;
289
290 std::shared_ptr<vector> r;
291 std::shared_ptr<vector> p;
292 std::shared_ptr<vector> v;
293 std::shared_ptr<vector> s;
294 std::shared_ptr<vector> t;
295 std::shared_ptr<vector> rh;
296 std::shared_ptr<vector> T;
297
298 InnerProduct inner_product;
299
300 template <class Vec>
301 scalar_type norm(const Vec& x) const
302 {
303 return sqrt(math::norm(inner_product(x, x)));
304 }
305};
306
307/*---------------------------------------------------------------------------*/
308/*---------------------------------------------------------------------------*/
309
310} // namespace Arcane::Alina
311
312/*---------------------------------------------------------------------------*/
313/*---------------------------------------------------------------------------*/
314
315#endif
BiConjugate Gradient Stabilized (BiCGSTAB) method.
BiCGStabSolver(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.
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 BiConjugate Gradient Stabilized solver.
bool check_after
Always do at least one iteration.
bool verbose
Verbose output (show iterations and error).
double abstol
Target absolute residual error.
Int32 maxiter
Maximum number of iterations.
double tol
Target relative residual error.
ePreconditionerSideType pside
Preconditioning kind (left/right).
bool ns_search
Ignore the trivial solution x=0 when rhs is zero.
Default implementation for inner product.