chombo-discharge
Loading...
Searching...
No Matches
CD_KrylovGMRESImplem.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_KRYLOVGMRESIMPLEM_H
14#define CD_KRYLOVGMRESIMPLEM_H
15
16// Std includes
17#include <algorithm>
18#include <cmath>
19
20// Chombo includes
21#include <CH_Timer.H>
22#include <parstream.H>
23
24// Our includes
25#include <CD_KrylovGMRES.H>
26#include <CD_NamespaceHeader.H>
27
28template <class T>
30{
31 this->undefine();
32}
33
34template <class T>
35void
36KrylovGMRES<T>::define(AMRMultigridKrylovOp<T>* a_op, const Vector<LevelData<T>*>& a_template) noexcept
37{
38 CH_TIME("KrylovGMRES::define");
39
40 this->undefine();
41
42 m_op = a_op;
43 m_allocRestart = std::max(1, m_restart);
44
45 m_basis.resize(m_allocRestart + 1);
46 for (int i = 0; i < m_basis.size(); i++) {
47 m_op->create(m_basis[i], a_template);
48 }
49 m_op->create(m_w, a_template);
50 m_op->create(m_z, a_template);
51 m_op->create(m_c, a_template);
52 m_op->create(m_r, a_template);
53
54 m_H = Vector<Real>((m_allocRestart + 1) * m_allocRestart, 0.0);
55 m_cs = Vector<Real>(m_allocRestart, 0.0);
56 m_sn = Vector<Real>(m_allocRestart, 0.0);
57 m_g = Vector<Real>(m_allocRestart + 1, 0.0);
58 m_y = Vector<Real>(m_allocRestart, 0.0);
59 m_hcol = Vector<Real>(m_allocRestart, 0.0);
60 m_corr = Vector<Real>(m_allocRestart, 0.0);
61
62 m_isDefined = true;
63}
64
65template <class T>
66void
68{
69 if (m_isDefined) {
70 for (int i = 0; i < m_basis.size(); i++) {
71 m_op->clear(m_basis[i]);
72 }
73
74 m_basis.clear();
75
76 m_op->clear(m_w);
77 m_op->clear(m_z);
78 m_op->clear(m_c);
79 m_op->clear(m_r);
80
81 m_isDefined = false;
82 }
83}
84
85template <class T>
86bool
88{
89 return m_isDefined;
90}
91
92template <class T>
93bool
94KrylovGMRES<T>::solve(Vector<LevelData<T>*>& a_phi, const Vector<LevelData<T>*>& a_rhs) noexcept
95{
96 CH_TIME("KrylovGMRES::solve");
97
98 CH_assert(m_isDefined);
99
101
102 m_exitStatus = -1;
103
104 const int m = m_allocRestart; // restart length
105 const int ldH = m + 1; // Hessenberg leading dimension
106
107 const auto Hidx = [ldH](const int r, const int c) -> int {
108 return r + c * ldH;
109 };
110
111 // True (right-preconditioning preserves it) initial residual r = rhs - A*phi.
112 op.applyOp(m_r, a_phi, true);
113 op.axby(m_r, a_rhs, m_r, 1.0, -1.0);
115 const Real beta0 = op.norm(m_r, 2);
116 if (beta0 == 0.0) {
117 m_exitStatus = 1;
118 m_residualNorm = 0.0;
119 m_iterations = 0;
120 return true;
122
123 // Tolerance scale. m_residualScale is the host's zero-residual ||rhs - L(0)||; falling back to beta0 when the
124 // host did not supply one reproduces a plain reduction relative to this solve's own initial residual.
125 const Real scale = (m_residualScale > 0.0) ? m_residualScale : beta0;
126 const Real epsScaled = m_eps * scale;
127
128 Real rnorm = beta0;
129 int iter = 0;
131 while (iter < m_maxIter && rnorm > epsScaled) {
132
133 // Start a restart cycle: v_0 = r / ||r||.
134 const Real beta = op.norm(m_r, 2);
135 op.assign(m_basis[0], m_r);
136 op.scale(m_basis[0], 1.0 / beta);
137
138 for (int i = 0; i <= m; i++) {
139 m_g[i] = 0.0;
140 }
141 m_g[0] = beta;
142
143 int jUsed = 0;
144
145 for (int j = 0; j < m && iter < m_maxIter; j++) {
146 iter++;
147
148 // Arnoldi step on A*K^{-1}: w = A (K^{-1} v_j). The preconditioned vector is not stored; the solution update
149 // is preconditioned once per restart cycle below.
150 op.preCond(m_z, m_basis[j]);
151 op.applyOp(m_w, m_z, true);
152
153 // Classical Gram-Schmidt against v_0..v_j -- one fused reduction (mDotProduct) instead of j+1 dot products.
154 // m_hcol is persistent scratch; only entries 0..j are written and read this step.
155 op.mDotProduct(m_w, j + 1, &m_basis[0], &m_hcol[0]);
156
157 for (int i = 0; i <= j; i++) {
158 op.incr(m_w, m_basis[i], -m_hcol[i]);
159 m_H[Hidx(i, j)] = m_hcol[i];
160 }
161
162 if (m_reorthogonalize) {
163 op.mDotProduct(m_w, j + 1, &m_basis[0], &m_corr[0]);
164
165 for (int i = 0; i <= j; i++) {
166 op.incr(m_w, m_basis[i], -m_corr[i]);
167 m_H[Hidx(i, j)] += m_corr[i];
168 }
169 }
170
171 const Real hjp1 = op.norm(m_w, 2);
172 m_H[Hidx(j + 1, j)] = hjp1;
173 if (hjp1 != 0.0) {
174 op.assign(m_basis[j + 1], m_w);
175 op.scale(m_basis[j + 1], 1.0 / hjp1);
176 }
177
178 // Apply existing Givens rotations to the new Hessenberg column.
179 for (int k = 0; k < j; k++) {
180 const Real t = m_cs[k] * m_H[Hidx(k, j)] + m_sn[k] * m_H[Hidx(k + 1, j)];
181 m_H[Hidx(k + 1, j)] = -m_sn[k] * m_H[Hidx(k, j)] + m_cs[k] * m_H[Hidx(k + 1, j)];
182 m_H[Hidx(k, j)] = t;
183 }
184
185 // New Givens rotation eliminating H[j+1][j].
186 const Real hjj = m_H[Hidx(j, j)];
187 const Real hj1j = m_H[Hidx(j + 1, j)];
188 const Real denom = std::sqrt(hjj * hjj + hj1j * hj1j);
189
190 if (denom == 0.0) {
191 break;
192 }
193
194 m_cs[j] = hjj / denom;
195 m_sn[j] = hj1j / denom;
196 m_H[Hidx(j, j)] = denom;
197 m_H[Hidx(j + 1, j)] = 0.0;
198
199 const Real gtemp = m_cs[j] * m_g[j];
200 m_g[j + 1] = -m_sn[j] * m_g[j];
201 m_g[j] = gtemp;
202
203 rnorm = std::abs(m_g[j + 1]);
204 jUsed = j + 1;
205
206 if (m_verbosity >= 3) {
207 pout() << " KrylovGMRES:: iteration = " << iter << ", rel. residual = " << (rnorm / scale) << endl;
208 }
209
210 if (rnorm <= epsScaled || hjp1 == 0.0) {
211 break;
212 }
213 }
214
215 // Back-substitute the (jUsed x jUsed) upper-triangular system H y = g.
216 for (int i = jUsed - 1; i >= 0; i--) {
217 Real s = m_g[i];
218
219 for (int k = i + 1; k < jUsed; k++) {
220 s -= m_H[Hidx(i, k)] * m_y[k];
221 }
222
223 m_y[i] = (m_H[Hidx(i, i)] != 0.0) ? s / m_H[Hidx(i, i)] : 0.0;
224 }
225
226 // Form the unpreconditioned update c = sum_k y_k v_k, precondition once, and apply: phi += K^{-1} c.
227 op.setToZero(m_c);
228 for (int k = 0; k < jUsed; k++) {
229 op.incr(m_c, m_basis[k], m_y[k]);
230 }
231
232 op.preCond(m_z, m_c);
233 op.incr(a_phi, m_z, 1.0);
234
235 // Recompute the true residual for the restart/convergence decision.
236 op.applyOp(m_r, a_phi, true);
237 op.axby(m_r, a_rhs, m_r, 1.0, -1.0);
238 rnorm = op.norm(m_r, 2);
239
240 if (jUsed == 0) { // no progress (degenerate) -- avoid spinning
241 break;
242 }
243 }
244
245 m_exitStatus = (rnorm <= epsScaled) ? 1 : 3;
246 m_residualNorm = rnorm;
247 m_iterations = iter;
248
249 return (m_exitStatus == 1);
250}
251
252#include <CD_NamespaceFooter.H>
253
254#endif
Restarted, right-preconditioned GMRES over an AMR hierarchy, with persistent basis and fused reductio...
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
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.
Definition CD_KrylovGMRESImplem.H:87
~KrylovGMRES() noexcept
Destructor. Frees the persistent basis and scratch.
Definition CD_KrylovGMRESImplem.H:29
void define(AMRMultigridKrylovOp< T > *a_op, const Vector< LevelData< T > * > &a_template) noexcept
Allocate the persistent basis (m_restart + 1 vectors) and scratch. Call once per regrid.
Definition CD_KrylovGMRESImplem.H:36
bool solve(Vector< LevelData< T > * > &a_phi, const Vector< LevelData< T > * > &a_rhs) noexcept
Solve a_op(a_phi) = a_rhs (homogeneous operator) with restarted GMRES(m_restart).
Definition CD_KrylovGMRESImplem.H:94
void undefine() noexcept
Free the persistent basis and scratch.
Definition CD_KrylovGMRESImplem.H:67