Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_MLMG.H
Go to the documentation of this file.
1#ifndef AMREX_ML_MG_H_
2#define AMREX_ML_MG_H_
3#include <AMReX_Config.H>
4#include <AMReX_Enum.H>
5
6#include <AMReX_MLLinOp.H>
7#include <AMReX_MLCGSolver.H>
8
9#include <functional>
10#include <limits>
11#include <optional>
12
13namespace amrex {
14
21
22// Norm used to evaluate the target convergence criteria
24
35
37template <typename MF>
38class MLMGT
39{
40public:
41
42 class error
43 : public std::runtime_error
44 {
45 public :
46 using std::runtime_error::runtime_error;
47 };
48
49 template <typename T> friend class MLCGSolverT;
50 template <typename M> friend class GMRESMLMGT;
51
52 using MFType = MF;
53 using FAB = typename MLLinOpT<MF>::FAB;
54 using RT = typename MLLinOpT<MF>::RT;
55
56 using BCMode = typename MLLinOpT<MF>::BCMode;
58
61 enum class CFStrategy : int {none,ghostnodes};
62
65
66 MLMGT (MLMGT<MF> const&) = delete;
67 MLMGT (MLMGT<MF> &&) = delete;
68 MLMGT<MF>& operator= (MLMGT<MF> const&) = delete;
70
81 template <typename AMF>
82 RT solve (const Vector<AMF*>& a_sol, const Vector<AMF const*>& a_rhs,
83 RT a_tol_rel, RT a_tol_abs, const char* checkpoint_file = nullptr);
84
95 template <typename AMF>
96 RT solve (std::initializer_list<AMF*> a_sol,
97 std::initializer_list<AMF const*> a_rhs,
98 RT a_tol_rel, RT a_tol_abs, const char* checkpoint_file = nullptr);
99
109 RT precond (Vector<MF*> const& a_sol, Vector<MF const*> const& a_rhs,
110 RT a_tol_rel, RT a_tol_abs);
111
118 template <typename AMF>
120 Location a_loc = Location::FaceCenter);
121
128 template <typename AMF>
129 void getGradSolution (std::initializer_list<Array<AMF*,AMREX_SPACEDIM>> a_grad_sol,
130 Location a_loc = Location::FaceCenter);
131
138 template <typename AMF>
140 Location a_loc = Location::FaceCenter);
141
148 template <typename AMF>
149 void getFluxes (std::initializer_list<Array<AMF*,AMREX_SPACEDIM>> a_flux,
150 Location a_loc = Location::FaceCenter);
151
159 template <typename AMF>
161 const Vector<AMF*> & a_sol,
162 Location a_loc = Location::FaceCenter);
163
171 template <typename AMF>
172 void getFluxes (std::initializer_list<Array<AMF*,AMREX_SPACEDIM>> a_flux,
173 std::initializer_list<AMF*> a_sol,
174 Location a_loc = Location::FaceCenter);
175
182 template <typename AMF>
183 void getFluxes (const Vector<AMF*> & a_flux,
184 Location a_loc = Location::CellCenter);
185
192 template <typename AMF>
193 void getFluxes (std::initializer_list<AMF*> a_flux,
194 Location a_loc = Location::CellCenter);
195
203 template <typename AMF>
204 void getFluxes (const Vector<AMF*> & a_flux,
205 const Vector<AMF*> & a_sol,
206 Location a_loc = Location::CellCenter);
207
215 template <typename AMF>
216 void getFluxes (std::initializer_list<AMF*> a_flux,
217 std::initializer_list<AMF*> a_sol,
218 Location a_loc = Location::CellCenter);
219
227 void compResidual (const Vector<MF*>& a_res, const Vector<MF*>& a_sol,
228 const Vector<MF const*>& a_rhs);
229
230#ifdef AMREX_USE_EB
236 void getEBFluxes (const Vector<MF*>& a_eb_flux);
243 void getEBFluxes (const Vector<MF*>& a_eb_flux, const Vector<MF*> & a_sol);
244#endif
245
251 void apply (const Vector<MF*>& out, const Vector<MF*>& in);
252
259 void applyPrecond (const Vector<MF*>& out, const Vector<MF*>& in);
260
261 [[nodiscard]] int getVerbose () const { return verbose; }
262 [[nodiscard]] int getBottomVerbose () const { return bottom_verbose; }
263
268
276 void setThrowException (bool t) noexcept { throw_exception = t; }
282 void setVerbose (int v) noexcept { verbose = v; }
288 void setMaxIter (int n) noexcept { max_iters = n; }
294 void setMaxFmgIter (int n) noexcept { max_fmg_iters = n; }
301 void setFixedIter (int nit) noexcept { do_fixed_number_of_iters = nit; }
308 void setPrecondIter (int nit) noexcept { max_precond_iters = nit; }
309
315 void setPreSmooth (int n) noexcept { nu1 = n; }
321 void setPostSmooth (int n) noexcept { nu2 = n; }
327 void setFinalSmooth (int n) noexcept { nuf = n; }
333 void setBottomSmooth (int n) noexcept { nub = n; }
334
340 void setBottomSolver (BottomSolver s) noexcept { bottom_solver = s; }
341 [[nodiscard]] BottomSolver getBottomSolver () const noexcept { return bottom_solver; }
342
344 void setMultigridType (MultigridType t) noexcept { multigrid_type = t; }
345 [[nodiscard]] MultigridType getMultigridType () const noexcept { return multigrid_type; }
348 [[nodiscard]] bool usedAlgMG () const noexcept { return m_algmg_active; }
349
360 void setAlgMGOptions (std::function<void(AlgMG<RT>&)> f) {
361 m_algmg_options = std::move(f);
362 m_algmg_bottom.reset();
363 m_algmg_level0.reset();
364 }
365
371 void setHybridStallCriterion (int window, RT rate) noexcept {
372 hybrid_stall_window = window; hybrid_stall_rate = rate;
373 }
374
376 void setHybridDivergenceFactor (RT f) noexcept { hybrid_divergence_factor = f; }
382 void setCFStrategy (CFStrategy a_cf_strategy) noexcept {cf_strategy = a_cf_strategy;}
388 void setBottomVerbose (int v) noexcept { bottom_verbose = v; }
394 void setBottomMaxIter (int n) noexcept { bottom_maxiter = n; }
400 void setBottomTolerance (RT t) noexcept { bottom_reltol = t; }
406 void setBottomToleranceAbs (RT t) noexcept { bottom_abstol = t;}
407 [[nodiscard]] RT getBottomToleranceAbs () const noexcept{ return bottom_abstol; }
408
409 [[deprecated("Use MLMG::setConvergenceNormType() instead.")]]
415 void setAlwaysUseBNorm (int flag) noexcept;
416
422 void setConvergenceNormType (MLMGNormType norm) noexcept { norm_type = norm; }
423
429 void setFinalFillBC (int flag) noexcept { final_fill_bc = flag; }
430
431 [[nodiscard]] int numAMRLevels () const noexcept { return namrlevs; }
432
438 void setNSolve (int flag) noexcept { do_nsolve = flag; }
444 void setNSolveGridSize (int s) noexcept { nsolve_grid_size = s; }
445
461 void setNoGpuSync (bool do_not_sync) noexcept { do_no_sync_gpu = do_not_sync; }
462
463#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
469 void setHypreInterface (Hypre::Interface f) noexcept {
470 // must use ij interface for EB
471#ifndef AMREX_USE_EB
472 hypre_interface = f;
473#else
475#endif
476 }
477
483 void setHypreOptionsNamespace(const std::string& prefix) noexcept
484 {
485 hypre_options_namespace = prefix;
486 }
487
489 void setHypreOldDefault (bool l) noexcept {hypre_old_default = l;}
491 void setHypreRelaxType (int n) noexcept {hypre_relax_type = n;}
493 void setHypreRelaxOrder (int n) noexcept {hypre_relax_order = n;}
495 void setHypreNumSweeps (int n) noexcept {hypre_num_sweeps = n;}
497 void setHypreStrongThreshold (Real t) noexcept {hypre_strong_threshold = t;}
498#endif
499
506
513 template <typename AMF>
514 void prepareForSolve (Vector<AMF*> const& a_sol, Vector<AMF const*> const& a_rhs);
515
518
521
524
530 void oneIter (int iter);
531
537 void miniCycle (int amrlev);
538
545 void mgVcycle (int amrlev, int mglev);
547 void mgFcycle ();
548
550 void bottomSolve ();
558 void NSolve (MLMGT<MF>& a_solver, MF& a_sol, MF& a_rhs);
561
570 void postCG (int ret, int niters = -1);
571
577 void computeMLResidual (int amrlevmax);
583 void computeResidual (int alev);
590 void computeResWithCrseSolFineCor (int calev, int falev);
602 void interpCorrection (int alev);
609 void interpCorrection (int alev, int mglev);
616 void addInterpCorrection (int alev, int mglev);
617
624 void computeResOfCorrection (int amrlev, int mglev);
625
632 RT ResNormInf (int alev, bool local = false);
639 RT MLResNormInf (int alevmax, bool local = false);
645 RT MLRhsNormInf (bool local = false);
646
656 void makeSolvable (int amrlev, int mglev, MF& mf);
657
658#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
666 template <class TMF=MF>
667 requires (std::same_as<TMF,MultiFab>)
668 void bottomSolveWithHypre (MF& x, const MF& b);
669#endif
670
671#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
679 template <class TMF=MF>
680 requires (std::same_as<TMF,MultiFab>)
681 void bottomSolveWithPETSc (MF& x, const MF& b);
682#endif
683
692 int bottomSolveWithCG (MF& x, const MF& b, typename MLCGSolverT<MF>::Type type);
693
695 template <class TMF=MF>
696 requires (std::same_as<TMF,MultiFab>)
697 void bottomSolveWithAlgMG (MF& x, const MF& b);
698
701 template <class TMF=MF>
702 requires (std::same_as<TMF,MultiFab>)
704
705 template <class TMF=MF>
706 requires (std::same_as<TMF,MultiFab>)
708 void configureAlgMG (MLAlgMG& s, bool singular, bool with_options);
709
710 template <class TMF=MF>
711 requires (std::same_as<TMF,MultiFab>)
713 void algmgSolve (MLAlgMG& s, MF& x, MF const& b);
714
717 [[nodiscard]] bool hybridShouldSwitch (Vector<RT> const& norms, RT max_norm,
718 RT best_norm, std::string& reason) const;
719
720 [[nodiscard]] RT getInitRHS () const noexcept { return m_rhsnorm0; }
721 // Initial composite residual
722 [[nodiscard]] RT getInitResidual () const noexcept { return m_init_resnorm0; }
723 // Final composite residual
724 [[nodiscard]] RT getFinalResidual () const noexcept { return m_final_resnorm0; }
725 // Residuals on the *finest* AMR level after each iteration
726 [[nodiscard]] Vector<RT> const& getResidualHistory () const noexcept { return m_iter_fine_resnorm0; }
727 [[nodiscard]] int getNumIters () const noexcept { return m_iter_fine_resnorm0.size(); }
728 [[nodiscard]] Vector<int> const& getNumCGIters () const noexcept { return m_niters_cg; }
729
730 MLLinOpT<MF>& getLinOp () { return linop; }
731
732private:
733
734 bool precond_mode = false;
735 bool throw_exception = false;
736 int verbose = 1;
737
738 int max_iters = 200;
739 int do_fixed_number_of_iters = 0;
740 int max_precond_iters = 1;
741
742 int nu1 = 2;
743 int nu2 = 2;
744 int nuf = 8;
745 int nub = 0;
746
747 int max_fmg_iters = 0;
748
749 BottomSolver bottom_solver = BottomSolver::Default;
750 CFStrategy cf_strategy = CFStrategy::none;
751 int bottom_verbose = 0;
752 int bottom_maxiter = 200;
753 RT bottom_reltol = std::is_same<RT,double>() ? RT(1.e-4) : RT(1.e-3);
754 RT bottom_abstol = RT(-1.0);
755
757
759 bool m_algmg_active = false;
760 int hybrid_stall_window = 4;
761 RT hybrid_stall_rate = RT(0.8);
762 RT hybrid_divergence_factor = RT(10.0);
763 RT m_algmg_reltol = RT(0);
764 std::function<void(AlgMG<RT>&)> m_algmg_options;
765 std::unique_ptr<MLAlgMG> m_algmg_bottom;
766 std::unique_ptr<MLAlgMG> m_algmg_level0;
767
768 int final_fill_bc = 0;
769
770 MLLinOpT<MF>& linop;
771 int ncomp;
772 int namrlevs;
773 int finest_amr_lev;
774
775 bool linop_prepared = false;
776 Long solve_called = 0;
777
779 int do_nsolve = false;
780 int nsolve_grid_size = 16;
781 std::unique_ptr<MLLinOpT<MF>> ns_linop;
782 std::unique_ptr<MLMGT<MF>> ns_mlmg;
783 std::unique_ptr<MF> ns_sol;
784 std::unique_ptr<MF> ns_rhs;
785
786 std::string print_ident;
787
788 bool do_no_sync_gpu = false;
789
791#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
792 // Hypre::Interface hypre_interface = Hypre::Interface::structed;
793 // Hypre::Interface hypre_interface = Hypre::Interface::semi_structed;
794 Hypre::Interface hypre_interface = Hypre::Interface::ij;
795
796 std::unique_ptr<Hypre> hypre_solver;
797 std::unique_ptr<MLMGBndryT<MF>> hypre_bndry;
798 std::unique_ptr<HypreNodeLap> hypre_node_solver;
799
800 std::string hypre_options_namespace = "hypre";
801 bool hypre_old_default = HypreDefaults::old_default;
802 int hypre_relax_type = HypreDefaults::relax_type;
803 int hypre_relax_order = HypreDefaults::relax_order;
804 int hypre_num_sweeps = 2; // Sweeps on each level
805 Real hypre_strong_threshold = 0.25; // HYPRE default is 0.25
806#endif
807
809#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
810 std::unique_ptr<PETScABecLap> petsc_solver;
811 std::unique_ptr<MLMGBndryT<MF>> petsc_bndry;
812#endif
813
818 Vector<MF> sol;
819 Vector<MF> rhs;
821
822 Vector<int> sol_is_alias;
823
828 Vector<Vector<MF> > res;
829 Vector<Vector<MF> > cor;
830 Vector<Vector<MF> > cor_hold;
831 Vector<Vector<MF> > rescor;
833
834 enum timer_types { solve_time=0, iter_time, bottom_time, ntimers };
835 Vector<double> timer;
836
837 RT m_rhsnorm0 = RT(-1.0);
838 RT m_init_resnorm0 = RT(-1.0);
839 RT m_final_resnorm0 = RT(-1.0);
840 Vector<int> m_niters_cg;
841 Vector<RT> m_iter_fine_resnorm0; // Residual for each iteration at the finest level
842
852 void checkPoint (const Vector<MultiFab*>& a_sol,
853 const Vector<MultiFab const*>& a_rhs,
854 RT a_tol_rel, RT a_tol_abs, const char* a_file_name) const;
855
856};
857
858template <typename MF>
860 : linop(a_lp), ncomp(a_lp.getNComp()), namrlevs(a_lp.NAMRLevels()),
861 finest_amr_lev(a_lp.NAMRLevels()-1)
862{}
863
864template <typename MF> MLMGT<MF>::~MLMGT () = default;
865
866template <typename MF>
867void
869{
870 if (flag) {
871 norm_type = MLMGNormType::bnorm;
872 } else {
873 norm_type = MLMGNormType::greater;
874 }
875}
876
877template <typename MF>
878template <typename AMF>
879auto
880MLMGT<MF>::solve (std::initializer_list<AMF*> a_sol,
881 std::initializer_list<AMF const*> a_rhs,
882 RT a_tol_rel, RT a_tol_abs, const char* checkpoint_file) -> RT
883{
884 return solve(Vector<AMF*>(std::move(a_sol)),
885 Vector<AMF const*>(std::move(a_rhs)),
886 a_tol_rel, a_tol_abs, checkpoint_file);
887}
888
889template <typename MF>
890template <typename AMF>
891auto
893 RT a_tol_rel, RT a_tol_abs, const char* checkpoint_file) -> RT
894{
895 BL_PROFILE("MLMG::solve()");
896
897 // If requested with setNoGpuSync(true), run the whole solve in a
898 // single-stream, no-implicit-sync region. The RAII objects restore the
899 // previous state (also when an exception is thrown), and SyncAtExitOnly
900 // synchronizes the GPU streams on exit unless the caller was already in
901 // a NoSync region. The order matters:
902 // no_sync_region is destroyed last, i.e., after the single stream region
903 // has been popped, so that all streams are synchronized.
904 std::optional<Gpu::SyncAtExitOnly> no_sync_region;
905 std::optional<Gpu::SingleStreamRegion> single_stream_region;
906 if (do_no_sync_gpu) {
907 no_sync_region.emplace();
908 single_stream_region.emplace();
909 }
910
911 if constexpr (std::is_same<AMF,MultiFab>()) {
912 if (checkpoint_file != nullptr) {
913 checkPoint(a_sol, a_rhs, a_tol_rel, a_tol_abs, checkpoint_file);
914 }
915 }
916
917 if ((bottom_solver == BottomSolver::custom) && !linop.supportCustomBottomSolver()) {
918 bottom_solver = BottomSolver::Default;
919 }
920
921 if (bottom_solver == BottomSolver::Default) {
922 bottom_solver = linop.getDefaultBottomSolver();
923 }
924
925 // The assembled EB stencils support at most third order.
926 bool eb_linop = false;
927#ifdef AMREX_USE_EB
928 if constexpr (IsFabArray_v<AMF>) {
929 eb_linop = dynamic_cast<EBFArrayBoxFactory const*>(linop.Factory(0)) != nullptr;
930 }
931#endif
932 amrex::ignore_unused(eb_linop);
933
934#if (defined(AMREX_USE_HYPRE) || defined(AMREX_USE_PETSC)) && (AMREX_SPACEDIM > 1)
935 if constexpr (IsFabArray_v<AMF>) {
936 if (bottom_solver == BottomSolver::hypre || bottom_solver == BottomSolver::petsc) {
937 if (eb_linop) {
938 linop.setMaxOrder(std::min(3, linop.getMaxOrder()));
939 }
940#ifdef AMREX_USE_HYPRE
941 else if (bottom_solver == BottomSolver::hypre && hypre_interface != Hypre::Interface::ij) {
942 linop.setMaxOrder(std::min(3, linop.getMaxOrder())); // 4 needs the IJ interface
943 }
944#endif
945 }
946 }
947#endif
948 if (bottom_solver == BottomSolver::algmg || multigrid_type != MultigridType::geometric) {
949 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(linop.supportsAlgMG() && ncomp == 1,
950 "MLMG: AlgMG is not available for this operator (single-component cell and nodal operators only)");
951 // The algebraic correction has zero ghost cells.
952 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(cf_strategy != CFStrategy::ghostnodes || linop.getNGrow() == 0,
953 "MLMG: AlgMG does not support the ghostnodes strategy with ghost cells");
954 // The matrix must match the operator.
955 if (eb_linop) {
956 linop.setMaxOrder(std::min(3, linop.getMaxOrder()));
957 }
958 }
959
960 bool is_nsolve = linop.m_parent;
961
962 m_algmg_active = (multigrid_type == MultigridType::algebraic);
963 const bool hybrid = (multigrid_type == MultigridType::hybrid) && !is_nsolve;
964 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!(do_nsolve && multigrid_type != MultigridType::geometric),
965 "MLMG: NSolve requires the geometric multigrid type");
966 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!(hybrid && do_fixed_number_of_iters != 0),
967 "MLMG: the hybrid multigrid type needs a convergence test; as a preconditioner or with a fixed number of iterations use geometric or algebraic");
968
969 auto solve_start_time = amrex::second();
970
971 RT& composite_norminf = m_final_resnorm0;
972
973 m_niters_cg.clear();
974 m_iter_fine_resnorm0.clear();
975
976 prepareForSolve(a_sol, a_rhs);
977
978 computeMLResidual(finest_amr_lev);
979
980 bool local = true;
981 RT resnorm0 = MLResNormInf(finest_amr_lev, local);
982 RT rhsnorm0 = MLRhsNormInf(local);
983 if (!is_nsolve) {
985
986 if (verbose >= 1)
987 {
988 amrex::Print() << print_ident << "MLMG: Initial rhs = " << rhsnorm0 << "\n"
989 << print_ident << "MLMG: Initial residual (resid0) = " << resnorm0 << "\n";
990 }
991 }
992
993 m_init_resnorm0 = resnorm0;
994 m_rhsnorm0 = rhsnorm0;
995
996 if (!is_nsolve && !amrex::isfinite(std::max(resnorm0, rhsnorm0))) {
997 if ( throw_exception ) {
998 throw error("MLMG: rhs or initial residual is not finite.");
999 } else {
1000 amrex::Abort("MLMG: rhs or initial residual is not finite");
1001 }
1002 }
1003
1004 RT max_norm = resnorm0;
1005 std::string norm_name = "resid0";
1006 switch (norm_type) {
1008 if (rhsnorm0 >= resnorm0) {
1009 norm_name = "bnorm";
1010 max_norm = rhsnorm0;
1011 } else {
1012 norm_name = "resid0";
1013 max_norm = resnorm0;
1014 }
1015 break;
1017 norm_name = "bnorm";
1018 max_norm = rhsnorm0;
1019 break;
1021 norm_name = "resid0";
1022 max_norm = resnorm0;
1023 break;
1024 }
1025
1026 const RT res_target = std::max(a_tol_abs, std::max(a_tol_rel,std::numeric_limits<RT>::epsilon())*max_norm);
1027
1028 if (!is_nsolve && resnorm0 <= res_target) {
1029 composite_norminf = resnorm0;
1030 if (verbose >= 1) {
1031 amrex::Print() << print_ident << "MLMG: No iterations needed\n";
1032 }
1033 } else {
1034 auto iter_start_time = amrex::second();
1035 bool converged = false;
1036
1037 // Hybrid: keep the best iterate so far, starting with the initial guess.
1038 // The switch tests use the residual MLMG reports: the finest level
1039 // until it converges, then the composite one.
1040 Vector<MF> best_sol;
1041 Vector<RT> hybrid_norms;
1042 bool hybrid_composite = false; // the reported residual became the composite one
1043 RT best_norm = resnorm0;
1044 if (hybrid) {
1045 if (namrlevs > 1) { best_norm = ResNormInf(finest_amr_lev); }
1046 best_sol.resize(namrlevs);
1047 for (int alev = 0; alev < namrlevs; ++alev) {
1048 const IntVect ng = nGrowVect(sol[alev]);
1049 best_sol[alev] = linop.make(alev, 0, ng);
1050 LocalCopy(best_sol[alev], sol[alev], 0, 0, ncomp, ng);
1051 }
1052 }
1053
1054 int niters = do_fixed_number_of_iters ? do_fixed_number_of_iters : max_iters;
1055 for (int iter = 0; iter < niters; ++iter)
1056 {
1057 // AlgMG solving all of AMR level 0 gets the reduction still needed
1058 // (one AMR level; 2-norm for max-norm, MLMG checks its own residual)
1059 // or 1e-2 (several; the finer levels' V-cycles rarely do better),
1060 // if looser than bottom_reltol.
1061 m_algmg_reltol = bottom_reltol;
1062 if (!is_nsolve && !do_fixed_number_of_iters && (m_algmg_active || linop.NMGLevels(0) == 1)) {
1063 RT const resnorm = (iter == 0) ? resnorm0 : composite_norminf;
1064 m_algmg_reltol = std::max(bottom_reltol, (namrlevs == 1) ? res_target/resnorm : RT(1.e-2));
1065 }
1066
1067 oneIter(iter);
1068
1069 converged = false;
1070
1071 // Test convergence on the fine amr level
1072 computeResidual(finest_amr_lev);
1073
1074 if (is_nsolve) { continue; }
1075
1076 RT fine_norminf = ResNormInf(finest_amr_lev);
1077 m_iter_fine_resnorm0.push_back(fine_norminf);
1078 composite_norminf = fine_norminf;
1079 if (verbose >= 2) {
1080 amrex::Print() << print_ident << "MLMG: Iteration " << std::setw(3) << iter+1 << " Fine resid/"
1081 << norm_name << " = " << fine_norminf/max_norm << "\n";
1082 }
1083 bool fine_converged = (fine_norminf <= res_target);
1084
1085 if (namrlevs == 1 && fine_converged) {
1086 converged = true;
1087 } else if (fine_converged) {
1088 // finest level is converged, but we still need to test the coarse levels
1089 computeMLResidual(finest_amr_lev-1);
1090 RT crse_norminf = MLResNormInf(finest_amr_lev-1);
1091 if (verbose >= 2) {
1092 amrex::Print() << print_ident << "MLMG: Iteration " << std::setw(3) << iter+1
1093 << " Crse resid/" << norm_name << " = "
1094 << crse_norminf/max_norm << "\n";
1095 }
1096 converged = (crse_norminf <= res_target);
1097 composite_norminf = std::max(fine_norminf, crse_norminf);
1098 } else {
1099 converged = false;
1100 }
1101
1102 if (converged) {
1103 if (verbose >= 1) {
1104 amrex::Print() << print_ident << "MLMG: Final Iter. " << iter+1
1105 << " resid, resid/" << norm_name << " = "
1106 << composite_norminf << ", "
1107 << composite_norminf/max_norm << "\n";
1108 }
1109 break;
1110 } else {
1111 if (hybrid && !m_algmg_active) {
1112 if (fine_converged && !hybrid_composite) {
1113 // The reported residual now includes the coarse levels:
1114 // restart the comparisons from this iterate.
1115 hybrid_composite = true;
1116 hybrid_norms.clear();
1117 best_norm = composite_norminf;
1118 for (int alev = 0; alev < namrlevs; ++alev) {
1119 LocalCopy(best_sol[alev], sol[alev], 0, 0, ncomp, nGrowVect(sol[alev]));
1120 }
1121 }
1122 hybrid_norms.push_back(composite_norminf);
1123 std::string reason;
1124 bool do_switch = hybridShouldSwitch(hybrid_norms, max_norm,
1125 best_norm, reason);
1126 if (!do_switch && iter+1 == niters) {
1127 do_switch = true;
1128 reason = "not converged";
1129 }
1130 if (do_switch) {
1131 if (verbose >= 1) {
1132 amrex::Print() << print_ident << "MLMG: " << reason << " after "
1133 << iter+1 << " iterations, switching to AlgMG on AMR level 0\n";
1134 }
1135 if (!amrex::isfinite(composite_norminf) || composite_norminf > best_norm) {
1136 for (int alev = 0; alev < namrlevs; ++alev) {
1137 LocalCopy(sol[alev], best_sol[alev], 0, 0, ncomp, nGrowVect(sol[alev]));
1138 }
1139 computeResidual(finest_amr_lev);
1140 composite_norminf = best_norm;
1141 if (verbose >= 2) {
1142 amrex::Print() << print_ident << "MLMG: restarting from the best iterate, resid/"
1143 << norm_name << " = " << best_norm/max_norm << "\n";
1144 }
1145 }
1146 m_algmg_active = true;
1147 niters = iter + 1 + max_iters;
1148 continue;
1149 }
1150 if (composite_norminf < best_norm) {
1151 best_norm = composite_norminf;
1152 for (int alev = 0; alev < namrlevs; ++alev) {
1153 LocalCopy(best_sol[alev], sol[alev], 0, 0, ncomp, nGrowVect(sol[alev]));
1154 }
1155 }
1156 }
1157 if (composite_norminf > RT(1.e20)*max_norm || !amrex::isfinite(composite_norminf))
1158 {
1159 if (verbose > 0) {
1160 amrex::Print() << print_ident << "MLMG: Failing to converge after " << iter+1 << " iterations."
1161 << " resid, resid/" << norm_name << " = "
1162 << composite_norminf << ", "
1163 << composite_norminf/max_norm << "\n";
1164 }
1165
1166 if ( throw_exception ) {
1167 throw error("MLMG blew up.");
1168 } else {
1169 amrex::Abort("MLMG failing so lets stop here");
1170 }
1171 }
1172 }
1173 }
1174
1175 if (!converged && do_fixed_number_of_iters == 0) {
1176 if (verbose > 0) {
1177 amrex::Print() << print_ident << "MLMG: Failed to converge after " << niters << " iterations."
1178 << " resid, resid/" << norm_name << " = "
1179 << composite_norminf << ", "
1180 << composite_norminf/max_norm << "\n";
1181 }
1182
1183 if ( throw_exception ) {
1184 throw error("MLMG failed to converge.");
1185 } else {
1186 amrex::Abort("MLMG failed.");
1187 }
1188 }
1189 timer[iter_time] = amrex::second() - iter_start_time;
1190 }
1191
1192 linop.postSolve(GetVecOfPtrs(sol));
1193
1194 IntVect ng_back = final_fill_bc ? IntVect(1) : IntVect(0);
1195 if (linop.hasHiddenDimension()) {
1196 ng_back[linop.hiddenDirection()] = 0;
1197 }
1198 for (int alev = 0; alev < namrlevs; ++alev)
1199 {
1200 if (!sol_is_alias[alev]) {
1201 LocalCopy(*a_sol[alev], sol[alev], 0, 0, ncomp, ng_back);
1202 }
1203 }
1204
1205 timer[solve_time] = amrex::second() - solve_start_time;
1206 if (verbose >= 1) {
1207 ParallelReduce::Max<double>(timer.data(), timer.size(), 0,
1209 if (ParallelContext::MyProcSub() == 0)
1210 {
1211 amrex::AllPrint() << print_ident << "MLMG: Timers: Solve = " << timer[solve_time]
1212 << " Iter = " << timer[iter_time]
1213 << " Bottom = " << timer[bottom_time] << "\n";
1214 }
1215 }
1216
1217 ++solve_called;
1218
1219 return composite_norminf;
1220}
1221
1222template <typename MF>
1223auto
1225 RT a_tol_rel, RT a_tol_abs) -> RT
1226{
1227 precond_mode = true;
1228 std::swap(max_precond_iters, do_fixed_number_of_iters);
1229 linop.beginPrecondBC();
1230
1231 auto r = solve(a_sol, a_rhs, a_tol_rel, a_tol_abs);
1232
1233 linop.endPrecondBC();
1234 std::swap(max_precond_iters, do_fixed_number_of_iters);
1235 precond_mode = false;
1236
1237 return r;
1238}
1239
1240template <typename MF>
1241void
1243{
1244 for (int alev = finest_amr_lev; alev >= 0; --alev) {
1245 const MF* crse_bcdata = (alev > 0) ? a_sol[alev-1] : nullptr;
1246 linop.prepareForFluxes(alev, crse_bcdata);
1247 }
1248}
1249
1250template <typename MF>
1251template <typename AMF>
1252void
1254{
1255 BL_PROFILE("MLMG::getGradSolution()");
1256 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1257 if constexpr (std::is_same<AMF,MF>()) {
1258 linop.compGrad(alev, a_grad_sol[alev], sol[alev], a_loc);
1259 } else {
1260 Array<MF,AMREX_SPACEDIM> grad_sol;
1261 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1262 auto const& amf = *(a_grad_sol[alev][idim]);
1263 grad_sol[idim].define(boxArray(amf), DistributionMap(amf), ncomp, 0);
1264 }
1265 linop.compGrad(alev, GetArrOfPtrs(grad_sol), sol[alev], a_loc);
1266 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1267 LocalCopy(*a_grad_sol[alev][idim], grad_sol[idim], 0, 0, ncomp, IntVect(0));
1268 }
1269 }
1270 }
1271}
1272
1273template <typename MF>
1274template <typename AMF>
1275void
1276MLMGT<MF>::getGradSolution (std::initializer_list<Array<AMF*,AMREX_SPACEDIM>> a_grad_sol, Location a_loc)
1277{
1278 getGradSolution(Vector<Array<AMF*,AMREX_SPACEDIM>>(std::move(a_grad_sol)), a_loc);
1279}
1280
1281template <typename MF>
1282template <typename AMF>
1283void
1285 Location a_loc)
1286{
1287 if (!linop.isCellCentered()) {
1288 amrex::Abort("Calling wrong getFluxes for nodal solver");
1289 }
1290
1291 AMREX_ASSERT(sol.size() == a_flux.size());
1292
1293 if constexpr (std::is_same<AMF,MF>()) {
1294 getFluxes(a_flux, GetVecOfPtrs(sol), a_loc);
1295 } else {
1296 Vector<Array<MF,AMREX_SPACEDIM>> fluxes(namrlevs);
1297 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1298 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1299 auto const& amf = *(a_flux[ilev][idim]);
1300 fluxes[ilev][idim].define(boxArray(amf), DistributionMap(amf), ncomp, 0);
1301 }
1302 }
1303 getFluxes(GetVecOfArrOfPtrs(fluxes), GetVecOfPtrs(sol), a_loc);
1304 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1305 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1306 LocalCopy(*a_flux[ilev][idim], fluxes[ilev][idim], 0, 0, ncomp, IntVect(0));
1307 }
1308 }
1309 }
1310}
1311
1312template <typename MF>
1313template <typename AMF>
1314void
1316 Location a_loc)
1317{
1318 getFluxes(Vector<Array<AMF*,AMREX_SPACEDIM>>(std::move(a_flux)), a_loc);
1319}
1320
1321template <typename MF>
1322template <typename AMF>
1323void
1325 const Vector<AMF*>& a_sol, Location a_loc)
1326{
1327 BL_PROFILE("MLMG::getFluxes()");
1328
1329 if (!linop.isCellCentered()) {
1330 amrex::Abort("Calling wrong getFluxes for nodal solver");
1331 }
1332
1333 if constexpr (std::is_same<AMF,MF>()) {
1334 linop.getFluxes(a_flux, a_sol, a_loc);
1335 } else {
1336 Vector<Array<MF,AMREX_SPACEDIM>> fluxes(namrlevs);
1337 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1338 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1339 auto const& amf = *(a_flux[ilev][idim]);
1340 fluxes[ilev][idim].define(boxArray(amf), DistributionMap(amf), ncomp, 0);
1341 }
1342 LocalCopy(sol[ilev], *a_sol[ilev], 0, 0, ncomp, nGrowVect(sol[ilev]));
1343 }
1344 linop.getFluxes(GetVecOfArrOfPtrs(fluxes), GetVecOfPtrs(sol), a_loc);
1345 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1346 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1347 LocalCopy(*a_flux[ilev][idim], fluxes[ilev][idim], 0, 0, ncomp, IntVect(0));
1348 }
1349 }
1350 }
1351}
1352
1353template <typename MF>
1354template <typename AMF>
1355void
1357 std::initializer_list<AMF*> a_sol, Location a_loc)
1358{
1359 getFluxes(Vector<Array<AMF*,AMREX_SPACEDIM>>(std::move(a_flux)),
1360 Vector<AMF*>(std::move(a_sol)), a_loc);
1361}
1362
1363template <typename MF>
1364template <typename AMF>
1365void
1367{
1368 AMREX_ASSERT(sol.size() == a_flux.size());
1369 if constexpr (std::is_same<AMF,MF>()) {
1370 getFluxes(a_flux, GetVecOfPtrs(sol), a_loc);
1371 } else {
1372 Vector<MF> fluxes(namrlevs);
1373 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1374 auto const& amf = *a_flux[ilev];
1375 fluxes[ilev].define(boxArray(amf), DistributionMap(amf), AMREX_SPACEDIM, 0);
1376 }
1377 getFluxes(GetVecOfPtrs(fluxes), GetVecOfPtrs(sol), a_loc);
1378 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1379 LocalCopy(*a_flux[ilev], fluxes[ilev], 0, 0, AMREX_SPACEDIM, IntVect(0));
1380 }
1381 }
1382}
1383
1384template <typename MF>
1385template <typename AMF>
1386void
1387MLMGT<MF>::getFluxes (std::initializer_list<AMF*> a_flux, Location a_loc)
1388{
1389 getFluxes(Vector<AMF*>(std::move(a_flux)), a_loc);
1390}
1391
1392template <typename MF>
1393template <typename AMF>
1394void
1396 const Vector<AMF*>& a_sol, Location /*a_loc*/)
1397{
1398 AMREX_ASSERT(nComp(*a_flux[0]) >= AMREX_SPACEDIM);
1399
1400 if constexpr (! std::is_same<AMF,MF>()) {
1401 for (int alev = 0; alev < namrlevs; ++alev) {
1402 LocalCopy(sol[alev], *a_sol[alev], 0, 0, ncomp, nGrowVect(sol[alev]));
1403 }
1404 }
1405
1406 if (linop.isCellCentered())
1407 {
1408 Vector<Array<MF,AMREX_SPACEDIM> > ffluxes(namrlevs);
1409 for (int alev = 0; alev < namrlevs; ++alev) {
1410 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1411 const int mglev = 0;
1412 int nghost = 0;
1413 if (cf_strategy == CFStrategy::ghostnodes) { nghost = linop.getNGrow(alev); }
1414 ffluxes[alev][idim].define(amrex::convert(linop.m_grids[alev][mglev],
1416 linop.m_dmap[alev][mglev], ncomp, nghost, MFInfo(),
1417 *linop.m_factory[alev][mglev]);
1418 }
1419 }
1420 if constexpr (std::is_same<AMF,MF>()) {
1421 getFluxes(amrex::GetVecOfArrOfPtrs(ffluxes), a_sol, Location::FaceCenter);
1422 } else {
1423 getFluxes(amrex::GetVecOfArrOfPtrs(ffluxes), GetVecOfPtrs(sol), Location::FaceCenter);
1424 }
1425 for (int alev = 0; alev < namrlevs; ++alev) {
1426#ifdef AMREX_USE_EB
1427 EB_average_face_to_cellcenter(*a_flux[alev], 0, amrex::GetArrOfConstPtrs(ffluxes[alev]));
1428#else
1429 average_face_to_cellcenter(*a_flux[alev], 0, amrex::GetArrOfConstPtrs(ffluxes[alev]));
1430#endif
1431 }
1432
1433 } else {
1434 if constexpr (std::is_same<AMF,MF>()) {
1435 linop.getFluxes(a_flux, a_sol);
1436 } else {
1437 Vector<MF> fluxes(namrlevs);
1438 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1439 auto const& amf = *a_flux[ilev];
1440 fluxes[ilev].define(boxArray(amf), DistributionMap(amf), AMREX_SPACEDIM, 0);
1441 }
1442 linop.getFluxes(GetVecOfPtrs(fluxes), GetVecOfPtrs(sol));
1443 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1444 LocalCopy(*a_flux[ilev], fluxes[ilev], 0, 0, AMREX_SPACEDIM, IntVect(0));
1445 }
1446 }
1447 }
1448}
1449
1450template <typename MF>
1451template <typename AMF>
1452void
1453MLMGT<MF>::getFluxes (std::initializer_list<AMF*> a_flux,
1454 std::initializer_list<AMF*> a_sol, Location a_loc)
1455{
1456 getFluxes(Vector<AMF*>(std::move(a_flux)),
1457 Vector<AMF*>(std::move(a_sol)), a_loc);
1458}
1459
1460#ifdef AMREX_USE_EB
1461template <typename MF>
1462void
1464{
1465 if (!linop.isCellCentered()) {
1466 amrex::Abort("getEBFluxes is for cell-centered only");
1467 }
1468
1469 AMREX_ASSERT(sol.size() == a_eb_flux.size());
1470 getEBFluxes(a_eb_flux, GetVecOfPtrs(sol));
1471}
1472
1473template <typename MF>
1474void
1475MLMGT<MF>::getEBFluxes (const Vector<MF*>& a_eb_flux, const Vector<MF*>& a_sol)
1476{
1477 BL_PROFILE("MLMG::getEBFluxes()");
1478
1479 if (!linop.isCellCentered()) {
1480 amrex::Abort("getEBFluxes is for cell-centered only");
1481 }
1482
1483 linop.getEBFluxes(a_eb_flux, a_sol);
1484}
1485#endif
1486
1487template <typename MF>
1488void
1490 const Vector<MF const*>& a_rhs)
1491{
1492 BL_PROFILE("MLMG::compResidual()");
1493
1494 IntVect ng_sol(1);
1495 if (linop.hasHiddenDimension()) { ng_sol[linop.hiddenDirection()] = 0; }
1496
1497 sol.resize(namrlevs);
1498 sol_is_alias.resize(namrlevs,true);
1499 for (int alev = 0; alev < namrlevs; ++alev)
1500 {
1501 if (cf_strategy == CFStrategy::ghostnodes || nGrowVect(*a_sol[alev]) == ng_sol)
1502 {
1503 sol[alev] = linop.makeAlias(*a_sol[alev]);
1504 sol_is_alias[alev] = true;
1505 }
1506 else
1507 {
1508 if (sol_is_alias[alev])
1509 {
1510 sol[alev] = linop.make(alev, 0, ng_sol);
1511 sol_is_alias[alev] = false;
1512 }
1513 LocalCopy(sol[alev], *a_sol[alev], 0, 0, ncomp, IntVect(0));
1514 }
1515 }
1516
1517 prepareLinOp();
1518
1519
1520 for (int alev = finest_amr_lev; alev >= 0; --alev) {
1521 const MF* crse_bcdata = (alev > 0) ? &(sol[alev-1]) : nullptr;
1522 const MF* prhs = a_rhs[alev];
1523#if (AMREX_SPACEDIM != 3)
1524 int nghost = (cf_strategy == CFStrategy::ghostnodes) ? linop.getNGrow(alev) : 0;
1525 MF rhstmp(boxArray(*prhs), DistributionMap(*prhs), ncomp, nghost,
1526 MFInfo(), *linop.Factory(alev));
1527 LocalCopy(rhstmp, *prhs, 0, 0, ncomp, IntVect(nghost));
1528 linop.applyMetricTerm(alev, 0, rhstmp);
1529 linop.unimposeNeumannBC(alev, rhstmp);
1530 linop.applyInhomogNeumannTerm(alev, rhstmp);
1531 prhs = &rhstmp;
1532#endif
1533 linop.solutionResidual(alev, *a_res[alev], sol[alev], *prhs, crse_bcdata);
1534 if (alev < finest_amr_lev) {
1535 linop.reflux(alev, *a_res[alev], sol[alev], *prhs,
1536 *a_res[alev+1], sol[alev+1], *a_rhs[alev+1]);
1537 if (linop.isCellCentered()) {
1538#ifdef AMREX_USE_EB
1539 EB_average_down(*a_res[alev+1], *a_res[alev], 0, ncomp, linop.AMRRefRatioVect(alev));
1540#else
1541 average_down(*a_res[alev+1], *a_res[alev], 0, ncomp, linop.AMRRefRatioVect(alev));
1542#endif
1543 }
1544 }
1545 }
1546
1547
1548#if (AMREX_SPACEDIM != 3)
1549 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1550 linop.unapplyMetricTerm(alev, 0, *a_res[alev]);
1551 }
1552#endif
1553}
1554
1555template <typename MF>
1556void
1558{
1559 BL_PROFILE("MLMG::apply()");
1560
1561 Vector<MF*> in(namrlevs);
1562 Vector<MF> in_raii(namrlevs);
1563 Vector<MF> rh(namrlevs);
1564 int nghost = 0;
1565 IntVect ng_sol(1);
1566 if (linop.hasHiddenDimension()) { ng_sol[linop.hiddenDirection()] = 0; }
1567
1568 for (int alev = 0; alev < namrlevs; ++alev)
1569 {
1570 if (cf_strategy == CFStrategy::ghostnodes)
1571 {
1572 nghost = linop.getNGrow(alev);
1573 in[alev] = a_in[alev];
1574 }
1575 else if (nGrowVect(*a_in[alev]) == ng_sol)
1576 {
1577 in[alev] = a_in[alev];
1578 }
1579 else
1580 {
1581 IntVect ng = ng_sol;
1582 if (cf_strategy == CFStrategy::ghostnodes) { ng = IntVect(nghost); }
1583 in_raii[alev] = linop.make(alev, 0, ng,
1584 MFInfo().SetArena(The_Async_Arena()));
1585 LocalCopy(in_raii[alev], *a_in[alev], 0, 0, ncomp, IntVect(nghost));
1586 in[alev] = &(in_raii[alev]);
1587 }
1588 rh[alev] = linop.make(alev, 0, IntVect(nghost),
1589 MFInfo().SetArena(The_Async_Arena()));
1590 setVal(rh[alev], RT(0.0));
1591 }
1592
1593 prepareLinOp();
1594
1595 for (int alev = 0; alev < namrlevs; ++alev) {
1596 linop.applyInhomogNeumannTerm(alev, rh[alev]);
1597 }
1598
1599
1600 for (int alev = finest_amr_lev; alev >= 0; --alev) {
1601 const MF* crse_bcdata = (alev > 0) ? in[alev-1] : nullptr;
1602 linop.solutionResidual(alev, *out[alev], *in[alev], rh[alev], crse_bcdata);
1603 if (alev < finest_amr_lev) {
1604 linop.reflux(alev, *out[alev], *in[alev], rh[alev],
1605 *out[alev+1], *in[alev+1], rh[alev+1]);
1606 if (linop.isCellCentered()) {
1607 if constexpr (IsMultiFabLike_v<MF>) {
1608#ifdef AMREX_USE_EB
1609 EB_average_down(*out[alev+1], *out[alev], 0, nComp(*out[alev]), linop.AMRRefRatioVect(alev));
1610#else
1611 average_down(*out[alev+1], *out[alev], 0, nComp(*out[alev]), linop.AMRRefRatioVect(alev));
1612#endif
1613 } else {
1614 amrex::Abort("MLMG: TODO average_down for non-MultiFab");
1615 }
1616 }
1617 }
1618 }
1619
1620#if (AMREX_SPACEDIM != 3)
1621 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1622 linop.unapplyMetricTerm(alev, 0, *out[alev]);
1623 }
1624#endif
1625
1626 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1627 if (cf_strategy == CFStrategy::ghostnodes) { nghost = linop.getNGrow(alev); }
1628 Scale(*out[alev], RT(-1), 0, nComp(*out[alev]), nghost);
1629 }
1630}
1631
1632template <typename MF>
1633void
1635{
1636 precond_mode = true;
1637 linop.beginPrecondBC();
1638 apply(out, in);
1639 linop.endPrecondBC();
1640 precond_mode = false;
1641}
1642
1643template <typename MF>
1644template <typename AMF>
1645void
1647{
1648 BL_PROFILE("MLMG::prepareForSolve()");
1649
1650 AMREX_ASSERT(namrlevs <= a_sol.size());
1651 AMREX_ASSERT(namrlevs <= a_rhs.size());
1652
1653 timer.assign(ntimers, 0.0);
1654
1655 IntVect ng_rhs(0);
1656 IntVect ng_sol(1);
1657 if (linop.hasHiddenDimension()) { ng_sol[linop.hiddenDirection()] = 0; }
1658
1659 linop.setPrintIndentation(print_ident);
1660 if (!linop_prepared) {
1661 linop.prepareForSolve();
1662 AMREX_ASSERT(!linop.m_mg_deferred || linop.m_mg_built);
1663 linop_prepared = true;
1664 } else if (linop.needsUpdate()) {
1665 linop.update();
1666
1667 m_algmg_bottom.reset();
1668 m_algmg_level0.reset();
1669
1670#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
1671 hypre_solver.reset();
1672 hypre_bndry.reset();
1673 hypre_node_solver.reset();
1674#endif
1675
1676#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
1677 petsc_solver.reset();
1678 petsc_bndry.reset();
1679#endif
1680 }
1681
1682 sol.resize(namrlevs);
1683 sol_is_alias.resize(namrlevs,false);
1684 for (int alev = 0; alev < namrlevs; ++alev)
1685 {
1686 if (cf_strategy == CFStrategy::ghostnodes)
1687 {
1688 if constexpr (std::is_same<AMF,MF>()) {
1689 sol[alev] = linop.makeAlias(*a_sol[alev]);
1690 sol_is_alias[alev] = true;
1691 } else {
1692 amrex::Abort("Type conversion not supported for CFStrategy::ghostnodes");
1693 }
1694 }
1695 else
1696 {
1697 bool alias_made = false;
1698 if (nGrowVect(*a_sol[alev]) == ng_sol) {
1699 if constexpr (std::is_same<AMF,MF>()) {
1700 sol[alev] = linop.makeAlias(*a_sol[alev]);
1701 sol_is_alias[alev] = true;
1702 alias_made = true;
1703 }
1704 }
1705 if (!alias_made) {
1706 if (sol_is_alias[alev] || !solve_called) {
1707 sol[alev] = linop.make(alev, 0, ng_sol);
1708 sol_is_alias[alev] = false;
1709 }
1710 LocalCopy(sol[alev], *a_sol[alev], 0, 0, ncomp, IntVect(0));
1711 setBndry(sol[alev], RT(0.0), 0, ncomp);
1712 }
1713 }
1714 }
1715
1716 rhs.resize(namrlevs);
1717 for (int alev = 0; alev < namrlevs; ++alev)
1718 {
1719 if (cf_strategy == CFStrategy::ghostnodes) { ng_rhs = IntVect(linop.getNGrow(alev)); }
1720 if (!solve_called) {
1721 rhs[alev] = linop.make(alev, 0, ng_rhs);
1722 }
1723 LocalCopy(rhs[alev], *a_rhs[alev], 0, 0, ncomp, ng_rhs);
1724 linop.applyMetricTerm(alev, 0, rhs[alev]);
1725 linop.unimposeNeumannBC(alev, rhs[alev]);
1726 linop.applyInhomogNeumannTerm(alev, rhs[alev]);
1727 linop.applyOverset(alev, rhs[alev]);
1728 if ( ! precond_mode) {
1729 bool r = linop.scaleRHS(alev, &(rhs[alev]));
1731 }
1732
1733#ifdef AMREX_USE_EB
1734 const auto *factory = dynamic_cast<EBFArrayBoxFactory const*>(linop.Factory(alev));
1735 if (factory && !factory->isAllRegular()) {
1736 if constexpr (std::is_same<MF,MultiFab>()) {
1737 EB_set_covered(rhs[alev], 0, ncomp, 0, RT(0.0));
1738 EB_set_covered(sol[alev], 0, ncomp, 0, RT(0.0));
1739 } else {
1740 amrex::Abort("TODO: MLMG with EB only works with MultiFab");
1741 }
1742 }
1743#endif
1744 }
1745
1746 for (int falev = finest_amr_lev; falev > 0; --falev)
1747 {
1748 linop.averageDownSolutionRHS(falev-1, sol[falev-1], rhs[falev-1], sol[falev], rhs[falev]);
1749 }
1750
1751 // Nodal solvers: the fine coarse/fine nodes from the coarse solution
1752 if (cf_strategy != CFStrategy::ghostnodes) {
1753 for (int falev = 1; falev <= finest_amr_lev; ++falev) {
1754 linop.interpCoarseFineSolution(falev, sol[falev], sol[falev-1]);
1755 }
1756 }
1757
1758 // enforce solvability if appropriate
1759 if (linop.isSingular(0) && linop.getEnforceSingularSolvable())
1760 {
1761 makeSolvable();
1762 }
1763
1764 IntVect ng = linop.getNGrowVectRestriction();
1765 if (cf_strategy == CFStrategy::ghostnodes) { ng = ng_rhs; }
1766 if (!solve_called) {
1767 linop.make(res, ng);
1768 linop.make(rescor, ng);
1769 }
1770 for (int alev = 0; alev <= finest_amr_lev; ++alev)
1771 {
1772 const int nmglevs = linop.NMGLevels(alev);
1773 for (int mglev = 0; mglev < nmglevs; ++mglev)
1774 {
1775 setVal(res [alev][mglev], RT(0.0));
1776 setVal(rescor[alev][mglev], RT(0.0));
1777 }
1778 }
1779
1780 if (cf_strategy != CFStrategy::ghostnodes) { ng = ng_sol; }
1781 cor.resize(namrlevs);
1782 for (int alev = 0; alev <= finest_amr_lev; ++alev)
1783 {
1784 const int nmglevs = linop.NMGLevels(alev);
1785 cor[alev].resize(nmglevs);
1786 for (int mglev = 0; mglev < nmglevs; ++mglev)
1787 {
1788 if (!solve_called) {
1789 IntVect _ng = ng;
1790 if (cf_strategy == CFStrategy::ghostnodes) { _ng=IntVect(linop.getNGrow(alev,mglev)); }
1791 cor[alev][mglev] = linop.make(alev, mglev, _ng);
1792 }
1793 setVal(cor[alev][mglev], RT(0.0));
1794 }
1795 }
1796
1797 cor_hold.resize(std::max(namrlevs-1,1));
1798 {
1799 const int alev = 0;
1800 const int nmglevs = linop.NMGLevels(alev);
1801 cor_hold[alev].resize(nmglevs);
1802 for (int mglev = 0; mglev < nmglevs-1; ++mglev)
1803 {
1804 if (!solve_called) {
1805 IntVect _ng = ng;
1806 if (cf_strategy == CFStrategy::ghostnodes) { _ng=IntVect(linop.getNGrow(alev,mglev)); }
1807 cor_hold[alev][mglev] = linop.make(alev, mglev, _ng);
1808 }
1809 setVal(cor_hold[alev][mglev], RT(0.0));
1810 }
1811 }
1812 for (int alev = 1; alev < finest_amr_lev; ++alev)
1813 {
1814 cor_hold[alev].resize(1);
1815 if (!solve_called) {
1816 IntVect _ng = ng;
1817 if (cf_strategy == CFStrategy::ghostnodes) { _ng=IntVect(linop.getNGrow(alev)); }
1818 cor_hold[alev][0] = linop.make(alev, 0, _ng);
1819 }
1820 setVal(cor_hold[alev][0], RT(0.0));
1821 }
1822
1823 if (linop.m_parent // no embedded N-Solve
1824 || !linop.supportNSolve())
1825 {
1826 do_nsolve = false;
1827 }
1828
1829 if (do_nsolve && ns_linop == nullptr)
1830 {
1832 }
1833
1834 if (verbose >= 2) {
1835 amrex::Print() << print_ident << "MLMG: # of AMR levels: " << namrlevs << "\n"
1836 << print_ident << " # of MG levels on the coarsest AMR level: " << linop.NMGLevels(0)
1837 << "\n";
1838 if (ns_linop) {
1839 amrex::Print() << print_ident << " # of MG levels in N-Solve: " << ns_linop->NMGLevels(0) << "\n"
1840 << print_ident << " # of grids in N-Solve: " << ns_linop->m_grids[0][0].size() << "\n";
1841 }
1842 }
1843}
1844
1845template <typename MF>
1846void
1848{
1849 linop.setPrintIndentation(print_ident);
1850 if (!linop_prepared) {
1851 linop.prepareForSolve();
1852 AMREX_ASSERT(!linop.m_mg_deferred || linop.m_mg_built);
1853 linop_prepared = true;
1854 } else if (linop.needsUpdate()) {
1855 linop.update();
1856
1857 m_algmg_bottom.reset();
1858 m_algmg_level0.reset();
1859
1860#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
1861 hypre_solver.reset();
1862 hypre_bndry.reset();
1863 hypre_node_solver.reset();
1864#endif
1865
1866#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
1867 petsc_solver.reset();
1868 petsc_bndry.reset();
1869#endif
1870 }
1871}
1872
1873template <typename MF>
1874void
1876{
1877 prepareLinOp();
1878 linop.preparePrecond();
1879}
1880
1881template <typename MF>
1882void
1884{
1885 if constexpr (IsMultiFabLike_v<MF>) {
1886 ns_linop = linop.makeNLinOp(nsolve_grid_size);
1887
1888 int nghost = 0;
1889 if (cf_strategy == CFStrategy::ghostnodes) { nghost = linop.getNGrow(); }
1890
1891 const BoxArray& ba = (*ns_linop).m_grids[0][0];
1892 const DistributionMapping& dm =(*ns_linop).m_dmap[0][0];
1893
1894 int ng = 1;
1895 if (cf_strategy == CFStrategy::ghostnodes) { ng = nghost; }
1896 ns_sol = std::make_unique<MF>(ba, dm, ncomp, ng, MFInfo(), *(ns_linop->Factory(0,0)));
1897 ng = 0;
1898 if (cf_strategy == CFStrategy::ghostnodes) { ng = nghost; }
1899 ns_rhs = std::make_unique<MF>(ba, dm, ncomp, ng, MFInfo(), *(ns_linop->Factory(0,0)));
1900 setVal(*ns_sol, RT(0.0));
1901 setVal(*ns_rhs, RT(0.0));
1902
1903 ns_linop->setLevelBC(0, ns_sol.get());
1904
1905 ns_mlmg = std::make_unique<MLMGT<MF>>(*ns_linop);
1906 ns_mlmg->setVerbose(0);
1907 ns_mlmg->setFixedIter(1);
1908 ns_mlmg->setMaxFmgIter(20);
1909 ns_mlmg->setBottomSolver(BottomSolver::smoother);
1910 }
1911}
1912
1913// in : Residual (res) on the finest AMR level
1914// out : sol on all AMR levels
1915template <typename MF>
1916void MLMGT<MF>::oneIter (int iter)
1917{
1918 BL_PROFILE("MLMG::oneIter()");
1919
1920 for (int alev = finest_amr_lev; alev > 0; --alev)
1921 {
1922 miniCycle(alev);
1923
1924 IntVect nghost(0);
1925 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(alev)); }
1926 LocalAdd(sol[alev], cor[alev][0], 0, 0, ncomp, nghost);
1927
1928 // compute residual for the coarse AMR level
1929 computeResWithCrseSolFineCor(alev-1,alev);
1930
1931 if (alev != finest_amr_lev) {
1932 std::swap(cor_hold[alev][0], cor[alev][0]); // save it for the up cycle
1933 }
1934 }
1935
1936 // coarsest amr level
1937 {
1938 // enforce solvability if appropriate
1939 if (linop.isSingular(0) && linop.getEnforceSingularSolvable())
1940 {
1941 makeSolvable(0,0,res[0][0]);
1942 }
1943
1944 if (m_algmg_active) {
1945 if constexpr (std::is_same<MF,MultiFab>()) {
1947 } else {
1948 amrex::Abort("MLMG: AlgMG is only supported for MultiFab");
1949 }
1950 } else if (iter < max_fmg_iters) {
1951 mgFcycle();
1952 } else {
1953 mgVcycle(0, 0);
1954 }
1955
1956 IntVect nghost(0);
1957 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(0)); }
1958 LocalAdd(sol[0], cor[0][0], 0, 0, ncomp, nghost);
1959 }
1960
1961 for (int alev = 1; alev <= finest_amr_lev; ++alev)
1962 {
1963 // (Fine AMR correction) = I(Coarse AMR correction)
1964 interpCorrection(alev);
1965 // Known overset cells must not receive the interpolated correction.
1966 linop.applyOverset(alev, cor[alev][0]);
1967
1968 IntVect nghost(0);
1969 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(alev)); }
1970 LocalAdd(sol[alev], cor[alev][0], 0, 0, ncomp, nghost);
1971
1972 if (alev != finest_amr_lev) {
1973 LocalAdd(cor_hold[alev][0], cor[alev][0], 0, 0, ncomp, nghost);
1974 }
1975
1976 // Update fine AMR level correction
1978
1979 miniCycle(alev);
1980
1981 LocalAdd(sol[alev], cor[alev][0], 0, 0, ncomp, nghost);
1982
1983 if (alev != finest_amr_lev) {
1984 LocalAdd(cor[alev][0], cor_hold[alev][0], 0, 0, ncomp, nghost);
1985 }
1986 }
1987
1988 linop.averageDownAndSync(sol);
1989}
1990
1991template <typename MF>
1992void
1994{
1995 BL_PROFILE("MLMG::miniCycle()");
1996 const int mglev = 0;
1997 mgVcycle(amrlev, mglev);
1998}
1999
2000// in : Residual (res)
2001// out : Correction (cor) from bottom to this function's local top
2002template <typename MF>
2003void
2004MLMGT<MF>::mgVcycle (int amrlev, int mglev_top)
2005{
2006 BL_PROFILE("MLMG::mgVcycle()");
2007
2008 const int mglev_bottom = linop.NMGLevels(amrlev) - 1;
2009
2010 for (int mglev = mglev_top; mglev < mglev_bottom; ++mglev)
2011 {
2012 BL_PROFILE_VAR("MLMG::mgVcycle_down::"+std::to_string(mglev), blp_mgv_down_lev);
2013
2014 if (verbose >= 4)
2015 {
2016 RT norm = norminf(res[amrlev][mglev],0,ncomp,IntVect(0));
2017 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
2018 << " DN: Norm before smooth " << norm << "\n";
2019 }
2020
2021 setVal(cor[amrlev][mglev], RT(0.0));
2022 bool skip_fillboundary = true;
2023 linop.smooth(amrlev, mglev, cor[amrlev][mglev], res[amrlev][mglev], skip_fillboundary, nu1);
2024
2025 // rescor = res - L(cor)
2026 computeResOfCorrection(amrlev, mglev);
2027
2028 if (verbose >= 4)
2029 {
2030 RT norm = norminf(rescor[amrlev][mglev],0,ncomp,IntVect(0));
2031 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
2032 << " DN: Norm after smooth " << norm << "\n";
2033 }
2034
2035 // res_crse = R(rescor_fine); this provides res/b to the level below
2036 linop.restriction(amrlev, mglev+1, res[amrlev][mglev+1], rescor[amrlev][mglev]);
2037 }
2038
2039 BL_PROFILE_VAR("MLMG::mgVcycle_bottom", blp_bottom);
2040 if (amrlev == 0)
2041 {
2042 if (verbose >= 4)
2043 {
2044 RT norm = norminf(res[amrlev][mglev_bottom],0,ncomp,IntVect(0));
2045 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
2046 << " DN: Norm before bottom " << norm << "\n";
2047 }
2048 bottomSolve();
2049 if (verbose >= 4)
2050 {
2051 computeResOfCorrection(amrlev, mglev_bottom);
2052 RT norm = norminf(rescor[amrlev][mglev_bottom],0,ncomp,IntVect(0));
2053 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
2054 << " UP: Norm after bottom " << norm << "\n";
2055 }
2056 }
2057 else
2058 {
2059 if (verbose >= 4)
2060 {
2061 RT norm = norminf(res[amrlev][mglev_bottom],0,ncomp,IntVect(0));
2062 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
2063 << " Norm before smooth " << norm << "\n";
2064 }
2065 setVal(cor[amrlev][mglev_bottom], RT(0.0));
2066 bool skip_fillboundary = true;
2067 linop.smooth(amrlev, mglev_bottom, cor[amrlev][mglev_bottom],
2068 res[amrlev][mglev_bottom], skip_fillboundary, nu1);
2069 if (verbose >= 4)
2070 {
2071 computeResOfCorrection(amrlev, mglev_bottom);
2072 RT norm = norminf(rescor[amrlev][mglev_bottom],0,ncomp,IntVect(0));
2073 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
2074 << " Norm after smooth " << norm << "\n";
2075 }
2076 }
2077 BL_PROFILE_VAR_STOP(blp_bottom);
2078
2079 for (int mglev = mglev_bottom-1; mglev >= mglev_top; --mglev)
2080 {
2081 BL_PROFILE_VAR("MLMG::mgVcycle_up::"+std::to_string(mglev), blp_mgv_up_lev);
2082 // cor_fine += I(cor_crse)
2083 addInterpCorrection(amrlev, mglev);
2084 if (verbose >= 4)
2085 {
2086 computeResOfCorrection(amrlev, mglev);
2087 RT norm = norminf(rescor[amrlev][mglev],0,ncomp,IntVect(0));
2088 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
2089 << " UP: Norm before smooth " << norm << "\n";
2090 }
2091 linop.smooth(amrlev, mglev, cor[amrlev][mglev], res[amrlev][mglev], false, nu2);
2092
2093 if (cf_strategy == CFStrategy::ghostnodes) { computeResOfCorrection(amrlev, mglev); }
2094
2095 if (verbose >= 4)
2096 {
2097 computeResOfCorrection(amrlev, mglev);
2098 RT norm = norminf(rescor[amrlev][mglev],0,ncomp,IntVect(0));
2099 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
2100 << " UP: Norm after smooth " << norm << "\n";
2101 }
2102 }
2103}
2104
2105// FMG cycle on the coarsest AMR level.
2106// in: Residual on the top MG level (i.e., 0)
2107// out: Correction (cor) on all MG levels
2108template <typename MF>
2109void
2111{
2112 BL_PROFILE("MLMG::mgFcycle()");
2113
2114#ifdef AMREX_USE_EB
2115 auto* pf = linop.Factory(0);
2116 auto is_all_regular = [pf] () {
2117 const auto *const f = dynamic_cast<EBFArrayBoxFactory const*>(pf);
2118 if (f) {
2119 return f->isAllRegular();
2120 } else {
2121 return true;
2122 }
2123 };
2124 amrex::ignore_unused(pf, is_all_regular);
2125 AMREX_ASSERT(linop.isCellCentered() || is_all_regular());
2126#endif
2127
2128 const int amrlev = 0;
2129 const int mg_bottom_lev = linop.NMGLevels(amrlev) - 1;
2130 IntVect nghost(0);
2131 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(amrlev)); }
2132
2133 for (int mglev = 1; mglev <= mg_bottom_lev; ++mglev)
2134 {
2135 linop.avgDownResMG(mglev, res[amrlev][mglev], res[amrlev][mglev-1]);
2136 }
2137
2138 bottomSolve();
2139
2140 for (int mglev = mg_bottom_lev-1; mglev >= 0; --mglev)
2141 {
2142 // cor_fine = I(cor_crse)
2143 interpCorrection(amrlev, mglev);
2144
2145 // rescor = res - L(cor)
2146 computeResOfCorrection(amrlev, mglev);
2147 // res = rescor; this provides b to the vcycle below
2148 LocalCopy(res[amrlev][mglev], rescor[amrlev][mglev], 0, 0, ncomp, nghost);
2149
2150 // save cor; do v-cycle; add the saved to cor
2151 std::swap(cor[amrlev][mglev], cor_hold[amrlev][mglev]);
2152 mgVcycle(amrlev, mglev);
2153 LocalAdd(cor[amrlev][mglev], cor_hold[amrlev][mglev], 0, 0, ncomp, nghost);
2154 }
2155}
2156
2157// At the true bottom of the coarsest AMR level.
2158// in : Residual (res) as b
2159// out : Correction (cor) as x
2160template <typename MF>
2161void
2163{
2164 if (do_nsolve)
2165 {
2166 NSolve(*ns_mlmg, *ns_sol, *ns_rhs);
2167 }
2168 else
2169 {
2171 }
2172}
2173
2174template <typename MF>
2175void
2176MLMGT<MF>::NSolve (MLMGT<MF>& a_solver, MF& a_sol, MF& a_rhs)
2177{
2178 BL_PROFILE("MLMG::NSolve()");
2179
2180 setVal(a_sol, RT(0.0));
2181
2182 MF const& res_bottom = res[0].back();
2183 if (BoxArray::SameRefs(boxArray(a_rhs),boxArray(res_bottom)) &&
2185 {
2186 LocalCopy(a_rhs, res_bottom, 0, 0, ncomp, IntVect(0));
2187 } else {
2188 setVal(a_rhs, RT(0.0));
2189 ParallelCopy(a_rhs, res_bottom, 0, 0, ncomp);
2190 }
2191
2192 a_solver.solve(Vector<MF*>{&a_sol}, Vector<MF const*>{&a_rhs},
2193 RT(-1.0), RT(-1.0));
2194
2195 linop.copyNSolveSolution(cor[0].back(), a_sol);
2196}
2197
2198template <typename MF>
2199void
2200MLMGT<MF>::postCG (int ret, int niters)
2201{
2202 if (niters >= 0) {
2203 m_niters_cg.push_back(niters);
2204 }
2205
2206 const int amrlev = 0;
2207 const int mglev = linop.NMGLevels(amrlev) - 1;
2208 auto& x = cor[amrlev][mglev];
2209 auto& b = res[amrlev][mglev];
2210 const int n = (ret==0) ? nub : nuf;
2211 linop.smooth(amrlev, mglev, x, b, false, n);
2212}
2213
2214template <typename MF>
2215void
2217{
2218 BL_PROFILE("MLMG::actualBottomSolve()");
2219
2220 if (!linop.isBottomActive()) { return; }
2221
2222 auto bottom_start_time = amrex::second();
2223
2224 ParallelContext::push(linop.BottomCommunicator());
2225 struct PopGuard { ~PopGuard () { ParallelContext::pop(); } } pop_guard; // NOLINT(cppcoreguidelines-special-member-functions)
2226
2227 const int amrlev = 0;
2228 const int mglev = linop.NMGLevels(amrlev) - 1;
2229 auto& x = cor[amrlev][mglev];
2230 auto& b = res[amrlev][mglev];
2231
2232 setVal(x, RT(0.0));
2233
2234 if (bottom_solver == BottomSolver::smoother)
2235 {
2236 bool skip_fillboundary = true;
2237 linop.smooth(amrlev, mglev, x, b, skip_fillboundary, nuf);
2238 }
2239 else
2240 {
2241 MF* bottom_b = &b;
2242 MF raii_b;
2243 if (linop.isBottomSingular() && linop.getEnforceSingularSolvable())
2244 {
2245 const IntVect ng = nGrowVect(b);
2246 raii_b = linop.make(amrlev, mglev, ng,
2247 MFInfo().SetArena(The_Async_Arena()));
2248 LocalCopy(raii_b, b, 0, 0, ncomp, ng);
2249 bottom_b = &raii_b;
2250
2251 makeSolvable(amrlev,mglev,*bottom_b);
2252 }
2253
2254 if (bottom_solver == BottomSolver::hypre)
2255 {
2256#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
2257 if constexpr (std::is_same<MF,MultiFab>()) {
2258 bottomSolveWithHypre(x, *bottom_b);
2259 } else
2260#endif
2261 {
2262 amrex::Abort("Using Hypre as bottom solver not supported in this case");
2263 }
2264 }
2265 else if (bottom_solver == BottomSolver::petsc)
2266 {
2267#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
2268 if constexpr (std::is_same<MF,MultiFab>()) {
2269 bottomSolveWithPETSc(x, *bottom_b);
2270 } else
2271#endif
2272 {
2273 amrex::Abort("Using PETSc as bottom solver not supported in this case");
2274 }
2275 }
2276 else if (bottom_solver == BottomSolver::algmg)
2277 {
2278 if constexpr (std::is_same<MF,MultiFab>()) {
2279 bottomSolveWithAlgMG(x, *bottom_b);
2280 } else {
2281 amrex::Abort("MLMG: AlgMG is only supported for MultiFab");
2282 }
2283 }
2284 else if (bottom_solver == BottomSolver::custom && linop.supportCustomBottomSolver())
2285 {
2286 linop.customBottomSolve(this, x, *bottom_b, bottom_reltol, bottom_abstol,
2287 bottom_maxiter);
2288 }
2289 else
2290 {
2291 typename MLCGSolverT<MF>::Type cg_type;
2292 if (bottom_solver == BottomSolver::cg ||
2293 bottom_solver == BottomSolver::cgbicg) {
2294 cg_type = MLCGSolverT<MF>::Type::CG;
2295 } else {
2297 }
2298
2299 int ret = bottomSolveWithCG(x, *bottom_b, cg_type);
2300
2301 if (ret != 0 && (bottom_solver == BottomSolver::cgbicg ||
2302 bottom_solver == BottomSolver::bicgcg))
2303 {
2304 if (bottom_solver == BottomSolver::cgbicg) {
2305 cg_type = MLCGSolverT<MF>::Type::BiCGStab; // switch to bicg
2306 } else {
2307 cg_type = MLCGSolverT<MF>::Type::CG; // switch to cg
2308 }
2309 if (ret == 9) {
2310 // if ret != 9 && ret != 0, x has been set to zero in bottomSolveWithCG
2311 setVal(x, RT(0));
2312 }
2313 ret = bottomSolveWithCG(x, *bottom_b, cg_type);
2314 if (ret == 0) { // switch permanently
2315 if (cg_type == MLCGSolverT<MF>::Type::CG) {
2316 bottom_solver = BottomSolver::cg;
2317 } else {
2318 bottom_solver = BottomSolver::bicgstab;
2319 }
2320 }
2321 }
2322
2323 postCG(ret);
2324 }
2325 }
2326
2327 if (! timer.empty()) {
2328 timer[bottom_time] += amrex::second() - bottom_start_time;
2329 }
2330}
2331
2332template <typename MF>
2333int
2335{
2336 MLCGSolverT<MF> cg_solver(linop);
2337 cg_solver.setSolver(type);
2338 cg_solver.setVerbose(bottom_verbose);
2339 cg_solver.setPrintIndentation(print_ident);
2340 cg_solver.setMaxIter(bottom_maxiter);
2341 cg_solver.setInitSolnZeroed(true);
2342 if (cf_strategy == CFStrategy::ghostnodes) { cg_solver.setNGhost(linop.getNGrow()); }
2343
2344 int ret = cg_solver.solve(x, b, bottom_reltol, bottom_abstol);
2345 if (ret != 0 && verbose > 1) {
2346 amrex::Print() << print_ident << "MLMG: Bottom solve failed.\n";
2347 }
2348 m_niters_cg.push_back(cg_solver.getNumIters());
2349
2350 if (ret != 0 && ret != 9) {
2351 setVal(x, RT(0));
2352 }
2353
2354 return ret;
2355}
2356
2357// Compute multi-level Residual (res) up to amrlevmax.
2358template <typename MF>
2359void
2361{
2362 BL_PROFILE("MLMG::computeMLResidual()");
2363
2364 const int mglev = 0;
2365 for (int alev = amrlevmax; alev >= 0; --alev) {
2366 const MF* crse_bcdata = (alev > 0) ? &(sol[alev-1]) : nullptr;
2367 linop.solutionResidual(alev, res[alev][mglev], sol[alev], rhs[alev], crse_bcdata);
2368 if (alev < finest_amr_lev) {
2369 linop.reflux(alev, res[alev][mglev], sol[alev], rhs[alev],
2370 res[alev+1][mglev], sol[alev+1], rhs[alev+1]);
2371 }
2372 }
2373}
2374
2375// Compute single AMR level residual without masking.
2376template <typename MF>
2377void
2379{
2380 BL_PROFILE("MLMG::computeResidual()");
2381 const MF* crse_bcdata = (alev > 0) ? &(sol[alev-1]) : nullptr;
2382 linop.solutionResidual(alev, res[alev][0], sol[alev], rhs[alev], crse_bcdata);
2383}
2384
2385// Compute coarse AMR level composite residual with coarse solution and fine correction
2386template <typename MF>
2387void
2389{
2390 BL_PROFILE("MLMG::computeResWithCrseSolFineCor()");
2391
2392 IntVect nghost(0);
2393 if (cf_strategy == CFStrategy::ghostnodes) {
2394 nghost = IntVect(std::min(linop.getNGrow(falev),linop.getNGrow(calev)));
2395 }
2396
2397 MF& crse_sol = sol[calev];
2398 const MF& crse_rhs = rhs[calev];
2399 MF& crse_res = res[calev][0];
2400
2401 MF& fine_sol = sol[falev];
2402 const MF& fine_rhs = rhs[falev];
2403 MF& fine_cor = cor[falev][0];
2404 MF& fine_res = res[falev][0];
2405 MF& fine_rescor = rescor[falev][0];
2406
2407 const MF* crse_bcdata = (calev > 0) ? &(sol[calev-1]) : nullptr;
2408 linop.solutionResidual(calev, crse_res, crse_sol, crse_rhs, crse_bcdata);
2409
2410 linop.correctionResidual(falev, 0, fine_rescor, fine_cor, fine_res, BCMode::Homogeneous);
2411 LocalCopy(fine_res, fine_rescor, 0, 0, ncomp, nghost);
2412
2413 linop.reflux(calev, crse_res, crse_sol, crse_rhs, fine_res, fine_sol, fine_rhs);
2414
2415 linop.avgDownResAmr(calev, crse_res, fine_res);
2416}
2417
2418// Compute fine AMR level residual fine_res = fine_res - L(fine_cor) with coarse providing BC.
2419template <typename MF>
2420void
2422{
2423 BL_PROFILE("MLMG::computeResWithCrseCorFineCor()");
2424
2425 IntVect nghost(0);
2426 if (cf_strategy == CFStrategy::ghostnodes) {
2427 nghost = IntVect(linop.getNGrow(falev));
2428 }
2429
2430 const MF& crse_cor = cor[falev-1][0];
2431
2432 MF& fine_cor = cor [falev][0];
2433 MF& fine_res = res [falev][0];
2434 MF& fine_rescor = rescor[falev][0];
2435
2436 // fine_rescor = fine_res - L(fine_cor)
2437 linop.correctionResidual(falev, 0, fine_rescor, fine_cor, fine_res,
2438 BCMode::Inhomogeneous, &crse_cor);
2439 LocalCopy(fine_res, fine_rescor, 0, 0, ncomp, nghost);
2440}
2441
2442// Interpolate correction from coarse to fine AMR level.
2443template <typename MF>
2444void
2446{
2447 BL_PROFILE("MLMG::interpCorrection_1");
2448
2449 IntVect nghost(0);
2450 if (cf_strategy == CFStrategy::ghostnodes) {
2451 nghost = IntVect(linop.getNGrow(alev));
2452 }
2453
2454 MF & crse_cor = cor[alev-1][0];
2455 MF & fine_cor = cor[alev ][0];
2456
2457 const Geometry& crse_geom = linop.Geom(alev-1,0);
2458
2459 int ng_src = 0;
2460 int ng_dst = linop.isCellCentered() ? 1 : 0;
2461 if (cf_strategy == CFStrategy::ghostnodes)
2462 {
2463 ng_src = linop.getNGrow(alev-1);
2464 ng_dst = linop.getNGrow(alev-1);
2465 if constexpr (IsMultiFabLike_v<MF>) {
2466 crse_cor.FillBoundary(0, ncomp, IntVect(ng_src), crse_geom.periodicity());
2467 } else {
2468 amrex::Abort("MLMG: CFStrategy::ghostnodes not supported for non-MultiFab like types");
2469 }
2470 }
2471
2472 MF cfine = linop.makeCoarseAmr(alev, IntVect(ng_dst),
2473 MFInfo().SetArena(The_Async_Arena()));
2474 setVal(cfine, RT(0.0));
2475 ParallelCopy(cfine, crse_cor, 0, 0, ncomp, IntVect(ng_src), IntVect(ng_dst),
2476 crse_geom.periodicity());
2477
2478 linop.interpolationAmr(alev, fine_cor, cfine, nghost); // NOLINT(readability-suspicious-call-argument)
2479}
2480
2481// Interpolate correction between MG levels
2482// inout: Correction (cor) on coarse MG lev. (out due to FillBoundary)
2483// out : Correction (cor) on fine MG lev.
2484template <typename MF>
2485void
2486MLMGT<MF>::interpCorrection (int alev, int mglev)
2487{
2488 BL_PROFILE("MLMG::interpCorrection_2");
2489
2490 MF& crse_cor = cor[alev][mglev+1];
2491 MF& fine_cor = cor[alev][mglev ];
2492 linop.interpAssign(alev, mglev, fine_cor, crse_cor);
2493}
2494
2495// (Fine MG level correction) += I(Coarse MG level correction)
2496template <typename MF>
2497void
2499{
2500 BL_PROFILE("MLMG::addInterpCorrection()");
2501
2502 const MF& crse_cor = cor[alev][mglev+1];
2503 MF& fine_cor = cor[alev][mglev ];
2504
2505 MF cfine;
2506 const MF* cmf;
2507
2508 if (linop.isMFIterSafe(alev, mglev, mglev+1))
2509 {
2510 cmf = &crse_cor;
2511 }
2512 else
2513 {
2514 cfine = linop.makeCoarseMG(alev, mglev, IntVect(0),
2515 MFInfo().SetArena(The_Async_Arena()));
2516 ParallelCopy(cfine, crse_cor, 0, 0, ncomp);
2517 cmf = &cfine;
2518 }
2519
2520 linop.interpolation(alev, mglev, fine_cor, *cmf);
2521}
2522
2523// Compute rescor = res - L(cor)
2524// in : res
2525// inout: cor (out due to FillBoundary in linop.correctionResidual)
2526// out : rescor
2527template <typename MF>
2528void
2530{
2531 BL_PROFILE("MLMG:computeResOfCorrection()");
2532 MF & x = cor[amrlev][mglev];
2533 const MF& b = res[amrlev][mglev];
2534 MF & r = rescor[amrlev][mglev];
2535 linop.correctionResidual(amrlev, mglev, r, x, b, BCMode::Homogeneous);
2536}
2537
2538// Compute single-level masked inf-norm of Residual (res).
2539template <typename MF>
2540auto
2541MLMGT<MF>::ResNormInf (int alev, bool local) -> RT
2542{
2543 BL_PROFILE("MLMG::ResNormInf()");
2544 return linop.normInf(alev, res[alev][0], local);
2545}
2546
2547// Computes multi-level masked inf-norm of Residual (res).
2548template <typename MF>
2549auto
2550MLMGT<MF>::MLResNormInf (int alevmax, bool local) -> RT
2551{
2552 BL_PROFILE("MLMG::MLResNormInf()");
2553 RT r = RT(0.0);
2554 for (int alev = 0; alev <= alevmax; ++alev)
2555 {
2556 r = std::max(r, ResNormInf(alev,true));
2557 }
2559 return r;
2560}
2561
2562// Compute multi-level masked inf-norm of RHS (rhs).
2563template <typename MF>
2564auto
2566{
2567 BL_PROFILE("MLMG::MLRhsNormInf()");
2568 RT r = RT(0.0);
2569 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
2570 auto t = linop.normInf(alev, rhs[alev], true);
2571 r = std::max(r, t);
2572 }
2574 return r;
2575}
2576
2577template <typename MF>
2578void
2580{
2581 auto const& offset = linop.getSolvabilityOffset(0, 0, rhs[0]);
2582 if (verbose >= 4) {
2583 for (int c = 0; c < ncomp; ++c) {
2584 amrex::Print() << print_ident << "MLMG: Subtracting " << offset[c] << " from rhs component "
2585 << c << "\n";
2586 }
2587 }
2588 for (int alev = 0; alev < namrlevs; ++alev) {
2589 linop.fixSolvabilityByOffset(alev, 0, rhs[alev], offset);
2590 }
2591}
2592
2593template <typename MF>
2594void
2595MLMGT<MF>::makeSolvable (int amrlev, int mglev, MF& mf)
2596{
2597 auto const& offset = linop.getSolvabilityOffset(amrlev, mglev, mf);
2598 if (verbose >= 4) {
2599 for (int c = 0; c < ncomp; ++c) {
2600 amrex::Print() << print_ident << "MLMG: Subtracting " << offset[c]
2601 << " from mf component c = " << c
2602 << " on level (" << amrlev << ", " << mglev << ")\n";
2603 }
2604 }
2605 linop.fixSolvabilityByOffset(amrlev, mglev, mf, offset);
2606}
2607
2608template <typename MF>
2609template <class TMF>
2610requires (std::same_as<TMF,MultiFab>)
2611void
2612MLMGT<MF>::configureAlgMG (MLAlgMG& s, bool singular, bool with_options)
2613{
2614 // Dependent type: AlgMG stays a forward declaration in this header.
2615 AlgMG<RT>& a = s.solver();
2616 a.setVerbose(bottom_verbose);
2617 a.setPrintIndentation(print_ident + " ");
2618 a.setThrowException(throw_exception);
2619 // Settings that select the setup (singular flag, Krylov type, the user's
2620 // options) are applied once per assembled matrix. Reapplying them on
2621 // every solve could toggle a value against the callback and redo the
2622 // expensive AlgMG setup each time.
2623 if (with_options) {
2624 a.setSingular(singular);
2626 if (m_algmg_options) { m_algmg_options(a); }
2627 }
2628}
2629
2630template <typename MF>
2631template <class TMF>
2632requires (std::same_as<TMF,MultiFab>)
2633void
2634MLMGT<MF>::algmgSolve (MLAlgMG& s, MF& x, MF const& b)
2635{
2636 // As a preconditioner MLMG must be a fixed linear operation: one V-cycle.
2637 if (precond_mode) {
2638 s.applyVcycle(x, b);
2639 } else {
2640 try {
2641 s.solve(x, b, m_algmg_reltol, RT(0), bottom_maxiter);
2642 } catch (std::runtime_error const& e) {
2643 throw error(e.what());
2644 }
2645 }
2646}
2647
2648template <typename MF>
2649template <class TMF>
2650requires (std::same_as<TMF,MultiFab>)
2651void
2653{
2654 const int amrlev = 0;
2655 const int mglev = linop.NMGLevels(amrlev) - 1;
2656
2657 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ncomp == 1, "MLMG: AlgMG needs ncomp == 1");
2658
2659 bool const fresh = (m_algmg_bottom == nullptr);
2660 if (fresh) { m_algmg_bottom = linop.makeAlgMG(mglev); }
2661 configureAlgMG(*m_algmg_bottom, linop.isBottomSingular(), fresh);
2662 algmgSolve(*m_algmg_bottom, x, b);
2663
2664 if (linop.isSingular(amrlev) && linop.getEnforceSingularSolvable()) {
2665 makeSolvable(amrlev, mglev, x);
2666 }
2667}
2668
2669template <typename MF>
2670template <class TMF>
2671requires (std::same_as<TMF,MultiFab>)
2672void
2674{
2675 BL_PROFILE("MLMG::algmgLevel0Solve()");
2676
2677 auto t0 = amrex::second();
2678
2679 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ncomp == 1, "MLMG: AlgMG needs ncomp == 1");
2680
2681 bool const fresh = (m_algmg_level0 == nullptr);
2682 if (fresh) { m_algmg_level0 = linop.makeAlgMG(0); }
2683 configureAlgMG(*m_algmg_level0, linop.isSingular(0), fresh);
2684 algmgSolve(*m_algmg_level0, cor[0][0], res[0][0]);
2685
2686 // Known overset cells must not receive a correction; the matrix rows
2687 // there are identities with a zero rhs, so this is a safety pass.
2688 linop.applyOverset(0, cor[0][0]);
2689
2690 if (linop.isSingular(0) && linop.getEnforceSingularSolvable()) {
2691 makeSolvable(0, 0, cor[0][0]);
2692 }
2693
2694 if (! timer.empty()) {
2695 timer[bottom_time] += amrex::second() - t0;
2696 }
2697}
2698
2699template <typename MF>
2700bool
2702 RT best_norm, std::string& reason) const
2703{
2704 RT const rnorm = norms.back();
2705 if (!amrex::isfinite(rnorm)) {
2706 reason = "non-finite residual";
2707 return true;
2708 }
2709 if (rnorm > RT(1.e20)*max_norm ||
2710 rnorm > hybrid_divergence_factor*best_norm) {
2711 reason = "diverging";
2712 return true;
2713 }
2714 const int w = hybrid_stall_window;
2715 if (w > 0 && int(norms.size()) > w) {
2716 const RT r_old = norms[norms.size()-1-w];
2717 if (rnorm > std::pow(hybrid_stall_rate, RT(w)) * r_old) {
2718 reason = "stalling";
2719 return true;
2720 }
2721 }
2722 return false;
2723}
2724
2725#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
2726template <typename MF>
2727template <class TMF>
2728requires (std::same_as<TMF,MultiFab>)
2729void
2730MLMGT<MF>::bottomSolveWithHypre (MF& x, const MF& b)
2731{
2732 const int amrlev = 0;
2733 const int mglev = linop.NMGLevels(amrlev) - 1;
2734
2735 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ncomp == 1, "bottomSolveWithHypre doesn't work with ncomp > 1");
2736
2737 if (linop.isCellCentered())
2738 {
2739 if (hypre_solver == nullptr) // We should reuse the setup
2740 {
2741 hypre_solver = linop.makeHypre(hypre_interface);
2742
2743 hypre_solver->setVerbose(bottom_verbose);
2744 if (hypre_interface == amrex::Hypre::Interface::ij) {
2745 hypre_solver->setHypreOptionsNamespace(hypre_options_namespace);
2746 } else {
2747 hypre_solver->setHypreOldDefault(hypre_old_default);
2748 hypre_solver->setHypreRelaxType(hypre_relax_type);
2749 hypre_solver->setHypreRelaxOrder(hypre_relax_order);
2750 hypre_solver->setHypreNumSweeps(hypre_num_sweeps);
2751 hypre_solver->setHypreStrongThreshold(hypre_strong_threshold);
2752 }
2753
2754 const BoxArray& ba = linop.m_grids[amrlev].back();
2755 const DistributionMapping& dm = linop.m_dmap[amrlev].back();
2756 const Geometry& geom = linop.m_geom[amrlev].back();
2757
2758 hypre_bndry = std::make_unique<MLMGBndryT<MF>>(ba, dm, ncomp, geom);
2759 hypre_bndry->setHomogValues();
2760 const Real* dx = linop.m_geom[0][0].CellSize();
2761 IntVect crse_ratio = linop.m_coarse_data_crse_ratio.allGT(0) ? linop.m_coarse_data_crse_ratio : IntVect(1);
2762 RealVect bclocation(AMREX_D_DECL(0.5*dx[0]*crse_ratio[0],
2763 0.5*dx[1]*crse_ratio[1],
2764 0.5*dx[2]*crse_ratio[2]));
2765 hypre_bndry->setLOBndryConds(linop.m_lobc, linop.m_hibc, IntVect(-1), bclocation,
2766 linop.m_coarse_fine_bc_type);
2767 }
2768
2769 // IJ interface understands absolute tolerance API of HYPRE
2770 amrex::Real hypre_abstol =
2771 (hypre_interface == amrex::Hypre::Interface::ij)
2772 ? bottom_abstol : Real(-1.0);
2773 hypre_solver->setThrowException(throw_exception);
2774 try {
2775 hypre_solver->solve(
2776 x, b, bottom_reltol, hypre_abstol, bottom_maxiter, *hypre_bndry,
2777 linop.getMaxOrder());
2778 } catch (std::runtime_error const& e) {
2779 throw error(e.what());
2780 }
2781 }
2782 else
2783 {
2784 if (hypre_node_solver == nullptr)
2785 {
2786 hypre_node_solver =
2787 linop.makeHypreNodeLap(bottom_verbose, hypre_options_namespace);
2788 }
2789 hypre_node_solver->setThrowException(throw_exception);
2790 try {
2791 hypre_node_solver->solve(x, b, bottom_reltol, bottom_abstol, bottom_maxiter);
2792 } catch (std::runtime_error const& e) {
2793 throw error(e.what());
2794 }
2795 }
2796
2797 // For singular problems there may be a large constant added to all values of the solution
2798 // For precision reasons we enforce that the average of the correction from HYPRE is 0
2799 if (linop.isSingular(amrlev) && linop.getEnforceSingularSolvable())
2800 {
2801 makeSolvable(amrlev, mglev, x);
2802 }
2803}
2804#endif
2805
2806#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
2807template <typename MF>
2808template <class TMF>
2809requires (std::same_as<TMF,MultiFab>)
2810void
2811MLMGT<MF>::bottomSolveWithPETSc (MF& x, const MF& b)
2812{
2813 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ncomp == 1, "bottomSolveWithPETSc doesn't work with ncomp > 1");
2814
2815 if(petsc_solver == nullptr)
2816 {
2817 petsc_solver = linop.makePETSc();
2818 petsc_solver->setVerbose(bottom_verbose);
2819
2820 const BoxArray& ba = linop.m_grids[0].back();
2821 const DistributionMapping& dm = linop.m_dmap[0].back();
2822 const Geometry& geom = linop.m_geom[0].back();
2823
2824 petsc_bndry = std::make_unique<MLMGBndryT<MF>>(ba, dm, ncomp, geom);
2825 petsc_bndry->setHomogValues();
2826 const Real* dx = linop.m_geom[0][0].CellSize();
2827 auto crse_ratio = linop.m_coarse_data_crse_ratio.allGT(0) ? linop.m_coarse_data_crse_ratio : IntVect(1);
2828 RealVect bclocation(AMREX_D_DECL(0.5*dx[0]*crse_ratio[0],
2829 0.5*dx[1]*crse_ratio[1],
2830 0.5*dx[2]*crse_ratio[2]));
2831 petsc_bndry->setLOBndryConds(linop.m_lobc, linop.m_hibc, IntVect(-1), bclocation,
2832 linop.m_coarse_fine_bc_type);
2833 }
2834 petsc_solver->solve(x, b, bottom_reltol, Real(-1.), bottom_maxiter, *petsc_bndry,
2835 linop.getMaxOrder());
2836}
2837#endif
2838
2839template <typename MF>
2840void
2841MLMGT<MF>::checkPoint (const Vector<MultiFab*>& a_sol,
2842 const Vector<MultiFab const*>& a_rhs,
2843 RT a_tol_rel, RT a_tol_abs, const char* a_file_name) const
2844{
2845 std::string file_name(a_file_name);
2846 UtilCreateCleanDirectory(file_name, false);
2847
2849 {
2850 std::string HeaderFileName(std::string(a_file_name)+"/Header");
2851 std::ofstream HeaderFile;
2852 HeaderFile.open(HeaderFileName.c_str(), std::ofstream::out |
2853 std::ofstream::trunc |
2854 std::ofstream::binary);
2855 if( ! HeaderFile.good()) {
2856 FileOpenFailed(HeaderFileName);
2857 }
2858
2859 HeaderFile.precision(17);
2860
2861 std::string norm_name = getEnumNameString(norm_type);
2862
2863 HeaderFile << linop.name() << "\n"
2864 << "a_tol_rel = " << a_tol_rel << "\n"
2865 << "a_tol_abs = " << a_tol_abs << "\n"
2866 << "verbose = " << verbose << "\n"
2867 << "max_iters = " << max_iters << "\n"
2868 << "nu1 = " << nu1 << "\n"
2869 << "nu2 = " << nu2 << "\n"
2870 << "nuf = " << nuf << "\n"
2871 << "nub = " << nub << "\n"
2872 << "max_fmg_iters = " << max_fmg_iters << "\n"
2873 << "bottom_solver = " << static_cast<int>(bottom_solver) << "\n"
2874 << "bottom_verbose = " << bottom_verbose << "\n"
2875 << "bottom_maxiter = " << bottom_maxiter << "\n"
2876 << "bottom_reltol = " << bottom_reltol << "\n"
2877 << "convergence_norm = " << norm_name << "\n"
2878 << "namrlevs = " << namrlevs << "\n"
2879 << "finest_amr_lev = " << finest_amr_lev << "\n"
2880 << "linop_prepared = " << linop_prepared << "\n"
2881 << "solve_called = " << solve_called << "\n";
2882
2883 for (int ilev = 0; ilev <= finest_amr_lev; ++ilev) {
2884 UtilCreateCleanDirectory(file_name+"/Level_"+std::to_string(ilev), false);
2885 }
2886 }
2887
2889
2890 for (int ilev = 0; ilev <= finest_amr_lev; ++ilev) {
2891 VisMF::Write(*a_sol[ilev], file_name+"/Level_"+std::to_string(ilev)+"/sol");
2892 VisMF::Write(*a_rhs[ilev], file_name+"/Level_"+std::to_string(ilev)+"/rhs");
2893 }
2894
2895 linop.checkPoint(file_name+"/linop");
2896}
2897
2898template <typename MF>
2899void
2901{
2902 print_ident.resize(print_ident.size()+4, ' ');
2903}
2904
2905template <typename MF>
2906void
2908{
2909 if (print_ident.size() > 4) {
2910 print_ident.resize(print_ident.size()-4, ' ');
2911 } else {
2912 print_ident.clear();
2913 }
2914}
2915
2916extern template class MLMGT<MultiFab>;
2917
2920
2921}
2922
2923#endif
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:593
#define BL_PROFILE_VAR_STOP(vname)
Definition AMReX_BLProfiler.H:605
#define BL_PROFILE_VAR(fname, vname)
Definition AMReX_BLProfiler.H:602
#define AMREX_ALWAYS_ASSERT_WITH_MESSAGE(EX, MSG)
Definition AMReX_BLassert.H:49
#define AMREX_ASSERT(EX)
Definition AMReX_BLassert.H:38
Enum reflection utilities and the AMREX_ENUM macro.
#define AMREX_D_DECL(a, b, c)
Definition AMReX_SPACE.H:171
Algebraic multigrid solver for SpMatrix / AlgVector systems.
Definition AMReX_AlgMG.H:264
void setSingular(bool b)
Definition AMReX_AlgMG.H:330
void setPrintIndentation(std::string s)
Prefix of the lines the solver prints, e.g. to nest them in MLMG output.
Definition AMReX_AlgMG.H:286
void setThrowException(bool b)
Definition AMReX_AlgMG.H:370
void setVerbose(int v)
Definition AMReX_AlgMG.H:284
void setKrylovSolver(KrylovSolver a)
Krylov solver with the V-cycle as preconditioner (see KrylovSolver). Default None.
Definition AMReX_AlgMG.H:347
Print on all processors of the default communicator.
Definition AMReX_Print.H:113
Reference-counted collection of Boxes.
Definition AMReX_BoxArray.H:681
static bool SameRefs(const BoxArray &lhs, const BoxArray &rhs)
whether two BoxArrays share the same data
Definition AMReX_BoxArray.H:1251
Calculates the distribution of FABs to MPI processes.
Definition AMReX_DistributionMapping.H:51
static bool SameRefs(const DistributionMapping &lhs, const DistributionMapping &rhs)
Return true if lhs and rhs share the same underlying Ref.
Definition AMReX_DistributionMapping.H:295
Definition AMReX_EBFabFactory.H:32
Rectangular problem domain geometry.
Definition AMReX_Geometry.H:87
Periodicity periodicity() const noexcept
Return the Periodicity based on the length of the domain.
Definition AMReX_Geometry.H:426
Interface
HYPRE interface modes supported.
Definition AMReX_Hypre.H:70
@ ij
Definition AMReX_Hypre.H:70
__host__ static __device__ constexpr IntVectND< dim > TheDimensionVector(int d) noexcept
Definition AMReX_IntVect.H:790
Algebraic system of one MG level of AMR level 0 of an MLLinOp, solved with AlgMG.
Definition AMReX_MLAlgMG.H:25
void applyVcycle(MultiFab &soln, MultiFab const &rhs)
One AlgMG V-cycle with zero initial guess: the preconditioner form.
Definition AMReX_MLAlgMG.cpp:145
AlgMG< Real > & solver() noexcept
The solver, for settings such as verbosity and Krylov acceleration.
Definition AMReX_MLAlgMG.cpp:142
void solve(MultiFab &soln, MultiFab const &rhs, Real reltol, Real abstol, int maxiter)
Solve A x = b with zero initial guess.
Definition AMReX_MLAlgMG.cpp:155
void setSolver(Type _typ) noexcept
Switch between BiCGStab and CG after construction.
Definition AMReX_MLCGSolver.H:48
void setVerbose(int _verbose)
Control how much logging is emitted (0 = silent).
Definition AMReX_MLCGSolver.H:72
int getNumIters() const noexcept
Iteration count from the last solve* call (or -1 if unused).
Definition AMReX_MLCGSolver.H:151
void setInitSolnZeroed(bool _sol_zeroed)
Definition AMReX_MLCGSolver.H:99
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
@ BiCGStab
Definition AMReX_MLCGSolver.H:27
@ CG
Definition AMReX_MLCGSolver.H:27
void setNGhost(int _nghost)
Set the number of grow cells used when allocating temporaries.
Definition AMReX_MLCGSolver.H:108
void setMaxIter(int _maxiter)
Cap the number of Krylov iterations performed.
Definition AMReX_MLCGSolver.H:81
void setPrintIndentation(std::string s)
Prefix printed messages (e.g., to indent per level).
Definition AMReX_MLCGSolver.H:90
Definition AMReX_MLLinOp.H:142
typename FabDataType< MF >::fab_type FAB
Definition AMReX_MLLinOp.H:153
typename FabDataType< MF >::value_type RT
Definition AMReX_MLLinOp.H:154
LinOpEnumType::Location Location
Definition AMReX_MLLinOp.H:159
LinOpEnumType::BCMode BCMode
Definition AMReX_MLLinOp.H:157
Definition AMReX_MLMG.H:44
Definition AMReX_MLMG.H:39
void prepareForFluxes(Vector< MF const * > const &a_sol)
Build boundary caches needed by getFluxes()/compFluxes.
Definition AMReX_MLMG.H:1242
void setBottomVerbose(int v) noexcept
Verbosity for the bottom solver (0 silent).
Definition AMReX_MLMG.H:388
void setMaxFmgIter(int n) noexcept
Cap the number of FMG cycles executed.
Definition AMReX_MLMG.H:294
RT MLResNormInf(int alevmax, bool local=false)
Composite infinity norm of the residual up to level alevmax.
Definition AMReX_MLMG.H:2550
void postCG(int ret, int niters=-1)
Post CG smoothing.
Definition AMReX_MLMG.H:2200
RT MLRhsNormInf(bool local=false)
Composite infinity norm of the RHS.
Definition AMReX_MLMG.H:2565
void setNoGpuSync(bool do_not_sync) noexcept
Control implicit GPU synchronization inside solve().
Definition AMReX_MLMG.H:461
MLMGT(MLMGT< MF > &&)=delete
bool hybridShouldSwitch(Vector< RT > const &norms, RT max_norm, RT best_norm, std::string &reason) const
Definition AMReX_MLMG.H:2701
void actualBottomSolve()
Execute the actual bottom solve after pre-smoothing and restriction.
Definition AMReX_MLMG.H:2216
MF MFType
Definition AMReX_MLMG.H:52
void interpCorrection(int alev, int mglev)
Interpolate corrections on (alev,mglev).
Definition AMReX_MLMG.H:2486
BottomSolver getBottomSolver() const noexcept
Definition AMReX_MLMG.H:341
void setPreSmooth(int n) noexcept
Number of pre-smoothing passes per V-cycle.
Definition AMReX_MLMG.H:315
void getFluxes(const Vector< AMF * > &a_flux, Location a_loc=Location::CellCenter)
Cell-centered flux helper.
Definition AMReX_MLMG.H:1366
void setBottomToleranceAbs(RT t) noexcept
Absolute tolerance for the bottom solver (not used by AlgMG and PETSc).
Definition AMReX_MLMG.H:406
RT getFinalResidual() const noexcept
Definition AMReX_MLMG.H:724
void interpCorrection(int alev)
Interpolate corrections onto AMR level alev.
Definition AMReX_MLMG.H:2445
void getEBFluxes(const Vector< MF * > &a_eb_flux)
Flux into the EB wall using the internally stored solution.
Definition AMReX_MLMG.H:1463
void getEBFluxes(const Vector< MF * > &a_eb_flux, const Vector< MF * > &a_sol)
Flux into the EB wall using an explicit solution vector.
Definition AMReX_MLMG.H:1475
friend class GMRESMLMGT
Definition AMReX_MLMG.H:50
void getGradSolution(const Vector< Array< AMF *, 3 > > &a_grad_sol, Location a_loc=Location::FaceCenter)
Populate gradient components of the converged solution.
Definition AMReX_MLMG.H:1253
void setBottomSmooth(int n) noexcept
Additional smoothing passes executed after the bottom solver.
Definition AMReX_MLMG.H:333
void setNSolve(int flag) noexcept
Enable (flag!=0) or disable the N-solve path.
Definition AMReX_MLMG.H:438
int getBottomVerbose() const
Definition AMReX_MLMG.H:262
void computeResOfCorrection(int amrlev, int mglev)
Compute the residual of the correction equation on (amrlev,mglev).
Definition AMReX_MLMG.H:2529
void applyPrecond(const Vector< MF * > &out, const Vector< MF * > &in)
Apply the linear operator as a preconditioner (out = L(in)).
Definition AMReX_MLMG.H:1634
void setCFStrategy(CFStrategy a_cf_strategy) noexcept
Select the coarse-fine synchronization strategy.
Definition AMReX_MLMG.H:382
void computeResWithCrseCorFineCor(int falev)
Residual update using coarse correction / fine correction.
Definition AMReX_MLMG.H:2421
void NSolve(MLMGT< MF > &a_solver, MF &a_sol, MF &a_rhs)
Perform an NSolve using an MLMGT wrapper.
Definition AMReX_MLMG.H:2176
void getFluxes(const Vector< Array< AMF *, 3 > > &a_flux, const Vector< AMF * > &a_sol, Location a_loc=Location::FaceCenter)
Face-centered flux helper that accepts an explicit solution vector.
Definition AMReX_MLMG.H:1324
typename MLLinOpT< MF >::Location Location
Definition AMReX_MLMG.H:57
void apply(const Vector< MF * > &out, const Vector< MF * > &in)
out = L(in). Note that, if no actual solve is needed, one could turn off multigrid coarsening by cons...
Definition AMReX_MLMG.H:1557
void getFluxes(const Vector< Array< AMF *, 3 > > &a_flux, Location a_loc=Location::FaceCenter)
Face-centered flux helper (-b grad(phi) for alpha a - beta div(b grad)).
Definition AMReX_MLMG.H:1284
void setNSolveGridSize(int s) noexcept
Set the tile size used for N-solve builds.
Definition AMReX_MLMG.H:444
void setVerbose(int v) noexcept
Set the main solver verbosity (0 silent).
Definition AMReX_MLMG.H:282
MultigridType getMultigridType() const noexcept
Definition AMReX_MLMG.H:345
void computeMLResidual(int amrlevmax)
Compute the composite residual norm up to AMR level amrlevmax.
Definition AMReX_MLMG.H:2360
RT getInitResidual() const noexcept
Definition AMReX_MLMG.H:722
int getNumIters() const noexcept
Definition AMReX_MLMG.H:727
void setPostSmooth(int n) noexcept
Number of post-smoothing passes per V-cycle.
Definition AMReX_MLMG.H:321
void mgVcycle(int amrlev, int mglev)
Run a multigrid V-cycle on (amrlev,mglev).
Definition AMReX_MLMG.H:2004
void prepareForNSolve()
Prepare the NSolve path.
Definition AMReX_MLMG.H:1883
RT precond(Vector< MF * > const &a_sol, Vector< MF const * > const &a_rhs, RT a_tol_rel, RT a_tol_abs)
Apply MLMG as a right-preconditioner with relaxed tolerances.
Definition AMReX_MLMG.H:1224
void makeSolvable(int amrlev, int mglev, MF &mf)
Adjust a field on (amrlev,mglev) to satisfy null-space constraints.
Definition AMReX_MLMG.H:2595
void getFluxes(std::initializer_list< Array< AMF *, 3 > > a_flux, std::initializer_list< AMF * > a_sol, Location a_loc=Location::FaceCenter)
Initializer-list convenience overload for the face-centered flux helper above.
Definition AMReX_MLMG.H:1356
void setHybridStallCriterion(int window, RT rate) noexcept
Hybrid mode: switch to AlgMG when the residual has not dropped by rate per iteration on average over ...
Definition AMReX_MLMG.H:371
void makeSolvable()
Adjust RHS/solution to satisfy null-space constraints.
Definition AMReX_MLMG.H:2579
void setBottomSolver(BottomSolver s) noexcept
Select the bottom solver type (e.g., CG, BiCGStab, Hypre, PETSc).
Definition AMReX_MLMG.H:340
void preparePrecond()
Prepare preconditioner-specific caches (e.g., boundary data).
Definition AMReX_MLMG.H:1875
friend class MLCGSolverT
Definition AMReX_MLMG.H:49
void incPrintIdentation()
Increase the indentation used when printing solver logs.
Definition AMReX_MLMG.H:2900
typename MLLinOpT< MF >::RT RT
Definition AMReX_MLMG.H:54
void setThrowException(bool t) noexcept
Control behavior when the solve fails to converge or blows up.
Definition AMReX_MLMG.H:276
void decPrintIdentation()
Decrease the indentation used when printing solver logs.
Definition AMReX_MLMG.H:2907
void setFixedIter(int nit) noexcept
Set the number of fixed MLMG iterations (convergence may still exit early if the residual is already ...
Definition AMReX_MLMG.H:301
void setMultigridType(MultigridType t) noexcept
Select how AMR level 0 is solved. See MultigridType.
Definition AMReX_MLMG.H:344
Vector< RT > const & getResidualHistory() const noexcept
Definition AMReX_MLMG.H:726
void prepareLinOp()
Finalize operator-dependent metadata before iterating.
Definition AMReX_MLMG.H:1847
void setPrecondIter(int nit) noexcept
Set how many MLMG iterations the preconditioner executes per Krylov call (still subject to early-conv...
Definition AMReX_MLMG.H:308
CFStrategy
Definition AMReX_MLMG.H:61
@ none
Definition AMReX_MLMG.H:61
@ ghostnodes
Definition AMReX_MLMG.H:61
amrex::BottomSolver BottomSolver
Definition AMReX_MLMG.H:59
void prepareForSolve(Vector< AMF * > const &a_sol, Vector< AMF const * > const &a_rhs)
Prepare linear operators, coefficients, and RHS data prior to solving.
Definition AMReX_MLMG.H:1646
int bottomSolveWithCG(MF &x, const MF &b, typename MLCGSolverT< MF >::Type type)
Bottom solve using CG/BiCGStab implemented in MLCGSolverT.
Definition AMReX_MLMG.H:2334
void getGradSolution(std::initializer_list< Array< AMF *, 3 > > a_grad_sol, Location a_loc=Location::FaceCenter)
Initializer-list convenience overload for getGradSolution.
Definition AMReX_MLMG.H:1276
void setAlwaysUseBNorm(int flag) noexcept
Deprecated flag for forcing B-norm convergence checks.
Definition AMReX_MLMG.H:868
bool usedAlgMG() const noexcept
Definition AMReX_MLMG.H:348
void compResidual(const Vector< MF * > &a_res, const Vector< MF * > &a_sol, const Vector< MF const * > &a_rhs)
Compute multilevel residuals a_rhs - L(a_sol) on each AMR level.
Definition AMReX_MLMG.H:1489
void miniCycle(int amrlev)
Execute a per-level mini cycle.
Definition AMReX_MLMG.H:1993
RT solve(std::initializer_list< AMF * > a_sol, std::initializer_list< AMF const * > a_rhs, RT a_tol_rel, RT a_tol_abs, const char *checkpoint_file=nullptr)
Convenience initializer-list overload that forwards to the Vector-based solve.
void getFluxes(const Vector< AMF * > &a_flux, const Vector< AMF * > &a_sol, Location a_loc=Location::CellCenter)
Cell-centered flux helper that accepts an explicit solution vector.
Definition AMReX_MLMG.H:1395
void setFinalFillBC(int flag) noexcept
Force a FillBoundary at the end of the solve (nonzero enables).
Definition AMReX_MLMG.H:429
typename MLLinOpT< MF >::BCMode BCMode
Definition AMReX_MLMG.H:56
void setConvergenceNormType(MLMGNormType norm) noexcept
Choose the norm used for convergence tests.
Definition AMReX_MLMG.H:422
void setHybridDivergenceFactor(RT f) noexcept
Definition AMReX_MLMG.H:376
void computeResWithCrseSolFineCor(int calev, int falev)
Residual update using coarse solution / fine correction.
Definition AMReX_MLMG.H:2388
MLMGT< MF > & operator=(MLMGT< MF > const &)=delete
MLMGT(MLLinOpT< MF > &a_lp)
Definition AMReX_MLMG.H:859
void computeResidual(int alev)
Compute the residual on AMR level alev.
Definition AMReX_MLMG.H:2378
MLLinOpT< MF > & getLinOp()
Definition AMReX_MLMG.H:730
void getFluxes(std::initializer_list< AMF * > a_flux, Location a_loc=Location::CellCenter)
Initializer-list convenience overload for cell-centered fluxes.
Definition AMReX_MLMG.H:1387
typename MLLinOpT< MF >::FAB FAB
Definition AMReX_MLMG.H:53
RT getBottomToleranceAbs() const noexcept
Definition AMReX_MLMG.H:407
int numAMRLevels() const noexcept
Definition AMReX_MLMG.H:431
MLMGT(MLMGT< MF > const &)=delete
void getFluxes(std::initializer_list< Array< AMF *, 3 > > a_flux, Location a_loc=Location::FaceCenter)
Initializer-list convenience overload for getFluxes (face-centered).
Definition AMReX_MLMG.H:1315
void mgFcycle()
Run an FMG cycle starting from the coarsest grid.
Definition AMReX_MLMG.H:2110
RT getInitRHS() const noexcept
Definition AMReX_MLMG.H:720
void algmgSolve(MLAlgMG &s, MultiFab &x, MultiFab const &b)
RT ResNormInf(int alev, bool local=false)
Infinity norm of the residual on level alev.
Definition AMReX_MLMG.H:2541
Vector< int > const & getNumCGIters() const noexcept
Definition AMReX_MLMG.H:728
amrex::MultigridType MultigridType
Definition AMReX_MLMG.H:60
void bottomSolveWithAlgMG(MultiFab &x, const MultiFab &b)
void bottomSolve()
Execute the configured bottom solver (Hypre, PETSc, CG, etc.).
Definition AMReX_MLMG.H:2162
void setAlgMGOptions(std::function< void(AlgMG< RT > &)> f)
Adjust the AlgMG solvers MLMG builds (level 0 and bottom).
Definition AMReX_MLMG.H:360
void setBottomTolerance(RT t) noexcept
Relative tolerance for the bottom solver.
Definition AMReX_MLMG.H:400
void setFinalSmooth(int n) noexcept
Number of smoothing passes when MLMG is used standalone (final smooth).
Definition AMReX_MLMG.H:327
void addInterpCorrection(int alev, int mglev)
Add interpolated corrections to (alev,mglev) data.
Definition AMReX_MLMG.H:2498
int getVerbose() const
Definition AMReX_MLMG.H:261
RT solve(const Vector< AMF * > &a_sol, const Vector< AMF const * > &a_rhs, RT a_tol_rel, RT a_tol_abs, const char *checkpoint_file=nullptr)
Solve the multilevel system; optional checkpoint_file is for debugging only.
void getFluxes(std::initializer_list< AMF * > a_flux, std::initializer_list< AMF * > a_sol, Location a_loc=Location::CellCenter)
Initializer-list convenience overload for the cell-centered flux helper above.
Definition AMReX_MLMG.H:1453
void setBottomMaxIter(int n) noexcept
Cap the number of iterations inside the bottom solver.
Definition AMReX_MLMG.H:394
void configureAlgMG(MLAlgMG &s, bool singular, bool with_options)
void oneIter(int iter)
Execute a single multigrid iteration (FMG or V-cycle).
Definition AMReX_MLMG.H:1916
void setMaxIter(int n) noexcept
Cap the number of MLMG iterations executed.
Definition AMReX_MLMG.H:288
This class provides the user with a few print options.
Definition AMReX_Print.H:35
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:29
Long size() const noexcept
Definition AMReX_Vector.H:54
static Long Write(const FabArray< FArrayBox > &mf, const std::string &name, VisMF::How how=NFiles, bool set_ghost=false)
Write a FabArray<FArrayBox> to disk in a "smart" way. Returns the total number of bytes written on th...
Definition AMReX_VisMF.cpp:998
amrex_real Real
Floating Point Type for Fields.
Definition AMReX_REAL.H:80
amrex_long Long
Definition AMReX_INT.H:30
__host__ __device__ BoxND< dim > convert(const BoxND< dim > &b, const IntVectND< dim > &typ) noexcept
Return a copy of b converted to the nodal flags typ.
Definition AMReX_Box.H:1632
std::array< T, N > Array
Definition AMReX_Array.H:31
Arena * The_Async_Arena()
Definition AMReX_Arena.cpp:849
void Max(KeyValuePair< K, V > &vi, int root, MPI_Comm comm)
Definition AMReX_ParallelReduce.H:254
void Max(KeyValuePair< K, V > &vi, MPI_Comm comm)
Definition AMReX_ParallelReduce.H:133
MLMGT< MultiFab > MLMG
Definition AMReX_MLMG.H:2919
#define AMREX_ENUM(CLASS,...)
Declare a scoped enum with reflection support.
Definition AMReX_Enum.H:270
std::string getEnumNameString(T const &v)
Get the name string of an enum value.
Definition AMReX_Enum.H:190
constexpr int relax_order
Definition AMReX_Hypre.H:45
constexpr int relax_type
Definition AMReX_Hypre.H:44
constexpr bool old_default
Falgout coarsening with modified classical interpolation.
Definition AMReX_Hypre.H:39
void push(MPI_Comm c)
Definition AMReX_ParallelContext.H:105
void BarrierSub() noexcept
Definition AMReX_ParallelContext.H:88
MPI_Comm CommunicatorSub() noexcept
sub-communicator for current frame
Definition AMReX_ParallelContext.H:70
int MyProcSub() noexcept
my sub-rank in current frame
Definition AMReX_ParallelContext.H:76
void pop()
Note that it's the user's responsibility to free the MPI_Comm.
Definition AMReX_ParallelContext.H:111
bool IOProcessorSub() noexcept
Am IO processor for current frame?
Definition AMReX_ParallelContext.H:80
int verbose
Definition AMReX.cpp:113
Definition AMReX_Amr.cpp:50
MultigridType
How MLMG solves the coarsest AMR level.
Definition AMReX_MLMG.H:34
@ geometric
Definition AMReX_MLMG.H:34
@ algebraic
Definition AMReX_MLMG.H:34
@ hybrid
Definition AMReX_MLMG.H:34
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:273
void FileOpenFailed(const std::string &file)
Output a message and abort when couldn't open the file.
Definition AMReX_Utility.cpp:116
std::array< T const *, 3 > GetArrOfConstPtrs(const std::array< T, 3 > &a) noexcept
Create an array of const-qualified pointers from an array of objects.
Definition AMReX_Array.H:1079
void EB_average_face_to_cellcenter(MultiFab &ccmf, int dcomp, const Array< MultiFab const *, 3 > &fmf)
Average face-centered values to cell centers.
Definition AMReX_EBMultiFabUtil.cpp:809
__host__ __device__ T norm(const GpuComplex< T > &a_z) noexcept
Return the norm (magnitude squared) of a complex number.
Definition AMReX_GpuComplex.H:349
void average_down(const MultiFab &S_fine, MultiFab &S_crse, const Geometry &fgeom, const Geometry &cgeom, int scomp, int ncomp, int rr)
Definition AMReX_MultiFabUtil.cpp:360
void Scale(MF &dst, typename MF::value_type val, int scomp, int ncomp, int nghost)
dst *= val
Definition AMReX_FabArrayUtility.H:2180
BoxArray const & boxArray(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.boxArray().
Definition AMReX_FabArrayBase.cpp:2990
DistributionMapping const & DistributionMap(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.DistributionMap().
Definition AMReX_FabArrayBase.cpp:2995
void average_face_to_cellcenter(MultiFab &cc, int dcomp, const Vector< const MultiFab * > &fc, IntVect const &ng_vect)
Definition AMReX_MultiFabUtil.cpp:157
constexpr bool IsFabArray_v
Definition AMReX_TypeTraits.H:34
void EB_set_covered(MultiFab &mf, Real val)
Fill all covered cells with a single value val.
Definition AMReX_EBMultiFabUtil.cpp:21
constexpr bool IsMultiFabLike_v
Definition AMReX_TypeTraits.H:44
double second() noexcept
Definition AMReX_Utility.cpp:919
std::array< T *, 3 > GetArrOfPtrs(std::array< T, 3 > &a) noexcept
Create an array of pointers from an array of objects.
Definition AMReX_Array.H:1033
int nComp(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.nComp().
Definition AMReX_FabArrayBase.cpp:2980
void ParallelCopy(MF &dst, MF const &src, int scomp, int dcomp, int ncomp, IntVect const &ng_src=IntVect(0), IntVect const &ng_dst=IntVect(0), Periodicity const &period=Periodicity::NonPeriodic())
dst = src w/ MPI communication
Definition AMReX_FabArrayUtility.H:2256
void UtilCreateCleanDirectory(const std::string &path, bool callbarrier=true)
Create a new directory, renaming the old one if it exists.
Definition AMReX_Utility.cpp:146
void EB_average_down(const MultiFab &S_fine, MultiFab &S_crse, const MultiFab &vol_fine, const MultiFab &vfrac_fine, int scomp, int ncomp, const IntVect &ratio)
Volume-weighted average-down from fine to coarse using EB volume fractions.
Definition AMReX_EBMultiFabUtil.cpp:336
void computeResidual(AlgVector< T, AllocV > &res, SpMatrix< T, AllocM > const &A, AlgVector< T, AllocV > const &x, AlgVector< T, AllocV > const &b)
Compute the residual res = b - A * x.
Definition AMReX_SpMV.H:345
@ x
Definition AMReX_Orientation.H:14
BottomSolver
Definition AMReX_MLLinOp.H:42
@ algmg
Definition AMReX_MLLinOp.H:43
@ bicgcg
Definition AMReX_MLLinOp.H:43
@ petsc
Definition AMReX_MLLinOp.H:43
@ cg
Definition AMReX_MLLinOp.H:43
@ Default
Definition AMReX_MLLinOp.H:43
@ custom
Definition AMReX_MLLinOp.H:43
@ cgbicg
Definition AMReX_MLLinOp.H:43
@ hypre
Definition AMReX_MLLinOp.H:43
@ smoother
Definition AMReX_MLLinOp.H:43
@ bicgstab
Definition AMReX_MLLinOp.H:43
Vector< T * > GetVecOfPtrs(Vector< T > &a)
Definition AMReX_Vector.H:65
IntVect nGrowVect(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.nGrowVect().
Definition AMReX_FabArrayBase.cpp:2985
void LocalCopy(DMF &dst, SMF const &src, int scomp, int dcomp, int ncomp, IntVect const &nghost)
dst = src
Definition AMReX_FabArrayUtility.H:2187
IntVectND< 3 > IntVect
IntVect is an alias for amrex::IntVectND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:38
void setBndry(MF &dst, typename MF::value_type val, int scomp, int ncomp)
dst = val in ghost cells.
Definition AMReX_FabArrayUtility.H:2173
MF::value_type norminf(MF const &mf, int scomp, int ncomp, IntVect const &nghost, bool local=false)
Return the infinity norm, with an MPI maximum unless local is true.
Definition AMReX_FabArrayUtility.H:2267
Vector< std::array< T *, 3 > > GetVecOfArrOfPtrs(const Vector< std::array< std::unique_ptr< T >, 3 > > &a)
Definition AMReX_Vector.H:142
void Abort(const std::string &msg)
Print a fatal-error message to stderr and abort execution.
Definition AMReX.cpp:244
MLMGNormType
Definition AMReX_MLMG.H:23
@ resnorm
Definition AMReX_MLMG.H:23
@ bnorm
Definition AMReX_MLMG.H:23
@ greater
Definition AMReX_MLMG.H:23
RealVectND< 3 > RealVect
Definition AMReX_ParmParse.H:39
__host__ __device__ bool isfinite(T x) noexcept
Return true if x is neither NaN nor infinity. Works under fast math.
Definition AMReX_Math.H:155
void LocalAdd(MF &dst, MF const &src, int scomp, int dcomp, int ncomp, IntVect const &nghost)
dst += src
Definition AMReX_FabArrayUtility.H:2195
void setVal(MF &dst, typename MF::value_type val)
dst = val
Definition AMReX_FabArrayUtility.H:2166
FabArray memory allocation information.
Definition AMReX_FabArray.H:73