Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_PCG.H
Go to the documentation of this file.
1#ifndef AMREX_PCG_H_
2#define AMREX_PCG_H_
3#include <AMReX_Config.H>
4
5#include <AMReX_BLProfiler.H>
6#include <AMReX_Math.H>
7#include <AMReX_Print.H>
8
9#include <cmath>
10#include <iomanip>
11#include <limits>
12
13namespace amrex {
14
23template <typename V, typename M>
24class PCG
25{
26public:
27
28 using RT = typename M::RT; // double or float
29
30 PCG () = default;
31
35 void define (M& linop) { m_linop = &linop; }
36
50 void solve (V& a_sol, V const& a_rhs, RT a_tol_rel, RT a_tol_abs, int a_its=-1);
51
54 void setInitialGuessNonzero (bool flag) { m_nonzero_guess = flag; }
55
57 void setVerbose (int v) { m_verbose = v; }
58
60 void setMaxIters (int niters) { m_maxiter = niters; }
61
63 [[nodiscard]] int getNumIters () const { return m_its; }
64
74 [[nodiscard]] int getStatus () const { return m_status; }
75
77 [[nodiscard]] RT getResidualNorm () const { return m_res; }
78
79private:
80
81 int m_verbose = 0;
82 bool m_nonzero_guess = false;
83 int m_maxiter = 2000;
84 int m_its = 0;
85 int m_status = -1;
86 RT m_res = std::numeric_limits<RT>::max();
87 RT m_rtol = RT(0);
88 RT m_atol = RT(0);
89
90 M* m_linop = nullptr;
91};
92
93template <typename V, typename M>
94void PCG<V,M>::solve (V& a_sol, V const& a_rhs, RT a_tol_rel, RT a_tol_abs, int a_its)
95{
96 BL_PROFILE("PCG::solve()");
97
98 AMREX_ALWAYS_ASSERT(m_linop != nullptr);
99
100 m_rtol = a_tol_rel;
101 m_atol = a_tol_abs;
102 if (a_its < 0) { a_its = m_maxiter; }
103
104 m_its = 0;
105 m_status = -1;
106
107 V r = m_linop->makeVecRHS();
108 V z = m_linop->makeVecLHS();
109 V p = m_linop->makeVecLHS();
110 V q = m_linop->makeVecRHS();
111
112 if (m_nonzero_guess) {
113 // r = b - A x. apply() needs an x made by makeVecLHS.
114 m_linop->assign(p, a_sol);
115 m_linop->apply(q, p);
116 m_linop->linComb(r, RT(1), a_rhs, RT(-1), q);
117 } else {
118 m_linop->setToZero(a_sol);
119 m_linop->assign(r, a_rhs);
120 }
121
122 RT rnorm = m_linop->norm2(r);
123 RT const rnorm0 = rnorm;
124 m_res = rnorm;
125
126 if (m_verbose > 0) {
127 amrex::Print() << "PCG: Initial residual (error0) = " << rnorm0 << '\n';
128 }
129
130 if (rnorm0 == RT(0) || rnorm0 < m_atol) {
131 m_status = 0;
132 return;
133 }
134
135 auto converged = [&] (RT rn) {
136 return rn == RT(0) || rn < m_rtol*rnorm0 || rn < m_atol;
137 };
138
139 m_linop->precond(z, r);
140 m_linop->assign(p, z);
141 RT rz = m_linop->dotProduct(r, z);
142 // A and the preconditioner may both be negative definite: every r.z and
143 // p.Ap must then keep the sign of the first r.z.
144 RT const sgn = (rz < RT(0)) ? RT(-1) : RT(1);
145 if (rz == RT(0) || !amrex::isfinite(rz)) { m_status = 3; }
146
147 for (int iter = 1; iter <= a_its && m_status == -1; ++iter)
148 {
149 m_linop->apply(q, p);
150 RT const pq = m_linop->dotProduct(p, q);
151 if (sgn*pq <= RT(0) || !amrex::isfinite(pq)) { m_status = 2; break; }
152 RT const alpha = rz/pq;
153
154 m_linop->increment(a_sol, p, alpha);
155 m_linop->increment(r, q, -alpha);
156
157 rnorm = m_linop->norm2(r);
158 m_its = iter;
159 m_res = rnorm;
160
161 if (m_verbose > 1) {
162 amrex::Print() << "PCG: Iteration " << std::setw(11) << iter
163 << " rel. err. " << rnorm/rnorm0 << '\n';
164 }
165
166 if (converged(rnorm)) { m_status = 0; break; }
167 if (!amrex::isfinite(rnorm)) { m_status = 3; break; }
168 if (iter == a_its) { break; }
169
170 m_linop->precond(z, r);
171 RT const rz_new = m_linop->dotProduct(r, z);
172 if (sgn*rz_new <= RT(0) || !amrex::isfinite(rz_new)) { m_status = 3; break; }
173 RT const beta = rz_new/rz;
174 rz = rz_new;
175
176 m_linop->scale(p, beta);
177 m_linop->increment(p, z, RT(1)); // p = z + beta*p
178 }
179
180 if (m_status == -1) { m_status = 1; }
181
182 if (m_verbose > 0) {
183 amrex::Print() << "PCG: Final: Iteration " << std::setw(4) << m_its
184 << " rel. err. " << rnorm/rnorm0 << '\n';
185 }
186
187 if (m_status != 0 && m_verbose > 0) {
188 amrex::Print() << "PCG: Failed to converge, status = " << m_status << '\n';
189 }
190}
191
192}
193
194#endif
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:562
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
GpuArray< Real, 3 > beta
Definition AMReX_MLEBNodeFDLaplacian.cpp:1834
Preconditioned conjugate gradient solver.
Definition AMReX_PCG.H:25
void setVerbose(int v)
Sets verbosity.
Definition AMReX_PCG.H:57
void setMaxIters(int niters)
Sets the max number of iterations.
Definition AMReX_PCG.H:60
typename M::RT RT
Definition AMReX_PCG.H:28
void define(M &linop)
Definition AMReX_PCG.H:35
RT getResidualNorm() const
Gets the 2-norm of the residual.
Definition AMReX_PCG.H:77
void solve(V &a_sol, V const &a_rhs, RT a_tol_rel, RT a_tol_abs, int a_its=-1)
Solve the linear system.
Definition AMReX_PCG.H:94
int getStatus() const
Gets the solver status.
Definition AMReX_PCG.H:74
void setInitialGuessNonzero(bool flag)
Definition AMReX_PCG.H:54
int getNumIters() const
Gets the number of iterations.
Definition AMReX_PCG.H:63
PCG()=default
This class provides the user with a few print options.
Definition AMReX_Print.H:35
Definition AMReX_Amr.cpp:50