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
96 CH_TIME("KrylovGMRES::solve");
97
98 CH_assert(m_isDefined);
99
100 AMRMultigridKrylovOp<T>& op = *m_op;
101
102 m_exitStatus = -1;
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);
114
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;
121 }
122
123 Real rnorm = beta0;
124 int iter = 0;
126 while (iter < m_maxIter && rnorm > m_eps * beta0) {
127
128 // Start a restart cycle: v_0 = r / ||r||.
129 const Real beta = op.norm(m_r, 2);
130 op.assign(m_basis[0], m_r);
131 op.scale(m_basis[0], 1.0 / beta);
132
133 for (int i = 0; i <= m; i++) {
134 m_g[i] = 0.0;
135 }
136 m_g[0] = beta;
137
138 int jUsed = 0;
139
140 for (int j = 0; j < m && iter < m_maxIter; j++) {
141 iter++;
142
143 // Arnoldi step on A*K^{-1}: w = A (K^{-1} v_j). The preconditioned vector is not stored; the solution update
144 // is preconditioned once per restart cycle below.
145 op.preCond(m_z, m_basis[j]);
146 op.applyOp(m_w, m_z, true);
147
148 // Classical Gram-Schmidt against v_0..v_j -- one fused reduction (mDotProduct) instead of j+1 dot products.
149 // m_hcol is persistent scratch; only entries 0..j are written and read this step.
150 op.mDotProduct(m_w, j + 1, &m_basis[0], &m_hcol[0]);
151
152 for (int i = 0; i <= j; i++) {
153 op.incr(m_w, m_basis[i], -m_hcol[i]);
154 m_H[Hidx(i, j)] = m_hcol[i];
155 }
156
157 if (m_reorthogonalize) {
158 op.mDotProduct(m_w, j + 1, &m_basis[0], &m_corr[0]);
159
160 for (int i = 0; i <= j; i++) {
161 op.incr(m_w, m_basis[i], -m_corr[i]);
162 m_H[Hidx(i, j)] += m_corr[i];
163 }
164 }
165
166 const Real hjp1 = op.norm(m_w, 2);
167 m_H[Hidx(j + 1, j)] = hjp1;
168 if (hjp1 != 0.0) {
169 op.assign(m_basis[j + 1], m_w);
170 op.scale(m_basis[j + 1], 1.0 / hjp1);
171 }
172
173 // Apply existing Givens rotations to the new Hessenberg column.
174 for (int k = 0; k < j; k++) {
175 const Real t = m_cs[k] * m_H[Hidx(k, j)] + m_sn[k] * m_H[Hidx(k + 1, j)];
176 m_H[Hidx(k + 1, j)] = -m_sn[k] * m_H[Hidx(k, j)] + m_cs[k] * m_H[Hidx(k + 1, j)];
177 m_H[Hidx(k, j)] = t;
178 }
179
180 // New Givens rotation eliminating H[j+1][j].
181 const Real hjj = m_H[Hidx(j, j)];
182 const Real hj1j = m_H[Hidx(j + 1, j)];
183 const Real denom = std::sqrt(hjj * hjj + hj1j * hj1j);
184
185 if (denom == 0.0) {
186 break;
187 }
188
189 m_cs[j] = hjj / denom;
190 m_sn[j] = hj1j / denom;
191 m_H[Hidx(j, j)] = denom;
192 m_H[Hidx(j + 1, j)] = 0.0;
193
194 const Real gtemp = m_cs[j] * m_g[j];
195 m_g[j + 1] = -m_sn[j] * m_g[j];
196 m_g[j] = gtemp;
197
198 rnorm = std::abs(m_g[j + 1]);
199 jUsed = j + 1;
200
201 if (m_verbosity >= 3) {
202 pout() << " KrylovGMRES:: iteration = " << iter << ", rel. residual = " << (rnorm / m_residualScale)
203 << endl;
204 }
205
206 if (rnorm <= m_eps * beta0 || hjp1 == 0.0) {
207 break;
208 }
209 }
210
211 // Back-substitute the (jUsed x jUsed) upper-triangular system H y = g.
212 for (int i = jUsed - 1; i >= 0; i--) {
213 Real s = m_g[i];
214
215 for (int k = i + 1; k < jUsed; k++) {
216 s -= m_H[Hidx(i, k)] * m_y[k];
217 }
218
219 m_y[i] = (m_H[Hidx(i, i)] != 0.0) ? s / m_H[Hidx(i, i)] : 0.0;
220 }
221
222 // Form the unpreconditioned update c = sum_k y_k v_k, precondition once, and apply: phi += K^{-1} c.
223 op.setToZero(m_c);
224 for (int k = 0; k < jUsed; k++) {
225 op.incr(m_c, m_basis[k], m_y[k]);
226 }
227
228 op.preCond(m_z, m_c);
229 op.incr(a_phi, m_z, 1.0);
230
231 // Recompute the true residual for the restart/convergence decision.
232 op.applyOp(m_r, a_phi, true);
233 op.axby(m_r, a_rhs, m_r, 1.0, -1.0);
234 rnorm = op.norm(m_r, 2);
235
236 if (jUsed == 0) { // no progress (degenerate) -- avoid spinning
237 break;
238 }
239 }
240
241 m_exitStatus = (rnorm <= m_eps * beta0) ? 1 : 3;
242 m_residualNorm = rnorm;
243 m_iterations = iter;
244
245 return (m_exitStatus == 1);
246}
247
248#include <CD_NamespaceFooter.H>
249
250#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