Arcane  4.2.3.0
Developer documentation
Loading...
Searching...
No Matches
ConjugateGradientSolver.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/* ConjugateGradientSolver.h (C) 2000-2026 */
9/* */
10/* Conjugate Gradient method. */
11/*---------------------------------------------------------------------------*/
12#ifndef ARCCORE_ALINA_CONJUGATEGRADIENTSOLVER_H
13#define ARCCORE_ALINA_CONJUGATEGRADIENTSOLVER_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 <iostream>
27
28#include "arccore/alina/SolverUtils.h"
29#include "arccore/alina/SolverBase.h"
30
31/*---------------------------------------------------------------------------*/
32/*---------------------------------------------------------------------------*/
33
34namespace Arcane::Alina
35{
36
37/*---------------------------------------------------------------------------*/
38/*---------------------------------------------------------------------------*/
42struct ConjugateGradientSolverParams
43{
44 using params = ConjugateGradientSolverParams;
45
48
50 double tol = 1.0e-8;
51
53 double abstol = std::numeric_limits<double>::min();
54
60 bool ns_search = false;
61
63 bool verbose = false;
64
65 ConjugateGradientSolverParams() = default;
66
67 ConjugateGradientSolverParams(const PropertyTree& p)
68 : ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, maxiter)
69 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, tol)
70 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, abstol)
71 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, ns_search)
72 , ARCCORE_ALINA_PARAMS_IMPORT_VALUE(p, verbose)
73 {
74 p.check_params({ "maxiter", "tol", "abstol", "ns_search", "verbose" });
75 }
76
77 void get(PropertyTree& p, const std::string& path) const
78 {
79 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, maxiter);
80 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, tol);
81 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, abstol);
82 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, ns_search);
83 ARCCORE_ALINA_PARAMS_EXPORT_VALUE(p, path, verbose);
84 }
85};
86
87/*---------------------------------------------------------------------------*/
88/*---------------------------------------------------------------------------*/
94template <class Backend_, class InnerProduct = detail::default_inner_product>
96: public SolverBase
97{
98 public:
99
100 using Backend = Backend_;
101 using backend_type = Backend;
102 using BackendType = Backend;
103
104 typedef typename Backend::vector vector;
105 typedef typename Backend::value_type value_type;
106 typedef typename Backend::params backend_params;
107
108 typedef typename math::scalar_of<value_type>::type scalar_type;
109
110 typedef typename math::inner_product_impl<
111 typename math::rhs_of<value_type>::type>::return_type coef_type;
112
113 using params = ConjugateGradientSolverParams;
114
117 size_t n,
118 const params& prm = params(),
119 const backend_params& backend_prm = backend_params(),
120 const InnerProduct& inner_product = InnerProduct())
121 : SolverBase(context)
122 , prm(prm)
123 , n(n)
124 , r(Backend::create_vector(n, backend_prm))
125 , s(Backend::create_vector(n, backend_prm))
126 , p(Backend::create_vector(n, backend_prm))
127 , q(Backend::create_vector(n, backend_prm))
128 , inner_product(inner_product)
129 {}
130
131 /* Computes the solution for the given system matrix \p A and the
132 * right-hand side \p rhs. Returns the number of iterations made and
133 * the achieved residual as a ``std::tuple``. The solution vector
134 * \p x provides initial approximation in input and holds the computed
135 * solution on output.
136 *
137 * The system matrix may differ from the matrix used during
138 * initialization. This may be used for the solution of non-stationary
139 * problems with slowly changing coefficients. There is a strong chance
140 * that a preconditioner built for a time step will act as a reasonably
141 * good preconditioner for several subsequent time steps [DeSh12]_.
142 */
143 template <class Matrix, class Precond, class Vec1, class Vec2>
144 SolverResult operator()(const Matrix& A, const Precond& P, const Vec1& rhs, Vec2&& x) const
145 {
146 static const coef_type one = math::identity<coef_type>();
147 static const coef_type zero = math::zero<coef_type>();
148
149 ScopedStreamModifier ss(std::cout);
150
151 scalar_type norm_rhs = norm(rhs);
152 if (norm_rhs < Alina::detail::eps<scalar_type>(1)) {
153 if (prm.ns_search) {
154 norm_rhs = math::identity<scalar_type>();
155 }
156 else {
157 backend::clear(x);
158 return SolverResult(0, norm_rhs);
159 }
160 }
161
162 scalar_type eps = std::max(prm.tol * norm_rhs, prm.abstol);
163
164 coef_type rho1 = 2 * eps * one;
165 coef_type rho2 = zero;
166
167 backend::residual(rhs, A, x, *r);
168 scalar_type res_norm = norm(*r);
169
170 Int32 iter = 0;
171 for (; iter < prm.maxiter && math::norm(res_norm) > eps; ++iter) {
172 P.apply(*r, *s);
173
174 rho2 = rho1;
175 rho1 = inner_product(*r, *s);
176
177 if (iter != 0)
178 backend::axpby(one, *s, rho1 / rho2, *p);
179 else
180 backend::copy(*s, *p);
181
182 backend::spmv(one, A, *p, zero, *q);
183
184 coef_type alpha = rho1 / inner_product(*q, *p);
185
186 backend::axpby(alpha, *p, one, x);
187 backend::axpby(-alpha, *q, one, *r);
188
189 res_norm = norm(*r);
190 if (prm.verbose)
191 std::cout << iter << "\t" << std::scientific << res_norm / norm_rhs << std::endl;
192 }
193
194 return SolverResult(iter, res_norm / norm_rhs);
195 }
196
207 template <class Precond, class Vec1, class Vec2>
208 SolverResult operator()(const Precond& P, const Vec1& rhs, Vec2&& x) const
209 {
210 return (*this)(P.system_matrix(), P, rhs, x);
211 }
212
213 size_t bytes() const
214 {
215 return backend::bytes(*r) +
216 backend::bytes(*s) +
217 backend::bytes(*p) +
218 backend::bytes(*q);
219 }
220
221 friend std::ostream& operator<<(std::ostream& os, const ConjugateGradientSolver& s)
222 {
223 return os << "Type: CG"
224 << "\nUnknowns: " << s.n
225 << "\nMemory footprint: " << human_readable_memory(s.bytes())
226 << "\nparams: maxiter " << s.prm.maxiter
227 << "\nparams: tol " << s.prm.tol
228 << "\nparams: abstol " << s.prm.abstol
229 << "\nparams: ns_search " << s.prm.ns_search
230 << "\nparams: verbose " << s.prm.verbose
231 << std::endl;
232 }
233
234 public:
235
236 params prm;
237
238 private:
239
240 size_t n;
241
242 std::shared_ptr<vector> r;
243 std::shared_ptr<vector> s;
244 std::shared_ptr<vector> p;
245 std::shared_ptr<vector> q;
246
247 InnerProduct inner_product;
248
249 template <class Vec>
250 scalar_type norm(const Vec& x) const
251 {
252 return sqrt(math::norm(inner_product(x, x)));
253 }
254};
255
256/*---------------------------------------------------------------------------*/
257/*---------------------------------------------------------------------------*/
258
259} // namespace Arcane::Alina
260
261/*---------------------------------------------------------------------------*/
262/*---------------------------------------------------------------------------*/
263
264#endif
size_t bytes() const
Memory used in bytes.
SolverResult operator()(const Precond &P, const Vec1 &rhs, Vec2 &&x) const
Computes the solution for the given right-hand side.
ConjugateGradientSolver(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.
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 Conjugate Gradient solver.
bool ns_search
Ignore the trivial solution x=0 when rhs is zero.
bool verbose
Verbose output (show iterations and error).
double tol
Target relative residual error.
double abstol
Target absolute residual error.
Default implementation for inner product.