Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_MLCGSolver.H
Go to the documentation of this file.
1
2#ifndef AMREX_MLCGSOLVER_H_
3#define AMREX_MLCGSOLVER_H_
4#include <AMReX_Config.H>
5
6#include <AMReX_MLLinOp.H>
7
8namespace amrex {
9
19template <typename MF>
21{
22public:
23
24 using FAB = typename MLLinOpT<MF>::FAB;
25 using RT = typename MLLinOpT<MF>::RT;
26
27 enum struct Type { BiCGStab, CG };
28
37
38 MLCGSolverT (const MLCGSolverT<MF>& rhs) = delete;
39 MLCGSolverT (MLCGSolverT<MF>&& rhs) = delete;
42
48 void setSolver (Type _typ) noexcept { solver_type = _typ; }
49
65 int solve (MF& solnL, const MF& rhsL, RT eps_rel, RT eps_abs);
66
72 void setVerbose (int _verbose) { verbose = _verbose; }
74 [[nodiscard]] int getVerbose () const { return verbose; }
75
81 void setMaxIter (int _maxiter) { maxiter = _maxiter; }
83 [[nodiscard]] int getMaxIter () const { return maxiter; }
84
90 void setPrintIdentation (std::string s) { print_ident = std::move(s); }
91
99 void setInitSolnZeroed (bool _sol_zeroed) { initial_vec_zeroed = _sol_zeroed; }
101 [[nodiscard]] bool getInitSolnZeroed () const { return initial_vec_zeroed; }
102
108 void setNGhost(int _nghost) {nghost = IntVect(_nghost);}
110 [[nodiscard]] int getNGhost() {return nghost[0];}
111
120 [[nodiscard]] RT dotxy (const MF& r, const MF& z, bool local = false);
128 [[nodiscard]] RT norm_inf (const MF& res, bool local = false);
138 int solve_bicgstab (MF& solnL, const MF& rhsL, RT eps_rel, RT eps_abs);
148 int solve_cg (MF& solnL, const MF& rhsL, RT eps_rel, RT eps_abs);
149
151 [[nodiscard]] int getNumIters () const noexcept { return iter; }
152
153private:
154
155 MLLinOpT<MF>& Lp;
156 Type solver_type;
157 const int amrlev = 0;
158 const int mglev;
159 int verbose = 0;
160 int maxiter = 100;
161 IntVect nghost = IntVect(0);
162 int iter = -1;
163 bool initial_vec_zeroed = false;
164 std::string print_ident;
165};
166
167template <typename MF>
169 : Lp(_lp), solver_type(_typ), mglev(_lp.NMGLevels(0)-1)
170{}
171
172template <typename MF> MLCGSolverT<MF>::~MLCGSolverT () = default;
173
174template <typename MF>
175int
176MLCGSolverT<MF>::solve (MF& sol, const MF& rhs, RT eps_rel, RT eps_abs)
177{
178 if (solver_type == Type::BiCGStab) {
179 return solve_bicgstab(sol,rhs,eps_rel,eps_abs);
180 } else {
181 return solve_cg(sol,rhs,eps_rel,eps_abs);
182 }
183}
184
185template <typename MF>
186int
187MLCGSolverT<MF>::solve_bicgstab (MF& sol, const MF& rhs, RT eps_rel, RT eps_abs)
188{
189 BL_PROFILE("MLCGSolver::bicgstab");
190
191 const int ncomp = nComp(sol);
192
193 MF p = Lp.make(amrlev, mglev, nGrowVect(sol));
194 MF r = Lp.make(amrlev, mglev, nGrowVect(sol));
195 setVal(p, RT(0.0)); // Make sure all entries are initialized to avoid errors
196 setVal(r, RT(0.0));
197
198 MF rh = Lp.make(amrlev, mglev, nghost);
199 MF v = Lp.make(amrlev, mglev, nghost);
200 MF t = Lp.make(amrlev, mglev, nghost);
201
202
203 MF sorig;
204
205 if ( initial_vec_zeroed ) {
206 LocalCopy(r,rhs,0,0,ncomp,nghost);
207 } else {
208 sorig = Lp.make(amrlev, mglev, nghost);
209
210 Lp.correctionResidual(amrlev, mglev, r, sol, rhs, MLLinOpT<MF>::BCMode::Homogeneous);
211
212 LocalCopy(sorig,sol,0,0,ncomp,nghost);
213 setVal(sol, RT(0.0));
214 }
215
216 // Then normalize
217 Lp.normalize(amrlev, mglev, r);
218 LocalCopy(rh, r, 0,0,ncomp,nghost);
219
220 RT rnorm = norm_inf(r);
221 const RT rnorm0 = rnorm;
222
223 if ( verbose > 0 )
224 {
225 amrex::Print() << print_ident << "MLCGSolver_BiCGStab: Initial error (error0) = " << rnorm0 << '\n';
226 }
227 int ret = 0;
228 iter = 1;
229 RT rho_1 = 0, alpha = 0, omega = 0;
230
231 if ( rnorm0 == 0 || rnorm0 < eps_abs )
232 {
233 if ( verbose > 0 )
234 {
235 amrex::Print() << print_ident << "MLCGSolver_BiCGStab: niter = 0,"
236 << ", rnorm = " << rnorm
237 << ", eps_abs = " << eps_abs << '\n';
238 }
239 return ret;
240 }
241
242 for (; iter <= maxiter; ++iter)
243 {
244 const RT rho = dotxy(rh,r);
245 if ( rho == 0 )
246 {
247 ret = 1; break;
248 }
249 if ( iter == 1 )
250 {
251 LocalCopy(p,r,0,0,ncomp,nghost);
252 }
253 else
254 {
255 const RT beta = (rho/rho_1)*(alpha/omega);
256 if constexpr (IsMultiFabLike_v<MF>) {
257 // two operations: p += -omega*v; p = r + beta*p
258 // same as: p = r + beta*(p - omega*v)
259 Saxpy_Xpay(p, -omega, v, beta, r, 0, 0, ncomp, nghost);
260 } else {
261 Saxpy(p, -omega, v, 0, 0, ncomp, nghost); // p += -omega*v
262 Xpay(p, beta, r, 0, 0, ncomp, nghost); // p = r + beta*p
263 }
264 }
266 Lp.normalize(amrlev, mglev, v);
267
268 RT rhTv = dotxy(rh,v);
269 if ( rhTv != RT(0.0) )
270 {
271 alpha = rho/rhTv;
272 }
273 else
274 {
275 ret = 2; break;
276 }
277 if constexpr (IsMultiFabLike_v<MF>) {
278 // sol += alpha * p; r += -alpha * v
279 Saxpy_Saxpy(sol, alpha, p, r, -alpha, v, 0, 0, ncomp, nghost);
280 } else {
281 Saxpy(sol, alpha, p, 0, 0, ncomp, nghost); // sol += alpha * p
282 Saxpy(r, -alpha, v, 0, 0, ncomp, nghost); // r += -alpha * v
283 }
284
285 rnorm = norm_inf(r);
286
287 if ( verbose > 2 && ParallelDescriptor::IOProcessor() )
288 {
289 amrex::Print() << print_ident << "MLCGSolver_BiCGStab: Half Iter "
290 << std::setw(11) << iter
291 << " rel. err. "
292 << rnorm/(rnorm0) << '\n';
293 }
294
295 if ( rnorm < eps_rel*rnorm0 || rnorm < eps_abs ) { break; }
296
298 Lp.normalize(amrlev, mglev, t);
299 //
300 // This is a little funky. I want to elide one of the reductions
301 // in the following two dotxy()s. We do that by calculating the "local"
302 // values and then reducing the two local values at the same time.
303 //
304 RT tvals[2] = { dotxy(t,t,true), dotxy(t,r,true) };
305
306 BL_PROFILE_VAR("MLCGSolver::ParallelAllReduce", blp_par);
307 ParallelAllReduce::Sum(tvals,2,Lp.BottomCommunicator());
308 BL_PROFILE_VAR_STOP(blp_par);
309
310 if ( tvals[0] != RT(0.0) )
311 {
312 omega = tvals[1]/tvals[0];
313 }
314 else
315 {
316 ret = 3; break;
317 }
318 if constexpr (IsMultiFabLike_v<MF>) {
319 // sol += omega * r; r += -omega * t
320 Saypy_Saxpy(sol, omega, r, -omega, t, 0, 0, ncomp, nghost);
321 } else {
322 Saxpy(sol, omega, r, 0, 0, ncomp, nghost); // sol += omega * r
323 Saxpy(r, -omega, t, 0, 0, ncomp, nghost); // r += -omega * t
324 }
325
326 rnorm = norm_inf(r);
327
328 if ( verbose > 2 )
329 {
330 amrex::Print() << print_ident << "MLCGSolver_BiCGStab: Iteration "
331 << std::setw(11) << iter
332 << " rel. err. "
333 << rnorm/(rnorm0) << '\n';
334 }
335
336 if ( rnorm < eps_rel*rnorm0 || rnorm < eps_abs ) { break; }
337
338 if ( omega == 0 )
339 {
340 ret = 4; break;
341 }
342 rho_1 = rho;
343 }
344
345 if ( verbose > 0 )
346 {
347 amrex::Print() << print_ident << "MLCGSolver_BiCGStab: Final: Iteration "
348 << std::setw(4) << iter
349 << " rel. err. "
350 << rnorm/(rnorm0) << '\n';
351 }
352
353 if ( ret == 0 && rnorm > eps_rel*rnorm0 && rnorm > eps_abs)
354 {
355 if ( verbose > 0 && ParallelDescriptor::IOProcessor() ) {
356 amrex::Warning("MLCGSolver_BiCGStab:: failed to converge!");
357 }
358 ret = 8;
359 }
360
361 if ( ( ret == 0 || ret == 8 ) && (rnorm < rnorm0) )
362 {
363 if ( !initial_vec_zeroed ) {
364 LocalAdd(sol, sorig, 0, 0, ncomp, nghost);
365 }
366 if (ret == 8) { ret = 9; }
367 }
368 else
369 {
370 setVal(sol, RT(0.0));
371 if ( !initial_vec_zeroed ) {
372 LocalAdd(sol, sorig, 0, 0, ncomp, nghost);
373 }
374 }
375
376 return ret;
377}
378
379template <typename MF>
380int
381MLCGSolverT<MF>::solve_cg (MF& sol, const MF& rhs, RT eps_rel, RT eps_abs)
382{
383 BL_PROFILE("MLCGSolver::cg");
384
385 const int ncomp = nComp(sol);
386
387 MF p = Lp.make(amrlev, mglev, nGrowVect(sol));
388 setVal(p, RT(0.0));
389
390 MF r = Lp.make(amrlev, mglev, nghost);
391 MF q = Lp.make(amrlev, mglev, nghost);
392
393 MF sorig;
394
395 if ( initial_vec_zeroed ) {
396 LocalCopy(r,rhs,0,0,ncomp,nghost);
397 } else {
398 sorig = Lp.make(amrlev, mglev, nghost);
399
400 Lp.correctionResidual(amrlev, mglev, r, sol, rhs, MLLinOpT<MF>::BCMode::Homogeneous);
401
402 LocalCopy(sorig,sol,0,0,ncomp,nghost);
403 setVal(sol, RT(0.0));
404 }
405
406 RT rnorm = norm_inf(r);
407 const RT rnorm0 = rnorm;
408
409 if ( verbose > 0 )
410 {
411 amrex::Print() << print_ident << "MLCGSolver_CG: Initial error (error0) : " << rnorm0 << '\n';
412 }
413
414 RT rho_1 = 0;
415 int ret = 0;
416 iter = 1;
417
418 if ( rnorm0 == 0 || rnorm0 < eps_abs )
419 {
420 if ( verbose > 0 ) {
421 amrex::Print() << print_ident << "MLCGSolver_CG: niter = 0,"
422 << ", rnorm = " << rnorm
423 << ", eps_abs = " << eps_abs << '\n';
424 }
425 return ret;
426 }
427
428 for (; iter <= maxiter; ++iter)
429 {
430 RT rho = dotxy(r,r);
431
432 if ( rho == 0 )
433 {
434 ret = 1; break;
435 }
436 if (iter == 1)
437 {
438 LocalCopy(p,r,0,0,ncomp,nghost);
439 }
440 else
441 {
442 RT beta = rho/rho_1;
443 Xpay(p, beta, r, 0, 0, ncomp, nghost); // p = r + beta * p
444 }
446
447 RT alpha;
448 RT pw = dotxy(p,q);
449 if ( pw != RT(0.0))
450 {
451 alpha = rho/pw;
452 }
453 else
454 {
455 ret = 1; break;
456 }
457
458 if ( verbose > 2 )
459 {
460 amrex::Print() << print_ident << "MLCGSolver_cg:"
461 << " iter " << iter
462 << " rho " << rho
463 << " alpha " << alpha << '\n';
464 }
465 if constexpr (IsMultiFabLike_v<MF>) {
466 // sol += alpha * p; r += -alpha * q
467 Saxpy_Saxpy(sol, alpha, p, r, -alpha, q, 0, 0, ncomp, nghost);
468 } else {
469 Saxpy(sol, alpha, p, 0, 0, ncomp, nghost); // sol += alpha * p
470 Saxpy(r, -alpha, q, 0, 0, ncomp, nghost); // r += -alpha * q
471 }
472 rnorm = norm_inf(r);
473
474 if ( verbose > 2 )
475 {
476 amrex::Print() << print_ident << "MLCGSolver_cg: Iteration"
477 << std::setw(4) << iter
478 << " rel. err. "
479 << rnorm/(rnorm0) << '\n';
480 }
481
482 if ( rnorm < eps_rel*rnorm0 || rnorm < eps_abs ) { break; }
483
484 rho_1 = rho;
485 }
486
487 if ( verbose > 0 )
488 {
489 amrex::Print() << print_ident << "MLCGSolver_cg: Final Iteration"
490 << std::setw(4) << iter
491 << " rel. err. "
492 << rnorm/(rnorm0) << '\n';
493 }
494
495 if ( ret == 0 && rnorm > eps_rel*rnorm0 && rnorm > eps_abs )
496 {
497 if ( verbose > 0 && ParallelDescriptor::IOProcessor() ) {
498 amrex::Warning("MLCGSolver_cg: failed to converge!");
499 }
500 ret = 8;
501 }
502
503 if ( ( ret == 0 || ret == 8 ) && (rnorm < rnorm0) )
504 {
505 if ( !initial_vec_zeroed ) {
506 LocalAdd(sol, sorig, 0, 0, ncomp, nghost);
507 }
508 if (ret == 8) { ret = 9; }
509 }
510 else
511 {
512 setVal(sol, RT(0.0));
513 if ( !initial_vec_zeroed ) {
514 LocalAdd(sol, sorig, 0, 0, ncomp, nghost);
515 }
516 }
517
518 return ret;
519}
520
521template <typename MF>
522auto
523MLCGSolverT<MF>::dotxy (const MF& r, const MF& z, bool local) -> RT
524{
525 BL_PROFILE_VAR_NS("MLCGSolver::ParallelAllReduce", blp_par);
526 if (!local) { BL_PROFILE_VAR_START(blp_par); }
527 RT result = Lp.xdoty(amrlev, mglev, r, z, local);
528 if (!local) { BL_PROFILE_VAR_STOP(blp_par); }
529 return result;
530}
531
532template <typename MF>
533auto
534MLCGSolverT<MF>::norm_inf (const MF& res, bool local) -> RT
535{
536 int ncomp = nComp(res);
537 RT result = norminf(res,0,ncomp,IntVect(0),true);
538 if (!local) {
539 BL_PROFILE("MLCGSolver::ParallelAllReduce");
540 ParallelAllReduce::Max(result, Lp.BottomCommunicator());
541 }
542 return result;
543}
544
546
547}
548
549#endif /*_CGSOLVER_H_*/
#define BL_PROFILE_VAR_START(vname)
Definition AMReX_BLProfiler.H:562
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:551
#define BL_PROFILE_VAR_STOP(vname)
Definition AMReX_BLProfiler.H:563
#define BL_PROFILE_VAR(fname, vname)
Definition AMReX_BLProfiler.H:560
#define BL_PROFILE_VAR_NS(fname, vname)
Definition AMReX_BLProfiler.H:561
CG-family solvers (BiCGStab or CG) for use as the bottom solver in MLMG.
Definition AMReX_MLCGSolver.H:21
RT dotxy(const MF &r, const MF &z, bool local=false)
Dot product helper; set local to true to skip the MPI reduction.
Definition AMReX_MLCGSolver.H:523
void setSolver(Type _typ) noexcept
Switch between BiCGStab and CG after construction.
Definition AMReX_MLCGSolver.H:48
bool getInitSolnZeroed() const
Whether setInitSolnZeroed(true) was requested.
Definition AMReX_MLCGSolver.H:101
void setVerbose(int _verbose)
Control how much logging is emitted (0 = silent).
Definition AMReX_MLCGSolver.H:72
int getNGhost()
Current grow-cell count (same in every direction).
Definition AMReX_MLCGSolver.H:110
MLCGSolverT< MF > & operator=(const MLCGSolverT< MF > &rhs)=delete
int getNumIters() const noexcept
Iteration count from the last solve* call (or -1 if unused).
Definition AMReX_MLCGSolver.H:151
int solve_cg(MF &solnL, const MF &rhsL, RT eps_rel, RT eps_abs)
Raw CG implementation mirroring the high-level solve() signature.
Definition AMReX_MLCGSolver.H:381
RT norm_inf(const MF &res, bool local=false)
Infinity norm helper; set local to true to skip the MPI reduction.
Definition AMReX_MLCGSolver.H:534
void setInitSolnZeroed(bool _sol_zeroed)
Definition AMReX_MLCGSolver.H:99
int solve_bicgstab(MF &solnL, const MF &rhsL, RT eps_rel, RT eps_abs)
Raw BiCGStab implementation mirroring the high-level solve() signature.
Definition AMReX_MLCGSolver.H:187
MLCGSolverT(MLCGSolverT< MF > &&rhs)=delete
typename MLLinOpT< MF >::FAB FAB
Definition AMReX_MLCGSolver.H:24
int getMaxIter() const
Current iteration cap.
Definition AMReX_MLCGSolver.H:83
void setPrintIdentation(std::string s)
Prefix printed messages (e.g., to indent per level).
Definition AMReX_MLCGSolver.H:90
int solve(MF &solnL, const MF &rhsL, RT eps_rel, RT eps_abs)
Solve Lp(solnL)=rhsL to the requested tolerance.
Definition AMReX_MLCGSolver.H:176
Type
Definition AMReX_MLCGSolver.H:27
typename MLLinOpT< MF >::RT RT
Definition AMReX_MLCGSolver.H:25
void setNGhost(int _nghost)
Set the number of grow cells used when allocating temporaries.
Definition AMReX_MLCGSolver.H:108
MLCGSolverT(MLLinOpT< MF > &_lp, Type _typ=Type::BiCGStab)
Construct a solver bound to _lp.
Definition AMReX_MLCGSolver.H:168
int getVerbose() const
Current verbosity level.
Definition AMReX_MLCGSolver.H:74
void setMaxIter(int _maxiter)
Cap the number of Krylov iterations performed.
Definition AMReX_MLCGSolver.H:81
MLCGSolverT(const MLCGSolverT< MF > &rhs)=delete
Abstract base class for multilevel linear operators used by MLMG and the bottom solvers.
Definition AMReX_MLLinOp.H:137
typename FabDataType< MF >::fab_type FAB
Definition AMReX_MLLinOp.H:147
typename FabDataType< MF >::value_type RT
Definition AMReX_MLLinOp.H:148
This class provides the user with a few print options.
Definition AMReX_Print.H:35
void Sum(Gpu::DeviceVector< T > &v, MPI_Comm comm)
Definition AMReX_GpuParallelReduce.H:34
bool IOProcessor() noexcept
Is this CPU the I/O Processor? To get the rank number, call IOProcessorNumber()
Definition AMReX_ParallelDescriptor.H:289
void Max(KeyValuePair< K, V > &vi, MPI_Comm comm)
Definition AMReX_ParallelReduce.H:133
Definition AMReX_Amr.cpp:50
int nComp(FabArrayBase const &fa)
Definition AMReX_FabArrayBase.cpp:2852
IntVect nGrowVect(FabArrayBase const &fa)
Definition AMReX_FabArrayBase.cpp:2857
void Saxpy_Xpay(MF &dst, typename MF::value_type a_saxpy, MF const &src_saxpy, typename MF::value_type a_xpay, MF const &src_xpay, int scomp, int dcomp, int ncomp, IntVect const &nghost)
dst += a_saxpy * src_saxpy followed by dst = src_xpay + a_xpay * dst
Definition AMReX_FabArrayUtility.H:1914
void Saxpy_Saxpy(MF &dst1, typename MF::value_type a1, MF const &src1, MF &dst2, typename MF::value_type a2, MF const &src2, int scomp, int dcomp, int ncomp, IntVect const &nghost)
dst1 += a1 * src1 followed by dst2 += a2 * src2
Definition AMReX_FabArrayUtility.H:1923
void Saypy_Saxpy(MF &dst1, typename MF::value_type a1, MF &dst2, typename MF::value_type a2, MF const &src, int scomp, int dcomp, int ncomp, IntVect const &nghost)
dst1 += a1 * dst2 followed by dst2 += a2 * src
Definition AMReX_FabArrayUtility.H:1932
IntVectND< 3 > IntVect
IntVect is an alias for amrex::IntVectND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:38
void LocalCopy(DMF &dst, SMF const &src, int scomp, int dcomp, int ncomp, IntVect const &nghost)
dst = src
Definition AMReX_FabArrayUtility.H:1882
MF::value_type norminf(MF const &mf, int scomp, int ncomp, IntVect const &nghost, bool local=false)
Definition AMReX_FabArrayUtility.H:1961
void Xpay(MF &dst, typename MF::value_type a, MF const &src, int scomp, int dcomp, int ncomp, IntVect const &nghost)
dst = src + a * dst
Definition AMReX_FabArrayUtility.H:1906
void Warning(const std::string &msg)
Print a warning message to the diagnostic stream and keep running.
Definition AMReX.cpp:247
void Saxpy(MF &dst, typename MF::value_type a, MF const &src, int scomp, int dcomp, int ncomp, IntVect const &nghost)
dst += a * src
Definition AMReX_FabArrayUtility.H:1898
void LocalAdd(MF &dst, MF const &src, int scomp, int dcomp, int ncomp, IntVect const &nghost)
dst += src
Definition AMReX_FabArrayUtility.H:1890
void setVal(MF &dst, typename MF::value_type val)
dst = val
Definition AMReX_FabArrayUtility.H:1861