chombo-discharge
Loading...
Searching...
No Matches
CD_EllipticSolverChainImplem.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_ELLIPTICSOLVERCHAINIMPLEM_H
14#define CD_ELLIPTICSOLVERCHAINIMPLEM_H
15
16// Std includes
17#include <algorithm>
18
19// Chombo includes
20#include <CH_Timer.H>
21#include <parstream.H>
22
23// Our includes
25#include <CD_NamespaceHeader.H>
26
27namespace EllipticSolverChain {
28
29template <class T>
31{
32 this->freeWork();
33}
34
35template <class T>
36void
38{
39 m_bicg.undefine();
40 m_gmres.undefine();
41
42 if (m_workAllocated) {
43 m_op.clear(m_r0);
44 m_op.clear(m_dphi);
45 m_op.clear(m_phiInit);
46 m_workAllocated = false;
47 }
48}
49
50template <class T>
51void
53{
54 m_op.create(m_r0, a_template);
55 m_op.create(m_dphi, a_template);
56 m_op.create(m_phiInit, a_template);
57 m_workAllocated = true;
58}
59
60template <class T>
61void
63{
64 CH_TIME("EllipticSolverChain::KrylovSolver::snapshotGuess");
65
66 if (!m_workAllocated) {
67 this->allocateWork(a_phi);
68 }
69
70 m_op.assign(m_phiInit, a_phi);
71 m_initResidual = m_mg->computeAMRResidual(a_phi, a_rhs, m_lmax, m_lbase);
72}
73
74template <class T>
75bool
77{
78 CH_TIME("EllipticSolverChain::KrylovSolver::keepOrRestore");
79
80 const Real r = m_mg->computeAMRResidual(a_phi, a_rhs, m_lmax, m_lbase);
81
82 // Keep the just-failed solve only if it improved on the chain's initial residual; otherwise revert to the
83 // initial-residual solution.
84 if (r >= m_initResidual) {
85 m_op.assign(a_phi, m_phiInit);
86
87 return false;
88 }
89
90 return true;
91}
92
93template <class T>
94void
95KrylovSolver<T>::define(AMRMultiGrid<LevelData<T>>* a_mg,
96 const Vector<RefCountedPtr<LevelData<BaseFab<bool>>>>& a_validCells,
97 const Vector<Real>& a_dx,
98 const int a_lbase,
99 const int a_lmax,
100 const int a_numVCycles)
101{
102 CH_TIME("EllipticSolverChain::KrylovSolver::define");
103
104 // Release any storage tied to the previous grid before redefining the adapter for the new one. The work vectors
105 // are re-allocated lazily on the first solve (when a template hierarchy is available).
106 this->freeWork();
107
108 m_mg = a_mg;
109 m_lbase = a_lbase;
110 m_lmax = a_lmax;
111
112 m_op.define(a_mg, a_validCells, a_dx, a_lbase, a_lmax, a_numVCycles);
113
114 m_isDefined = true;
115}
116
117template <class T>
118bool
120 const Vec& a_rhs,
121 const bool a_zeroPhi,
122 const SolverType a_type,
123 const Settings& a_settings)
124{
125 CH_TIME("EllipticSolverChain::KrylovSolver::solve");
126
127 CH_assert(m_isDefined);
128
129 // Allocate the persistent residual-correction vectors once per grid (first solve after a define()/regrid).
130 if (!m_workAllocated) {
131 this->allocateWork(a_rhs);
132 }
133
134 if (a_zeroPhi) {
135 m_op.setToZero(a_phi);
136 }
137
138 // The preconditioner drives AMRMultiGrid::AMRVCycle directly, which needs the solver's *intermediate* multigrid
139 // depths. AMRMultiGrid sets these up in init() and tears them down in revert(); the host solvers revert() after
140 // every solve (and the stand-alone GMG path re-init()s every solve), so we re-initialise here -- once per field
141 // solve, exactly as the GMG path does.
142 m_mg->init(a_phi, a_rhs, m_lmax, m_lbase);
143 m_mg->setBottomSolver(m_lmax, m_lbase);
144
145 // Zero-residual ||rhs - L(0)|| in the adapter norm. The printed residuals are normalized against this so they are
146 // on a common, solver-independent scale (a relative residual), comparable to the convergence tolerance. m_dphi is
147 // the (zero) correction accumulator, reused here as the zero solution.
148 m_op.setToZero(m_dphi);
149 m_mg->computeAMRResidual(m_r0, m_dphi, a_rhs, m_lmax, m_lbase, false, false);
150
151 const Real zeroResid = m_op.norm(m_r0, 2);
152 const Real residualScale = (zeroResid > 0.0) ? zeroResid : 1.0;
153
154 // Initial residual r0 = rhs - L_inhomogeneous(phi). This is the only inhomogeneous evaluation; the Krylov matvec
155 // and preconditioner run with homogeneous boundary conditions. computeAMRResidual returns rhs - L(phi) in
156 // Chombo's convention, so it must NOT be negated.
157 m_mg->computeAMRResidual(m_r0, a_phi, a_rhs, m_lmax, m_lbase, false, false);
158
159 const Real rNorm0 = m_op.norm(m_r0, 2);
160
161 bool converged = false;
162 int exitStatus = -1;
163 Real residualNorm = rNorm0;
164
165 switch (a_type) {
166 case SolverType::GMRES: {
167 const int restart = std::max(1, a_settings.restart);
168
169 if (!m_gmres.isDefined() || m_gmres.m_restart != restart) {
170 m_gmres.m_restart = restart;
171 m_gmres.define(&m_op, a_rhs);
172 }
173
174 m_gmres.m_eps = a_settings.eps;
175 m_gmres.m_maxIter = a_settings.maxIter;
176 m_gmres.m_verbosity = a_settings.verbosity;
177 m_gmres.m_residualScale = residualScale;
178
179 converged = m_gmres.solve(m_dphi, m_r0);
180 exitStatus = m_gmres.m_exitStatus;
181 residualNorm = m_gmres.m_residualNorm;
182 m_lastKrylovIters = m_gmres.m_iterations;
183
184 break;
185 }
187 if (!m_bicg.isDefined()) {
188 m_bicg.define(&m_op, a_rhs);
189 }
190
191 m_bicg.m_eps = a_settings.eps;
192 m_bicg.m_maxIter = a_settings.maxIter;
193 m_bicg.m_verbosity = a_settings.verbosity;
194 m_bicg.m_residualScale = residualScale;
195
196 converged = m_bicg.solve(m_dphi, m_r0);
197 exitStatus = m_bicg.m_exitStatus;
198 residualNorm = m_bicg.m_residualNorm;
199 m_lastKrylovIters = m_bicg.m_iterations;
200
201 break;
202 }
203 default: {
204 MayDay::Error("EllipticSolverChain::KrylovSolver::solve - SolverType must be GMRES or BiCGStab");
205
206 break;
207 }
208 }
209
210 // One-line convergence summary (the per-iteration history, if any, is printed by the solver itself). The residual
211 // is reported relative to the zero-residual (rel.res = ||r|| / ||r_zero||), the same solver-independent scale used
212 // in the per-iteration output, with the absolute norms kept for reference. The verbosity is the host's
213 // gmg_verbosity, reused for the Krylov path.
214 if (a_settings.verbosity >= 1) {
215 const Real relRes = residualNorm / residualScale;
216
217 pout() << "EllipticSolverChain - " << solverTypeName(a_type) << (converged ? " converged" : " did NOT converge")
218 << " (rel.res = " << relRes << ", |r| = " << residualNorm << ", |r_zero| = " << zeroResid
219 << ", iters = " << m_lastKrylovIters << ", status = " << exitStatus << ")" << endl;
220 }
221
222 // phi += dphi.
223 m_op.incr(a_phi, m_dphi, 1.0);
224
225 return converged;
226}
227
228} // namespace EllipticSolverChain
229
230#include <CD_NamespaceFooter.H>
231
232#endif
Declaration of outer Krylov solves (GMRES/BiCGStab) preconditioned by an AMR-multigrid cycle.
bool keepOrRestore(Vec &a_phi, const Vec &a_rhs)
Warm-start rule applied between fallback attempts.
Definition CD_EllipticSolverChainImplem.H:76
bool solve(Vec &a_phi, const Vec &a_rhs, const bool a_zeroPhi, const SolverType a_type, const Settings &a_settings)
Solve L(phi) = rhs with the chosen outer Krylov method preconditioned by the multigrid cycle.
Definition CD_EllipticSolverChainImplem.H:119
~KrylovSolver() noexcept
Destructor. Frees the persistent work vectors and custom solvers.
Definition CD_EllipticSolverChainImplem.H:30
void snapshotGuess(Vec &a_phi, const Vec &a_rhs)
Snapshot the chain's starting guess and its residual for the fallback warm-start rule.
Definition CD_EllipticSolverChainImplem.H:62
Vector< LevelData< T > * > Vec
Multilevel data type.
Definition CD_EllipticSolverChain.H:317
void define(AMRMultiGrid< LevelData< T > > *a_mg, const Vector< RefCountedPtr< LevelData< BaseFab< bool > > > > &a_validCells, const Vector< Real > &a_dx, const int a_lbase, const int a_lmax, const int a_numVCycles)
Define the multigrid-preconditioner adapter and reset the persistent work. Call once per regrid.
Definition CD_EllipticSolverChainImplem.H:95
void allocateWork(const Vec &a_template)
Lazily allocate the persistent residual-correction vectors r0/dphi from a template hierarchy.
Definition CD_EllipticSolverChainImplem.H:52
void freeWork()
Free the persistent work vectors and undefine the custom solvers.
Definition CD_EllipticSolverChainImplem.H:37
Support code for using an AMR-multigrid cycle as a preconditioner for an outer Krylov solver.
Definition CD_EllipticSolverChain.H:37
std::string solverTypeName(const SolverType a_type)
Human-readable name for a solver type, for diagnostic output.
Definition CD_EllipticSolverChain.H:283
SolverType
Top-level solve method.
Definition CD_EllipticSolverChain.H:43
@ BiCGStab
BiCGStab preconditioned by the multigrid cycle.
@ GMRES
GMRES preconditioned by the multigrid cycle.
Outer-Krylov configuration. Defaults reproduce stand-alone multigrid (solvers == {GMG}).
Definition CD_EllipticSolverChain.H:64
Real eps
Relative residual tolerance.
Definition CD_EllipticSolverChain.H:74
int restart
GMRES restart length (unused for BiCGStab).
Definition CD_EllipticSolverChain.H:84
int maxIter
Maximum outer iterations.
Definition CD_EllipticSolverChain.H:79
int verbosity
Outer Krylov verbosity.
Definition CD_EllipticSolverChain.H:95