chombo-discharge
Loading...
Searching...
No Matches
CD_KrylovBiCGStabImplem.H
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2021-2026 SINTEF Energy Research
3 *
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 */
6
13#ifndef CD_KRYLOVBICGSTABIMPLEM_H
14#define CD_KRYLOVBICGSTABIMPLEM_H
15
16// Chombo includes
17#include <CH_Timer.H>
18#include <parstream.H>
19
20// Our includes
21#include <CD_KrylovBiCGStab.H>
22#include <CD_NamespaceHeader.H>
23
24template <class T>
26{
27 this->undefine();
28}
29
30template <class T>
31void
32KrylovBiCGStab<T>::define(AMRMultigridKrylovOp<T>* a_op, const Vector<LevelData<T>*>& a_template) noexcept
33{
34 CH_TIME("KrylovBiCGStab::define");
35
36 this->undefine();
37
38 m_op = a_op;
39
40 // Allocate the work vectors once; they are reused across all solves until the next define() (regrid).
41 m_op->create(m_r, a_template);
42 m_op->create(m_rTilde, a_template);
43 m_op->create(m_e, a_template);
44 m_op->create(m_p, a_template);
45 m_op->create(m_pTilde, a_template);
46 m_op->create(m_sTilde, a_template);
47 m_op->create(m_t, a_template);
48 m_op->create(m_v, a_template);
49
50 m_isDefined = true;
51}
52
53template <class T>
54void
56{
57 if (m_isDefined) {
58 m_op->clear(m_r);
59 m_op->clear(m_rTilde);
60 m_op->clear(m_e);
61 m_op->clear(m_p);
62 m_op->clear(m_pTilde);
63 m_op->clear(m_sTilde);
64 m_op->clear(m_t);
65 m_op->clear(m_v);
66
67 m_isDefined = false;
68 }
69}
70
71template <class T>
72bool
74{
75 return m_isDefined;
76}
77
78template <class T>
79bool
80KrylovBiCGStab<T>::solve(Vector<LevelData<T>*>& a_phi, const Vector<LevelData<T>*>& a_rhs) noexcept
81{
82 CH_TIME("KrylovBiCGStab::solve");
83
84 CH_assert(m_isDefined);
85
86 AMRMultigridKrylovOp<T>& op = *m_op;
87
88 m_exitStatus = -1;
89
90 // Initialize the (homogeneous) residual r = rhs - A*phi and the BiCGStab state. The correction is accumulated in
91 // m_e and added to a_phi at the very end, so warm starts (a_phi != 0) are honoured.
92 auto computeResidual = [&]() -> void {
93 op.applyOp(m_r, a_phi, true); // r = A*phi
94 op.axby(m_r, a_rhs, m_r, 1.0, -1.0); // r = rhs - A*phi
95 };
97 computeResidual();
98 op.assign(m_rTilde, m_r);
99 op.setToZero(m_e);
100 op.setToZero(m_p);
101 op.setToZero(m_v);
103 const Real rnorm0 = op.norm(m_r, 2);
104
105 if (rnorm0 == 0.0) {
106 m_exitStatus = 1;
107 m_residualNorm = 0.0;
108 m_iterations = 0;
109 return true;
110 }
111
112 Real rnorm = rnorm0;
113 Real rho = 1.0;
114 Real alpha = 1.0;
115 Real omega = 1.0;
116 int iter = 0;
117 int restarts = 0;
119 // Fold the work-so-far into a_phi and restart the iteration from the current solution. Used on breakdown.
120 auto restart = [&]() -> void {
121 op.incr(a_phi, m_e, 1.0);
122 op.setToZero(m_e);
123
124 computeResidual();
125
126 op.assign(m_rTilde, m_r);
127 op.setToZero(m_p);
128 op.setToZero(m_v);
129
130 rho = 1.0;
131 alpha = 1.0;
132 omega = 1.0;
133 rnorm = op.norm(m_r, 2);
134
135 restarts++;
136 };
137
138 // Treat a breakdown: restart if we still can, otherwise stop with status 2.
139 auto breakdown = [&]() -> bool {
140 if (restarts < m_numRestarts) {
141 restart();
142
143 return false; // continue
144 }
145
146 m_exitStatus = 2;
147
148 return true; // stop
149 };
150
151 while (iter < m_maxIter && rnorm > m_eps * rnorm0) {
152 const Real rnormStart = rnorm; // residual at the start of this iteration (for the convergence-rate report)
153
154 const Real rhoNew = op.dotProduct(m_rTilde, m_r);
155
156 if (rhoNew == 0.0) {
157 if (breakdown()) {
158 break;
159 }
160
161 continue;
162 }
163
164 // p = r + beta*(p - omega*v). With the initial/post-restart state (p = v = 0, rho = alpha = omega = 1) this
165 // correctly reduces to p = r, so no special first-iteration branch is needed.
166 const Real beta = (rhoNew / rho) * (alpha / omega);
167 op.incr(m_p, m_v, -omega);
168 op.scale(m_p, beta);
169 op.incr(m_p, m_r, 1.0);
170
171 op.preCond(m_pTilde, m_p); // p_tilde = K^{-1} p
172 op.applyOp(m_v, m_pTilde, true); // v = A p_tilde
173
174 const Real rtv = op.dotProduct(m_rTilde, m_v);
175 if (rtv == 0.0) {
176 if (breakdown()) {
177 break;
178 }
179
180 continue;
181 }
182 alpha = rhoNew / rtv;
183
184 op.incr(m_e, m_pTilde, alpha); // e += alpha*p_tilde
185 op.incr(m_r, m_v, -alpha); // r = s = r - alpha*v
186
187 rnorm = op.norm(m_r, 2);
188 if (rnorm <= m_eps * rnorm0) { // converged on the half-step s
189 m_exitStatus = 1;
190 iter++; // this (half) iteration was performed -- count it before breaking
191
192 break;
193 }
194
195 op.preCond(m_sTilde, m_r); // s_tilde = K^{-1} s
196 op.applyOp(m_t, m_sTilde, true); // t = A s_tilde
197
198 // omega = (t,s)/(t,t) -- both inner products in a single fused MPI reduction.
199 Real dots[2];
200 const Vector<LevelData<T>*> rhsPair[2] = {m_r, m_t};
201
202 op.mDotProduct(m_t, 2, rhsPair, dots);
203
204 const Real tDotT = dots[1];
205
206 if (tDotT == 0.0) {
207 if (breakdown()) {
208 break;
209 }
210
211 continue;
212 }
213
214 omega = dots[0] / tDotT;
215
216 op.incr(m_e, m_sTilde, omega); // e += omega*s_tilde
217 op.incr(m_r, m_t, -omega); // r = s - omega*t
218
219 rnorm = op.norm(m_r, 2);
220
221 rho = rhoNew;
222
223 if (m_verbosity >= 3) {
224 // Rate is the iteration-over-iteration reduction factor r_{k-1}/r_k (>1 = converging), matching AMRMultiGrid.
225 pout() << " KrylovBiCGStab:: iteration = " << iter << ", rel. residual = " << (rnorm / m_residualScale)
226 << ", rate = " << ((rnorm > 0.0) ? rnormStart / rnorm : 0.0) << endl;
227 }
228
229 if (rnorm <= m_eps * rnorm0) {
230 m_exitStatus = 1;
231 iter++; // this iteration was performed -- count it before breaking
232
233 break;
234 }
235
236 if (omega == 0.0) {
237 if (breakdown()) {
238 break;
239 }
240
241 continue;
242 }
243
244 iter++;
245 }
246
247 if (m_exitStatus == -1) {
248 m_exitStatus = (rnorm <= m_eps * rnorm0) ? 1 : 3; // converged on the loop guard, else max iterations
249 }
250
251 m_residualNorm = rnorm;
252 m_iterations = iter;
253
254 op.incr(a_phi, m_e, 1.0); // commit the accumulated correction
255
256 return (m_exitStatus == 1);
257}
258
259#include <CD_NamespaceFooter.H>
260
261#endif
Preconditioned BiCGStab over an AMR hierarchy, with persistent work vectors and fused reductions.
Adapter presenting an already-defined AMRMultiGrid as a LinearOp over the AMR hierarchy.
Definition CD_AMRMultigridKrylovOp.H:78
void incr(Vector< LevelData< T > * > &a_lhs, const Vector< LevelData< T > * > &a_x, Real a_scale) override
Increment a_lhs += a_scale*a_x.
Definition CD_AMRMultigridKrylovOpImplem.H:275
void assign(Vector< LevelData< T > * > &a_lhs, const Vector< LevelData< T > * > &a_rhs) override
Copy a_rhs into a_lhs.
Definition CD_AMRMultigridKrylovOpImplem.H:154
void preCond(Vector< LevelData< T > * > &a_cor, const Vector< LevelData< T > * > &a_residual) override
Apply the preconditioner: a_cor approx L^{-1} a_residual via m_numVCycles multigrid cycles (cycle typ...
Definition CD_AMRMultigridKrylovOpImplem.H:97
void mDotProduct(const Vector< LevelData< T > * > &a_1, const int a_sz, const Vector< LevelData< T > * > a_2[], Real a_mdots[]) override
Batched inner products a_mdots[k] = dotProduct(a_1, a_2[k]) for k=0..a_sz-1.
Definition CD_AMRMultigridKrylovOpImplem.H:214
void axby(Vector< LevelData< T > * > &a_lhs, const Vector< LevelData< T > * > &a_x, const Vector< LevelData< T > * > &a_y, Real a_a, Real a_b) override
Set a_lhs = a_a*a_x + a_b*a_y.
Definition CD_AMRMultigridKrylovOpImplem.H:284
void applyOp(Vector< LevelData< T > * > &a_lhs, const Vector< LevelData< T > * > &a_phi, bool a_homogeneous=false) override
Matrix-vector product L(phi) over the AMR hierarchy (always homogeneous for the Krylov path).
Definition CD_AMRMultigridKrylovOpImplem.H:74
Real norm(const Vector< LevelData< T > * > &a_rhs, int a_ord) override
Norm of a_rhs. For a_ord==2 this is the inner-product-induced norm sqrt(dotProduct(x,...
Definition CD_AMRMultigridKrylovOpImplem.H:306
Real dotProduct(const Vector< LevelData< T > * > &a_1, const Vector< LevelData< T > * > &a_2) override
AMR-composite inner product (allocation-free, fine-covered cells masked out).
Definition CD_AMRMultigridKrylovOpImplem.H:163
void setToZero(Vector< LevelData< T > * > &a_lhs) override
Set a_lhs = 0.
Definition CD_AMRMultigridKrylovOpImplem.H:322
void scale(Vector< LevelData< T > * > &a_lhs, const Real &a_scale) override
Scale a_lhs *= a_scale.
Definition CD_AMRMultigridKrylovOpImplem.H:297
bool isDefined() const noexcept
Whether define() has been called and the work vectors are allocated.
Definition CD_KrylovBiCGStabImplem.H:73
bool solve(Vector< LevelData< T > * > &a_phi, const Vector< LevelData< T > * > &a_rhs) noexcept
Solve a_op(a_phi) = a_rhs (homogeneous operator) with preconditioned BiCGStab.
Definition CD_KrylovBiCGStabImplem.H:80
void undefine() noexcept
Free the persistent work vectors.
Definition CD_KrylovBiCGStabImplem.H:55
void define(AMRMultigridKrylovOp< T > *a_op, const Vector< LevelData< T > * > &a_template) noexcept
Allocate the persistent work vectors. Call once per regrid.
Definition CD_KrylovBiCGStabImplem.H:32
~KrylovBiCGStab() noexcept
Destructor. Frees the persistent work vectors.
Definition CD_KrylovBiCGStabImplem.H:25