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
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
63 MLMGT (MLLinOpT<MF>& a_lp);
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>
119 void getGradSolution (const Vector<Array<AMF*,AMREX_SPACEDIM> >& a_grad_sol,
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>
139 void getFluxes (const Vector<Array<AMF*,AMREX_SPACEDIM> >& a_flux,
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>
160 void getFluxes (const Vector<Array<AMF*,AMREX_SPACEDIM> >& a_flux,
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
265 void incPrintIdentation ();
267 void decPrintIdentation ();
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 }
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
505 void prepareForFluxes (Vector<MF const*> const& a_sol);
506
513 template <typename AMF>
514 void prepareForSolve (Vector<AMF*> const& a_sol, Vector<AMF const*> const& a_rhs);
515
517 void prepareForNSolve ();
518
520 void prepareLinOp ();
521
523 void preparePrecond ();
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);
560 void actualBottomSolve ();
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);
596 void computeResWithCrseCorFineCor (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
648 void makeSolvable ();
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>)
703 void algmgLevel0Solve ();
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 std::function<void(AlgMG<RT>&)> m_algmg_options;
764 std::unique_ptr<MLAlgMG> m_algmg_bottom;
765 std::unique_ptr<MLAlgMG> m_algmg_level0;
766
767 int final_fill_bc = 0;
768
769 MLLinOpT<MF>& linop;
770 int ncomp;
771 int namrlevs;
772 int finest_amr_lev;
773
774 bool linop_prepared = false;
775 Long solve_called = 0;
776
778 int do_nsolve = false;
779 int nsolve_grid_size = 16;
780 std::unique_ptr<MLLinOpT<MF>> ns_linop;
781 std::unique_ptr<MLMGT<MF>> ns_mlmg;
782 std::unique_ptr<MF> ns_sol;
783 std::unique_ptr<MF> ns_rhs;
784
785 std::string print_ident;
786
787 bool do_no_sync_gpu = false;
788
790#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
791 // Hypre::Interface hypre_interface = Hypre::Interface::structed;
792 // Hypre::Interface hypre_interface = Hypre::Interface::semi_structed;
793 Hypre::Interface hypre_interface = Hypre::Interface::ij;
794
795 std::unique_ptr<Hypre> hypre_solver;
796 std::unique_ptr<MLMGBndryT<MF>> hypre_bndry;
797 std::unique_ptr<HypreNodeLap> hypre_node_solver;
798
799 std::string hypre_options_namespace = "hypre";
800 bool hypre_old_default = HypreDefaults::old_default;
801 int hypre_relax_type = HypreDefaults::relax_type;
802 int hypre_relax_order = HypreDefaults::relax_order;
803 int hypre_num_sweeps = 2; // Sweeps on each level
804 Real hypre_strong_threshold = 0.25; // HYPRE default is 0.25
805#endif
806
808#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
809 std::unique_ptr<PETScABecLap> petsc_solver;
810 std::unique_ptr<MLMGBndryT<MF>> petsc_bndry;
811#endif
812
817 Vector<MF> sol;
818 Vector<MF> rhs;
820
821 Vector<int> sol_is_alias;
822
827 Vector<Vector<MF> > res;
828 Vector<Vector<MF> > cor;
829 Vector<Vector<MF> > cor_hold;
830 Vector<Vector<MF> > rescor;
832
833 enum timer_types { solve_time=0, iter_time, bottom_time, ntimers };
834 Vector<double> timer;
835
836 RT m_rhsnorm0 = RT(-1.0);
837 RT m_init_resnorm0 = RT(-1.0);
838 RT m_final_resnorm0 = RT(-1.0);
839 Vector<int> m_niters_cg;
840 Vector<RT> m_iter_fine_resnorm0; // Residual for each iteration at the finest level
841
851 void checkPoint (const Vector<MultiFab*>& a_sol,
852 const Vector<MultiFab const*>& a_rhs,
853 RT a_tol_rel, RT a_tol_abs, const char* a_file_name) const;
854
855};
856
857template <typename MF>
859 : linop(a_lp), ncomp(a_lp.getNComp()), namrlevs(a_lp.NAMRLevels()),
860 finest_amr_lev(a_lp.NAMRLevels()-1)
861{}
862
863template <typename MF> MLMGT<MF>::~MLMGT () = default;
864
865template <typename MF>
866void
868{
869 if (flag) {
870 norm_type = MLMGNormType::bnorm;
871 } else {
872 norm_type = MLMGNormType::greater;
873 }
874}
875
876template <typename MF>
877template <typename AMF>
878auto
879MLMGT<MF>::solve (std::initializer_list<AMF*> a_sol,
880 std::initializer_list<AMF const*> a_rhs,
881 RT a_tol_rel, RT a_tol_abs, const char* checkpoint_file) -> RT
882{
883 return solve(Vector<AMF*>(std::move(a_sol)),
884 Vector<AMF const*>(std::move(a_rhs)),
885 a_tol_rel, a_tol_abs, checkpoint_file);
886}
887
888template <typename MF>
889template <typename AMF>
890auto
892 RT a_tol_rel, RT a_tol_abs, const char* checkpoint_file) -> RT
893{
894 BL_PROFILE("MLMG::solve()");
895
896 // If requested with setNoGpuSync(true), run the whole solve in a
897 // single-stream, no-implicit-sync region. The RAII objects restore the
898 // previous state (also when an exception is thrown), and SyncAtExitOnly
899 // synchronizes the GPU streams on exit unless the caller was already in
900 // a NoSync region. The order matters:
901 // no_sync_region is destroyed last, i.e., after the single stream region
902 // has been popped, so that all streams are synchronized.
903 std::optional<Gpu::SyncAtExitOnly> no_sync_region;
904 std::optional<Gpu::SingleStreamRegion> single_stream_region;
905 if (do_no_sync_gpu) {
906 no_sync_region.emplace();
907 single_stream_region.emplace();
908 }
909
910 if constexpr (std::is_same<AMF,MultiFab>()) {
911 if (checkpoint_file != nullptr) {
912 checkPoint(a_sol, a_rhs, a_tol_rel, a_tol_abs, checkpoint_file);
913 }
914 }
915
916 if ((bottom_solver == BottomSolver::custom) && !linop.supportCustomBottomSolver()) {
917 bottom_solver = BottomSolver::Default;
918 }
919
920 if (bottom_solver == BottomSolver::Default) {
921 bottom_solver = linop.getDefaultBottomSolver();
922 }
923
924 // The assembled EB stencils support at most third order.
925 bool eb_linop = false;
926#ifdef AMREX_USE_EB
927 if constexpr (IsFabArray_v<AMF>) {
928 eb_linop = dynamic_cast<EBFArrayBoxFactory const*>(linop.Factory(0)) != nullptr;
929 }
930#endif
931 amrex::ignore_unused(eb_linop);
932
933#if (defined(AMREX_USE_HYPRE) || defined(AMREX_USE_PETSC)) && (AMREX_SPACEDIM > 1)
934 if constexpr (IsFabArray_v<AMF>) {
935 if (bottom_solver == BottomSolver::hypre || bottom_solver == BottomSolver::petsc) {
936 if (eb_linop) {
937 linop.setMaxOrder(std::min(3, linop.getMaxOrder()));
938 }
939#ifdef AMREX_USE_HYPRE
940 else if (bottom_solver == BottomSolver::hypre && hypre_interface != Hypre::Interface::ij) {
941 linop.setMaxOrder(std::min(3, linop.getMaxOrder())); // 4 needs the IJ interface
942 }
943#endif
944 }
945 }
946#endif
947 if (bottom_solver == BottomSolver::algmg || multigrid_type != MultigridType::geometric) {
948 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(linop.supportsAlgMG() && ncomp == 1,
949 "MLMG: AlgMG is not available for this operator (single-component cell and nodal operators only)");
950 // The algebraic correction has zero ghost cells.
951 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(cf_strategy != CFStrategy::ghostnodes || linop.getNGrow() == 0,
952 "MLMG: AlgMG does not support the ghostnodes strategy with ghost cells");
953 // The matrix must match the operator.
954 if (eb_linop) {
955 linop.setMaxOrder(std::min(3, linop.getMaxOrder()));
956 }
957 }
958
959 bool is_nsolve = linop.m_parent;
960
961 m_algmg_active = (multigrid_type == MultigridType::algebraic);
962 const bool hybrid = (multigrid_type == MultigridType::hybrid) && !is_nsolve;
963 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!(do_nsolve && multigrid_type != MultigridType::geometric),
964 "MLMG: NSolve requires the geometric multigrid type");
965 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(!(hybrid && do_fixed_number_of_iters != 0),
966 "MLMG: the hybrid multigrid type needs a convergence test; as a preconditioner or with a fixed number of iterations use geometric or algebraic");
967
968 auto solve_start_time = amrex::second();
969
970 RT& composite_norminf = m_final_resnorm0;
971
972 m_niters_cg.clear();
973 m_iter_fine_resnorm0.clear();
974
975 prepareForSolve(a_sol, a_rhs);
976
977 computeMLResidual(finest_amr_lev);
978
979 bool local = true;
980 RT resnorm0 = MLResNormInf(finest_amr_lev, local);
981 RT rhsnorm0 = MLRhsNormInf(local);
982 if (!is_nsolve) {
983 ParallelAllReduce::Max<RT>({resnorm0, rhsnorm0}, ParallelContext::CommunicatorSub());
984
985 if (verbose >= 1)
986 {
987 amrex::Print() << print_ident << "MLMG: Initial rhs = " << rhsnorm0 << "\n"
988 << print_ident << "MLMG: Initial residual (resid0) = " << resnorm0 << "\n";
989 }
990 }
991
992 m_init_resnorm0 = resnorm0;
993 m_rhsnorm0 = rhsnorm0;
994
995 if (!is_nsolve && !amrex::isfinite(std::max(resnorm0, rhsnorm0))) {
996 if ( throw_exception ) {
997 throw error("MLMG: rhs or initial residual is not finite.");
998 } else {
999 amrex::Abort("MLMG: rhs or initial residual is not finite");
1000 }
1001 }
1002
1003 RT max_norm = resnorm0;
1004 std::string norm_name = "resid0";
1005 switch (norm_type) {
1007 if (rhsnorm0 >= resnorm0) {
1008 norm_name = "bnorm";
1009 max_norm = rhsnorm0;
1010 } else {
1011 norm_name = "resid0";
1012 max_norm = resnorm0;
1013 }
1014 break;
1016 norm_name = "bnorm";
1017 max_norm = rhsnorm0;
1018 break;
1020 norm_name = "resid0";
1021 max_norm = resnorm0;
1022 break;
1023 }
1024
1025 const RT res_target = std::max(a_tol_abs, std::max(a_tol_rel,std::numeric_limits<RT>::epsilon())*max_norm);
1026
1027 if (!is_nsolve && resnorm0 <= res_target) {
1028 composite_norminf = resnorm0;
1029 if (verbose >= 1) {
1030 amrex::Print() << print_ident << "MLMG: No iterations needed\n";
1031 }
1032 } else {
1033 auto iter_start_time = amrex::second();
1034 bool converged = false;
1035
1036 // Hybrid: keep the best iterate so far, starting with the initial guess.
1037 // The switch tests use the residual MLMG reports: the finest level
1038 // until it converges, then the composite one.
1039 Vector<MF> best_sol;
1040 Vector<RT> hybrid_norms;
1041 bool hybrid_composite = false; // the reported residual became the composite one
1042 RT best_norm = resnorm0;
1043 if (hybrid) {
1044 if (namrlevs > 1) { best_norm = ResNormInf(finest_amr_lev); }
1045 best_sol.resize(namrlevs);
1046 for (int alev = 0; alev < namrlevs; ++alev) {
1047 const IntVect ng = nGrowVect(sol[alev]);
1048 best_sol[alev] = linop.make(alev, 0, ng);
1049 LocalCopy(best_sol[alev], sol[alev], 0, 0, ncomp, ng);
1050 }
1051 }
1052
1053 int niters = do_fixed_number_of_iters ? do_fixed_number_of_iters : max_iters;
1054 for (int iter = 0; iter < niters; ++iter)
1055 {
1056 oneIter(iter);
1057
1058 converged = false;
1059
1060 // Test convergence on the fine amr level
1061 computeResidual(finest_amr_lev);
1062
1063 if (is_nsolve) { continue; }
1064
1065 RT fine_norminf = ResNormInf(finest_amr_lev);
1066 m_iter_fine_resnorm0.push_back(fine_norminf);
1067 composite_norminf = fine_norminf;
1068 if (verbose >= 2) {
1069 amrex::Print() << print_ident << "MLMG: Iteration " << std::setw(3) << iter+1 << " Fine resid/"
1070 << norm_name << " = " << fine_norminf/max_norm << "\n";
1071 }
1072 bool fine_converged = (fine_norminf <= res_target);
1073
1074 if (namrlevs == 1 && fine_converged) {
1075 converged = true;
1076 } else if (fine_converged) {
1077 // finest level is converged, but we still need to test the coarse levels
1078 computeMLResidual(finest_amr_lev-1);
1079 RT crse_norminf = MLResNormInf(finest_amr_lev-1);
1080 if (verbose >= 2) {
1081 amrex::Print() << print_ident << "MLMG: Iteration " << std::setw(3) << iter+1
1082 << " Crse resid/" << norm_name << " = "
1083 << crse_norminf/max_norm << "\n";
1084 }
1085 converged = (crse_norminf <= res_target);
1086 composite_norminf = std::max(fine_norminf, crse_norminf);
1087 } else {
1088 converged = false;
1089 }
1090
1091 if (converged) {
1092 if (verbose >= 1) {
1093 amrex::Print() << print_ident << "MLMG: Final Iter. " << iter+1
1094 << " resid, resid/" << norm_name << " = "
1095 << composite_norminf << ", "
1096 << composite_norminf/max_norm << "\n";
1097 }
1098 break;
1099 } else {
1100 if (hybrid && !m_algmg_active) {
1101 if (fine_converged && !hybrid_composite) {
1102 // The reported residual now includes the coarse levels:
1103 // restart the comparisons from this iterate.
1104 hybrid_composite = true;
1105 hybrid_norms.clear();
1106 best_norm = composite_norminf;
1107 for (int alev = 0; alev < namrlevs; ++alev) {
1108 LocalCopy(best_sol[alev], sol[alev], 0, 0, ncomp, nGrowVect(sol[alev]));
1109 }
1110 }
1111 hybrid_norms.push_back(composite_norminf);
1112 std::string reason;
1113 bool do_switch = hybridShouldSwitch(hybrid_norms, max_norm,
1114 best_norm, reason);
1115 if (!do_switch && iter+1 == niters) {
1116 do_switch = true;
1117 reason = "not converged";
1118 }
1119 if (do_switch) {
1120 if (verbose >= 1) {
1121 amrex::Print() << print_ident << "MLMG: " << reason << " after "
1122 << iter+1 << " iterations, switching to AlgMG on AMR level 0\n";
1123 }
1124 if (!amrex::isfinite(composite_norminf) || composite_norminf > best_norm) {
1125 for (int alev = 0; alev < namrlevs; ++alev) {
1126 LocalCopy(sol[alev], best_sol[alev], 0, 0, ncomp, nGrowVect(sol[alev]));
1127 }
1128 computeResidual(finest_amr_lev);
1129 composite_norminf = best_norm;
1130 if (verbose >= 2) {
1131 amrex::Print() << print_ident << "MLMG: restarting from the best iterate, resid/"
1132 << norm_name << " = " << best_norm/max_norm << "\n";
1133 }
1134 }
1135 m_algmg_active = true;
1136 niters = iter + 1 + max_iters;
1137 continue;
1138 }
1139 if (composite_norminf < best_norm) {
1140 best_norm = composite_norminf;
1141 for (int alev = 0; alev < namrlevs; ++alev) {
1142 LocalCopy(best_sol[alev], sol[alev], 0, 0, ncomp, nGrowVect(sol[alev]));
1143 }
1144 }
1145 }
1146 if (composite_norminf > RT(1.e20)*max_norm || !amrex::isfinite(composite_norminf))
1147 {
1148 if (verbose > 0) {
1149 amrex::Print() << print_ident << "MLMG: Failing to converge after " << iter+1 << " iterations."
1150 << " resid, resid/" << norm_name << " = "
1151 << composite_norminf << ", "
1152 << composite_norminf/max_norm << "\n";
1153 }
1154
1155 if ( throw_exception ) {
1156 throw error("MLMG blew up.");
1157 } else {
1158 amrex::Abort("MLMG failing so lets stop here");
1159 }
1160 }
1161 }
1162 }
1163
1164 if (!converged && do_fixed_number_of_iters == 0) {
1165 if (verbose > 0) {
1166 amrex::Print() << print_ident << "MLMG: Failed to converge after " << niters << " iterations."
1167 << " resid, resid/" << norm_name << " = "
1168 << composite_norminf << ", "
1169 << composite_norminf/max_norm << "\n";
1170 }
1171
1172 if ( throw_exception ) {
1173 throw error("MLMG failed to converge.");
1174 } else {
1175 amrex::Abort("MLMG failed.");
1176 }
1177 }
1178 timer[iter_time] = amrex::second() - iter_start_time;
1179 }
1180
1181 linop.postSolve(GetVecOfPtrs(sol));
1182
1183 IntVect ng_back = final_fill_bc ? IntVect(1) : IntVect(0);
1184 if (linop.hasHiddenDimension()) {
1185 ng_back[linop.hiddenDirection()] = 0;
1186 }
1187 for (int alev = 0; alev < namrlevs; ++alev)
1188 {
1189 if (!sol_is_alias[alev]) {
1190 LocalCopy(*a_sol[alev], sol[alev], 0, 0, ncomp, ng_back);
1191 }
1192 }
1193
1194 timer[solve_time] = amrex::second() - solve_start_time;
1195 if (verbose >= 1) {
1196 ParallelReduce::Max<double>(timer.data(), timer.size(), 0,
1198 if (ParallelContext::MyProcSub() == 0)
1199 {
1200 amrex::AllPrint() << print_ident << "MLMG: Timers: Solve = " << timer[solve_time]
1201 << " Iter = " << timer[iter_time]
1202 << " Bottom = " << timer[bottom_time] << "\n";
1203 }
1204 }
1205
1206 ++solve_called;
1207
1208 return composite_norminf;
1209}
1210
1211template <typename MF>
1212auto
1214 RT a_tol_rel, RT a_tol_abs) -> RT
1215{
1216 precond_mode = true;
1217 std::swap(max_precond_iters, do_fixed_number_of_iters);
1218 linop.beginPrecondBC();
1219
1220 auto r = solve(a_sol, a_rhs, a_tol_rel, a_tol_abs);
1221
1222 linop.endPrecondBC();
1223 std::swap(max_precond_iters, do_fixed_number_of_iters);
1224 precond_mode = false;
1225
1226 return r;
1227}
1228
1229template <typename MF>
1230void
1232{
1233 for (int alev = finest_amr_lev; alev >= 0; --alev) {
1234 const MF* crse_bcdata = (alev > 0) ? a_sol[alev-1] : nullptr;
1235 linop.prepareForFluxes(alev, crse_bcdata);
1236 }
1237}
1238
1239template <typename MF>
1240template <typename AMF>
1241void
1243{
1244 BL_PROFILE("MLMG::getGradSolution()");
1245 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1246 if constexpr (std::is_same<AMF,MF>()) {
1247 linop.compGrad(alev, a_grad_sol[alev], sol[alev], a_loc);
1248 } else {
1249 Array<MF,AMREX_SPACEDIM> grad_sol;
1250 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1251 auto const& amf = *(a_grad_sol[alev][idim]);
1252 grad_sol[idim].define(boxArray(amf), DistributionMap(amf), ncomp, 0);
1253 }
1254 linop.compGrad(alev, GetArrOfPtrs(grad_sol), sol[alev], a_loc);
1255 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1256 LocalCopy(*a_grad_sol[alev][idim], grad_sol[idim], 0, 0, ncomp, IntVect(0));
1257 }
1258 }
1259 }
1260}
1261
1262template <typename MF>
1263template <typename AMF>
1264void
1265MLMGT<MF>::getGradSolution (std::initializer_list<Array<AMF*,AMREX_SPACEDIM>> a_grad_sol, Location a_loc)
1266{
1267 getGradSolution(Vector<Array<AMF*,AMREX_SPACEDIM>>(std::move(a_grad_sol)), a_loc);
1268}
1269
1270template <typename MF>
1271template <typename AMF>
1272void
1274 Location a_loc)
1275{
1276 if (!linop.isCellCentered()) {
1277 amrex::Abort("Calling wrong getFluxes for nodal solver");
1278 }
1279
1280 AMREX_ASSERT(sol.size() == a_flux.size());
1281
1282 if constexpr (std::is_same<AMF,MF>()) {
1283 getFluxes(a_flux, GetVecOfPtrs(sol), a_loc);
1284 } else {
1285 Vector<Array<MF,AMREX_SPACEDIM>> fluxes(namrlevs);
1286 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1287 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1288 auto const& amf = *(a_flux[ilev][idim]);
1289 fluxes[ilev][idim].define(boxArray(amf), DistributionMap(amf), ncomp, 0);
1290 }
1291 }
1292 getFluxes(GetVecOfArrOfPtrs(fluxes), GetVecOfPtrs(sol), a_loc);
1293 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1294 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1295 LocalCopy(*a_flux[ilev][idim], fluxes[ilev][idim], 0, 0, ncomp, IntVect(0));
1296 }
1297 }
1298 }
1299}
1300
1301template <typename MF>
1302template <typename AMF>
1303void
1305 Location a_loc)
1306{
1307 getFluxes(Vector<Array<AMF*,AMREX_SPACEDIM>>(std::move(a_flux)), a_loc);
1308}
1309
1310template <typename MF>
1311template <typename AMF>
1312void
1314 const Vector<AMF*>& a_sol, Location a_loc)
1315{
1316 BL_PROFILE("MLMG::getFluxes()");
1317
1318 if (!linop.isCellCentered()) {
1319 amrex::Abort("Calling wrong getFluxes for nodal solver");
1320 }
1321
1322 if constexpr (std::is_same<AMF,MF>()) {
1323 linop.getFluxes(a_flux, a_sol, a_loc);
1324 } else {
1325 Vector<Array<MF,AMREX_SPACEDIM>> fluxes(namrlevs);
1326 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1327 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1328 auto const& amf = *(a_flux[ilev][idim]);
1329 fluxes[ilev][idim].define(boxArray(amf), DistributionMap(amf), ncomp, 0);
1330 }
1331 LocalCopy(sol[ilev], *a_sol[ilev], 0, 0, ncomp, nGrowVect(sol[ilev]));
1332 }
1333 linop.getFluxes(GetVecOfArrOfPtrs(fluxes), GetVecOfPtrs(sol), a_loc);
1334 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1335 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1336 LocalCopy(*a_flux[ilev][idim], fluxes[ilev][idim], 0, 0, ncomp, IntVect(0));
1337 }
1338 }
1339 }
1340}
1341
1342template <typename MF>
1343template <typename AMF>
1344void
1346 std::initializer_list<AMF*> a_sol, Location a_loc)
1347{
1348 getFluxes(Vector<Array<AMF*,AMREX_SPACEDIM>>(std::move(a_flux)),
1349 Vector<AMF*>(std::move(a_sol)), a_loc);
1350}
1351
1352template <typename MF>
1353template <typename AMF>
1354void
1356{
1357 AMREX_ASSERT(sol.size() == a_flux.size());
1358 if constexpr (std::is_same<AMF,MF>()) {
1359 getFluxes(a_flux, GetVecOfPtrs(sol), a_loc);
1360 } else {
1361 Vector<MF> fluxes(namrlevs);
1362 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1363 auto const& amf = *a_flux[ilev];
1364 fluxes[ilev].define(boxArray(amf), DistributionMap(amf), AMREX_SPACEDIM, 0);
1365 }
1366 getFluxes(GetVecOfPtrs(fluxes), GetVecOfPtrs(sol), a_loc);
1367 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1368 LocalCopy(*a_flux[ilev], fluxes[ilev], 0, 0, AMREX_SPACEDIM, IntVect(0));
1369 }
1370 }
1371}
1372
1373template <typename MF>
1374template <typename AMF>
1375void
1376MLMGT<MF>::getFluxes (std::initializer_list<AMF*> a_flux, Location a_loc)
1377{
1378 getFluxes(Vector<AMF*>(std::move(a_flux)), a_loc);
1379}
1380
1381template <typename MF>
1382template <typename AMF>
1383void
1385 const Vector<AMF*>& a_sol, Location /*a_loc*/)
1386{
1387 AMREX_ASSERT(nComp(*a_flux[0]) >= AMREX_SPACEDIM);
1388
1389 if constexpr (! std::is_same<AMF,MF>()) {
1390 for (int alev = 0; alev < namrlevs; ++alev) {
1391 LocalCopy(sol[alev], *a_sol[alev], 0, 0, ncomp, nGrowVect(sol[alev]));
1392 }
1393 }
1394
1395 if (linop.isCellCentered())
1396 {
1397 Vector<Array<MF,AMREX_SPACEDIM> > ffluxes(namrlevs);
1398 for (int alev = 0; alev < namrlevs; ++alev) {
1399 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1400 const int mglev = 0;
1401 int nghost = 0;
1402 if (cf_strategy == CFStrategy::ghostnodes) { nghost = linop.getNGrow(alev); }
1403 ffluxes[alev][idim].define(amrex::convert(linop.m_grids[alev][mglev],
1405 linop.m_dmap[alev][mglev], ncomp, nghost, MFInfo(),
1406 *linop.m_factory[alev][mglev]);
1407 }
1408 }
1409 if constexpr (std::is_same<AMF,MF>()) {
1410 getFluxes(amrex::GetVecOfArrOfPtrs(ffluxes), a_sol, Location::FaceCenter);
1411 } else {
1412 getFluxes(amrex::GetVecOfArrOfPtrs(ffluxes), GetVecOfPtrs(sol), Location::FaceCenter);
1413 }
1414 for (int alev = 0; alev < namrlevs; ++alev) {
1415#ifdef AMREX_USE_EB
1416 EB_average_face_to_cellcenter(*a_flux[alev], 0, amrex::GetArrOfConstPtrs(ffluxes[alev]));
1417#else
1418 average_face_to_cellcenter(*a_flux[alev], 0, amrex::GetArrOfConstPtrs(ffluxes[alev]));
1419#endif
1420 }
1421
1422 } else {
1423 if constexpr (std::is_same<AMF,MF>()) {
1424 linop.getFluxes(a_flux, a_sol);
1425 } else {
1426 Vector<MF> fluxes(namrlevs);
1427 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1428 auto const& amf = *a_flux[ilev];
1429 fluxes[ilev].define(boxArray(amf), DistributionMap(amf), AMREX_SPACEDIM, 0);
1430 }
1431 linop.getFluxes(GetVecOfPtrs(fluxes), GetVecOfPtrs(sol));
1432 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1433 LocalCopy(*a_flux[ilev], fluxes[ilev], 0, 0, AMREX_SPACEDIM, IntVect(0));
1434 }
1435 }
1436 }
1437}
1438
1439template <typename MF>
1440template <typename AMF>
1441void
1442MLMGT<MF>::getFluxes (std::initializer_list<AMF*> a_flux,
1443 std::initializer_list<AMF*> a_sol, Location a_loc)
1444{
1445 getFluxes(Vector<AMF*>(std::move(a_flux)),
1446 Vector<AMF*>(std::move(a_sol)), a_loc);
1447}
1448
1449#ifdef AMREX_USE_EB
1450template <typename MF>
1451void
1453{
1454 if (!linop.isCellCentered()) {
1455 amrex::Abort("getEBFluxes is for cell-centered only");
1456 }
1457
1458 AMREX_ASSERT(sol.size() == a_eb_flux.size());
1459 getEBFluxes(a_eb_flux, GetVecOfPtrs(sol));
1460}
1461
1462template <typename MF>
1463void
1464MLMGT<MF>::getEBFluxes (const Vector<MF*>& a_eb_flux, const Vector<MF*>& a_sol)
1465{
1466 BL_PROFILE("MLMG::getEBFluxes()");
1467
1468 if (!linop.isCellCentered()) {
1469 amrex::Abort("getEBFluxes is for cell-centered only");
1470 }
1471
1472 linop.getEBFluxes(a_eb_flux, a_sol);
1473}
1474#endif
1475
1476template <typename MF>
1477void
1479 const Vector<MF const*>& a_rhs)
1480{
1481 BL_PROFILE("MLMG::compResidual()");
1482
1483 IntVect ng_sol(1);
1484 if (linop.hasHiddenDimension()) { ng_sol[linop.hiddenDirection()] = 0; }
1485
1486 sol.resize(namrlevs);
1487 sol_is_alias.resize(namrlevs,true);
1488 for (int alev = 0; alev < namrlevs; ++alev)
1489 {
1490 if (cf_strategy == CFStrategy::ghostnodes || nGrowVect(*a_sol[alev]) == ng_sol)
1491 {
1492 sol[alev] = linop.makeAlias(*a_sol[alev]);
1493 sol_is_alias[alev] = true;
1494 }
1495 else
1496 {
1497 if (sol_is_alias[alev])
1498 {
1499 sol[alev] = linop.make(alev, 0, ng_sol);
1500 sol_is_alias[alev] = false;
1501 }
1502 LocalCopy(sol[alev], *a_sol[alev], 0, 0, ncomp, IntVect(0));
1503 }
1504 }
1505
1506 prepareLinOp();
1507
1508
1509 for (int alev = finest_amr_lev; alev >= 0; --alev) {
1510 const MF* crse_bcdata = (alev > 0) ? &(sol[alev-1]) : nullptr;
1511 const MF* prhs = a_rhs[alev];
1512#if (AMREX_SPACEDIM != 3)
1513 int nghost = (cf_strategy == CFStrategy::ghostnodes) ? linop.getNGrow(alev) : 0;
1514 MF rhstmp(boxArray(*prhs), DistributionMap(*prhs), ncomp, nghost,
1515 MFInfo(), *linop.Factory(alev));
1516 LocalCopy(rhstmp, *prhs, 0, 0, ncomp, IntVect(nghost));
1517 linop.applyMetricTerm(alev, 0, rhstmp);
1518 linop.unimposeNeumannBC(alev, rhstmp);
1519 linop.applyInhomogNeumannTerm(alev, rhstmp);
1520 prhs = &rhstmp;
1521#endif
1522 linop.solutionResidual(alev, *a_res[alev], sol[alev], *prhs, crse_bcdata);
1523 if (alev < finest_amr_lev) {
1524 linop.reflux(alev, *a_res[alev], sol[alev], *prhs,
1525 *a_res[alev+1], sol[alev+1], *a_rhs[alev+1]);
1526 if (linop.isCellCentered()) {
1527#ifdef AMREX_USE_EB
1528 EB_average_down(*a_res[alev+1], *a_res[alev], 0, ncomp, linop.AMRRefRatioVect(alev));
1529#else
1530 average_down(*a_res[alev+1], *a_res[alev], 0, ncomp, linop.AMRRefRatioVect(alev));
1531#endif
1532 }
1533 }
1534 }
1535
1536
1537#if (AMREX_SPACEDIM != 3)
1538 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1539 linop.unapplyMetricTerm(alev, 0, *a_res[alev]);
1540 }
1541#endif
1542}
1543
1544template <typename MF>
1545void
1547{
1548 BL_PROFILE("MLMG::apply()");
1549
1550 Vector<MF*> in(namrlevs);
1551 Vector<MF> in_raii(namrlevs);
1552 Vector<MF> rh(namrlevs);
1553 int nghost = 0;
1554 IntVect ng_sol(1);
1555 if (linop.hasHiddenDimension()) { ng_sol[linop.hiddenDirection()] = 0; }
1556
1557 for (int alev = 0; alev < namrlevs; ++alev)
1558 {
1559 if (cf_strategy == CFStrategy::ghostnodes)
1560 {
1561 nghost = linop.getNGrow(alev);
1562 in[alev] = a_in[alev];
1563 }
1564 else if (nGrowVect(*a_in[alev]) == ng_sol)
1565 {
1566 in[alev] = a_in[alev];
1567 }
1568 else
1569 {
1570 IntVect ng = ng_sol;
1571 if (cf_strategy == CFStrategy::ghostnodes) { ng = IntVect(nghost); }
1572 in_raii[alev] = linop.make(alev, 0, ng,
1573 MFInfo().SetArena(The_Async_Arena()));
1574 LocalCopy(in_raii[alev], *a_in[alev], 0, 0, ncomp, IntVect(nghost));
1575 in[alev] = &(in_raii[alev]);
1576 }
1577 rh[alev] = linop.make(alev, 0, IntVect(nghost),
1579 setVal(rh[alev], RT(0.0));
1580 }
1581
1582 prepareLinOp();
1583
1584 for (int alev = 0; alev < namrlevs; ++alev) {
1585 linop.applyInhomogNeumannTerm(alev, rh[alev]);
1586 }
1587
1588
1589 for (int alev = finest_amr_lev; alev >= 0; --alev) {
1590 const MF* crse_bcdata = (alev > 0) ? in[alev-1] : nullptr;
1591 linop.solutionResidual(alev, *out[alev], *in[alev], rh[alev], crse_bcdata);
1592 if (alev < finest_amr_lev) {
1593 linop.reflux(alev, *out[alev], *in[alev], rh[alev],
1594 *out[alev+1], *in[alev+1], rh[alev+1]);
1595 if (linop.isCellCentered()) {
1596 if constexpr (IsMultiFabLike_v<MF>) {
1597#ifdef AMREX_USE_EB
1598 EB_average_down(*out[alev+1], *out[alev], 0, nComp(*out[alev]), linop.AMRRefRatioVect(alev));
1599#else
1600 average_down(*out[alev+1], *out[alev], 0, nComp(*out[alev]), linop.AMRRefRatioVect(alev));
1601#endif
1602 } else {
1603 amrex::Abort("MLMG: TODO average_down for non-MultiFab");
1604 }
1605 }
1606 }
1607 }
1608
1609#if (AMREX_SPACEDIM != 3)
1610 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1611 linop.unapplyMetricTerm(alev, 0, *out[alev]);
1612 }
1613#endif
1614
1615 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1616 if (cf_strategy == CFStrategy::ghostnodes) { nghost = linop.getNGrow(alev); }
1617 Scale(*out[alev], RT(-1), 0, nComp(*out[alev]), nghost);
1618 }
1619}
1620
1621template <typename MF>
1622void
1624{
1625 precond_mode = true;
1626 linop.beginPrecondBC();
1627 apply(out, in);
1628 linop.endPrecondBC();
1629 precond_mode = false;
1630}
1631
1632template <typename MF>
1633template <typename AMF>
1634void
1636{
1637 BL_PROFILE("MLMG::prepareForSolve()");
1638
1639 AMREX_ASSERT(namrlevs <= a_sol.size());
1640 AMREX_ASSERT(namrlevs <= a_rhs.size());
1641
1642 timer.assign(ntimers, 0.0);
1643
1644 IntVect ng_rhs(0);
1645 IntVect ng_sol(1);
1646 if (linop.hasHiddenDimension()) { ng_sol[linop.hiddenDirection()] = 0; }
1647
1648 linop.setPrintIndentation(print_ident);
1649 if (!linop_prepared) {
1650 linop.prepareForSolve();
1651 AMREX_ASSERT(!linop.m_mg_deferred || linop.m_mg_built);
1652 linop_prepared = true;
1653 } else if (linop.needsUpdate()) {
1654 linop.update();
1655
1656 m_algmg_bottom.reset();
1657 m_algmg_level0.reset();
1658
1659#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
1660 hypre_solver.reset();
1661 hypre_bndry.reset();
1662 hypre_node_solver.reset();
1663#endif
1664
1665#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
1666 petsc_solver.reset();
1667 petsc_bndry.reset();
1668#endif
1669 }
1670
1671 sol.resize(namrlevs);
1672 sol_is_alias.resize(namrlevs,false);
1673 for (int alev = 0; alev < namrlevs; ++alev)
1674 {
1675 if (cf_strategy == CFStrategy::ghostnodes)
1676 {
1677 if constexpr (std::is_same<AMF,MF>()) {
1678 sol[alev] = linop.makeAlias(*a_sol[alev]);
1679 sol_is_alias[alev] = true;
1680 } else {
1681 amrex::Abort("Type conversion not supported for CFStrategy::ghostnodes");
1682 }
1683 }
1684 else
1685 {
1686 bool alias_made = false;
1687 if (nGrowVect(*a_sol[alev]) == ng_sol) {
1688 if constexpr (std::is_same<AMF,MF>()) {
1689 sol[alev] = linop.makeAlias(*a_sol[alev]);
1690 sol_is_alias[alev] = true;
1691 alias_made = true;
1692 }
1693 }
1694 if (!alias_made) {
1695 if (sol_is_alias[alev] || !solve_called) {
1696 sol[alev] = linop.make(alev, 0, ng_sol);
1697 sol_is_alias[alev] = false;
1698 }
1699 LocalCopy(sol[alev], *a_sol[alev], 0, 0, ncomp, IntVect(0));
1700 setBndry(sol[alev], RT(0.0), 0, ncomp);
1701 }
1702 }
1703 }
1704
1705 rhs.resize(namrlevs);
1706 for (int alev = 0; alev < namrlevs; ++alev)
1707 {
1708 if (cf_strategy == CFStrategy::ghostnodes) { ng_rhs = IntVect(linop.getNGrow(alev)); }
1709 if (!solve_called) {
1710 rhs[alev] = linop.make(alev, 0, ng_rhs);
1711 }
1712 LocalCopy(rhs[alev], *a_rhs[alev], 0, 0, ncomp, ng_rhs);
1713 linop.applyMetricTerm(alev, 0, rhs[alev]);
1714 linop.unimposeNeumannBC(alev, rhs[alev]);
1715 linop.applyInhomogNeumannTerm(alev, rhs[alev]);
1716 linop.applyOverset(alev, rhs[alev]);
1717 if ( ! precond_mode) {
1718 bool r = linop.scaleRHS(alev, &(rhs[alev]));
1720 }
1721
1722#ifdef AMREX_USE_EB
1723 const auto *factory = dynamic_cast<EBFArrayBoxFactory const*>(linop.Factory(alev));
1724 if (factory && !factory->isAllRegular()) {
1725 if constexpr (std::is_same<MF,MultiFab>()) {
1726 EB_set_covered(rhs[alev], 0, ncomp, 0, RT(0.0));
1727 EB_set_covered(sol[alev], 0, ncomp, 0, RT(0.0));
1728 } else {
1729 amrex::Abort("TODO: MLMG with EB only works with MultiFab");
1730 }
1731 }
1732#endif
1733 }
1734
1735 for (int falev = finest_amr_lev; falev > 0; --falev)
1736 {
1737 linop.averageDownSolutionRHS(falev-1, sol[falev-1], rhs[falev-1], sol[falev], rhs[falev]);
1738 }
1739
1740 // enforce solvability if appropriate
1741 if (linop.isSingular(0) && linop.getEnforceSingularSolvable())
1742 {
1743 makeSolvable();
1744 }
1745
1746 IntVect ng = linop.getNGrowVectRestriction();
1747 if (cf_strategy == CFStrategy::ghostnodes) { ng = ng_rhs; }
1748 if (!solve_called) {
1749 linop.make(res, ng);
1750 linop.make(rescor, ng);
1751 }
1752 for (int alev = 0; alev <= finest_amr_lev; ++alev)
1753 {
1754 const int nmglevs = linop.NMGLevels(alev);
1755 for (int mglev = 0; mglev < nmglevs; ++mglev)
1756 {
1757 setVal(res [alev][mglev], RT(0.0));
1758 setVal(rescor[alev][mglev], RT(0.0));
1759 }
1760 }
1761
1762 if (cf_strategy != CFStrategy::ghostnodes) { ng = ng_sol; }
1763 cor.resize(namrlevs);
1764 for (int alev = 0; alev <= finest_amr_lev; ++alev)
1765 {
1766 const int nmglevs = linop.NMGLevels(alev);
1767 cor[alev].resize(nmglevs);
1768 for (int mglev = 0; mglev < nmglevs; ++mglev)
1769 {
1770 if (!solve_called) {
1771 IntVect _ng = ng;
1772 if (cf_strategy == CFStrategy::ghostnodes) { _ng=IntVect(linop.getNGrow(alev,mglev)); }
1773 cor[alev][mglev] = linop.make(alev, mglev, _ng);
1774 }
1775 setVal(cor[alev][mglev], RT(0.0));
1776 }
1777 }
1778
1779 cor_hold.resize(std::max(namrlevs-1,1));
1780 {
1781 const int alev = 0;
1782 const int nmglevs = linop.NMGLevels(alev);
1783 cor_hold[alev].resize(nmglevs);
1784 for (int mglev = 0; mglev < nmglevs-1; ++mglev)
1785 {
1786 if (!solve_called) {
1787 IntVect _ng = ng;
1788 if (cf_strategy == CFStrategy::ghostnodes) { _ng=IntVect(linop.getNGrow(alev,mglev)); }
1789 cor_hold[alev][mglev] = linop.make(alev, mglev, _ng);
1790 }
1791 setVal(cor_hold[alev][mglev], RT(0.0));
1792 }
1793 }
1794 for (int alev = 1; alev < finest_amr_lev; ++alev)
1795 {
1796 cor_hold[alev].resize(1);
1797 if (!solve_called) {
1798 IntVect _ng = ng;
1799 if (cf_strategy == CFStrategy::ghostnodes) { _ng=IntVect(linop.getNGrow(alev)); }
1800 cor_hold[alev][0] = linop.make(alev, 0, _ng);
1801 }
1802 setVal(cor_hold[alev][0], RT(0.0));
1803 }
1804
1805 if (linop.m_parent // no embedded N-Solve
1806 || !linop.supportNSolve())
1807 {
1808 do_nsolve = false;
1809 }
1810
1811 if (do_nsolve && ns_linop == nullptr)
1812 {
1813 prepareForNSolve();
1814 }
1815
1816 if (verbose >= 2) {
1817 amrex::Print() << print_ident << "MLMG: # of AMR levels: " << namrlevs << "\n"
1818 << print_ident << " # of MG levels on the coarsest AMR level: " << linop.NMGLevels(0)
1819 << "\n";
1820 if (ns_linop) {
1821 amrex::Print() << print_ident << " # of MG levels in N-Solve: " << ns_linop->NMGLevels(0) << "\n"
1822 << print_ident << " # of grids in N-Solve: " << ns_linop->m_grids[0][0].size() << "\n";
1823 }
1824 }
1825}
1826
1827template <typename MF>
1828void
1830{
1831 linop.setPrintIndentation(print_ident);
1832 if (!linop_prepared) {
1833 linop.prepareForSolve();
1834 AMREX_ASSERT(!linop.m_mg_deferred || linop.m_mg_built);
1835 linop_prepared = true;
1836 } else if (linop.needsUpdate()) {
1837 linop.update();
1838
1839 m_algmg_bottom.reset();
1840 m_algmg_level0.reset();
1841
1842#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
1843 hypre_solver.reset();
1844 hypre_bndry.reset();
1845 hypre_node_solver.reset();
1846#endif
1847
1848#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
1849 petsc_solver.reset();
1850 petsc_bndry.reset();
1851#endif
1852 }
1853}
1854
1855template <typename MF>
1856void
1858{
1859 prepareLinOp();
1860 linop.preparePrecond();
1861}
1862
1863template <typename MF>
1864void
1866{
1867 if constexpr (IsMultiFabLike_v<MF>) {
1868 ns_linop = linop.makeNLinOp(nsolve_grid_size);
1869
1870 int nghost = 0;
1871 if (cf_strategy == CFStrategy::ghostnodes) { nghost = linop.getNGrow(); }
1872
1873 const BoxArray& ba = (*ns_linop).m_grids[0][0];
1874 const DistributionMapping& dm =(*ns_linop).m_dmap[0][0];
1875
1876 int ng = 1;
1877 if (cf_strategy == CFStrategy::ghostnodes) { ng = nghost; }
1878 ns_sol = std::make_unique<MF>(ba, dm, ncomp, ng, MFInfo(), *(ns_linop->Factory(0,0)));
1879 ng = 0;
1880 if (cf_strategy == CFStrategy::ghostnodes) { ng = nghost; }
1881 ns_rhs = std::make_unique<MF>(ba, dm, ncomp, ng, MFInfo(), *(ns_linop->Factory(0,0)));
1882 setVal(*ns_sol, RT(0.0));
1883 setVal(*ns_rhs, RT(0.0));
1884
1885 ns_linop->setLevelBC(0, ns_sol.get());
1886
1887 ns_mlmg = std::make_unique<MLMGT<MF>>(*ns_linop);
1888 ns_mlmg->setVerbose(0);
1889 ns_mlmg->setFixedIter(1);
1890 ns_mlmg->setMaxFmgIter(20);
1891 ns_mlmg->setBottomSolver(BottomSolver::smoother);
1892 }
1893}
1894
1895// in : Residual (res) on the finest AMR level
1896// out : sol on all AMR levels
1897template <typename MF>
1898void MLMGT<MF>::oneIter (int iter)
1899{
1900 BL_PROFILE("MLMG::oneIter()");
1901
1902 for (int alev = finest_amr_lev; alev > 0; --alev)
1903 {
1904 miniCycle(alev);
1905
1906 IntVect nghost(0);
1907 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(alev)); }
1908 LocalAdd(sol[alev], cor[alev][0], 0, 0, ncomp, nghost);
1909
1910 // compute residual for the coarse AMR level
1911 computeResWithCrseSolFineCor(alev-1,alev);
1912
1913 if (alev != finest_amr_lev) {
1914 std::swap(cor_hold[alev][0], cor[alev][0]); // save it for the up cycle
1915 }
1916 }
1917
1918 // coarsest amr level
1919 {
1920 // enforce solvability if appropriate
1921 if (linop.isSingular(0) && linop.getEnforceSingularSolvable())
1922 {
1923 makeSolvable(0,0,res[0][0]);
1924 }
1925
1926 if (m_algmg_active) {
1927 if constexpr (std::is_same<MF,MultiFab>()) {
1928 algmgLevel0Solve();
1929 } else {
1930 amrex::Abort("MLMG: AlgMG is only supported for MultiFab");
1931 }
1932 } else if (iter < max_fmg_iters) {
1933 mgFcycle();
1934 } else {
1935 mgVcycle(0, 0);
1936 }
1937
1938 IntVect nghost(0);
1939 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(0)); }
1940 LocalAdd(sol[0], cor[0][0], 0, 0, ncomp, nghost);
1941 }
1942
1943 for (int alev = 1; alev <= finest_amr_lev; ++alev)
1944 {
1945 // (Fine AMR correction) = I(Coarse AMR correction)
1946 interpCorrection(alev);
1947 // Known overset cells must not receive the interpolated correction.
1948 linop.applyOverset(alev, cor[alev][0]);
1949
1950 IntVect nghost(0);
1951 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(alev)); }
1952 LocalAdd(sol[alev], cor[alev][0], 0, 0, ncomp, nghost);
1953
1954 if (alev != finest_amr_lev) {
1955 LocalAdd(cor_hold[alev][0], cor[alev][0], 0, 0, ncomp, nghost);
1956 }
1957
1958 // Update fine AMR level correction
1959 computeResWithCrseCorFineCor(alev);
1960
1961 miniCycle(alev);
1962
1963 LocalAdd(sol[alev], cor[alev][0], 0, 0, ncomp, nghost);
1964
1965 if (alev != finest_amr_lev) {
1966 LocalAdd(cor[alev][0], cor_hold[alev][0], 0, 0, ncomp, nghost);
1967 }
1968 }
1969
1970 linop.averageDownAndSync(sol);
1971}
1972
1973template <typename MF>
1974void
1976{
1977 BL_PROFILE("MLMG::miniCycle()");
1978 const int mglev = 0;
1979 mgVcycle(amrlev, mglev);
1980}
1981
1982// in : Residual (res)
1983// out : Correction (cor) from bottom to this function's local top
1984template <typename MF>
1985void
1986MLMGT<MF>::mgVcycle (int amrlev, int mglev_top)
1987{
1988 BL_PROFILE("MLMG::mgVcycle()");
1989
1990 const int mglev_bottom = linop.NMGLevels(amrlev) - 1;
1991
1992 for (int mglev = mglev_top; mglev < mglev_bottom; ++mglev)
1993 {
1994 BL_PROFILE_VAR("MLMG::mgVcycle_down::"+std::to_string(mglev), blp_mgv_down_lev);
1995
1996 if (verbose >= 4)
1997 {
1998 RT norm = norminf(res[amrlev][mglev],0,ncomp,IntVect(0));
1999 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
2000 << " DN: Norm before smooth " << norm << "\n";
2001 }
2002
2003 setVal(cor[amrlev][mglev], RT(0.0));
2004 bool skip_fillboundary = true;
2005 linop.smooth(amrlev, mglev, cor[amrlev][mglev], res[amrlev][mglev], skip_fillboundary, nu1);
2006
2007 // rescor = res - L(cor)
2008 computeResOfCorrection(amrlev, mglev);
2009
2010 if (verbose >= 4)
2011 {
2012 RT norm = norminf(rescor[amrlev][mglev],0,ncomp,IntVect(0));
2013 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
2014 << " DN: Norm after smooth " << norm << "\n";
2015 }
2016
2017 // res_crse = R(rescor_fine); this provides res/b to the level below
2018 linop.restriction(amrlev, mglev+1, res[amrlev][mglev+1], rescor[amrlev][mglev]);
2019 }
2020
2021 BL_PROFILE_VAR("MLMG::mgVcycle_bottom", blp_bottom);
2022 if (amrlev == 0)
2023 {
2024 if (verbose >= 4)
2025 {
2026 RT norm = norminf(res[amrlev][mglev_bottom],0,ncomp,IntVect(0));
2027 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
2028 << " DN: Norm before bottom " << norm << "\n";
2029 }
2030 bottomSolve();
2031 if (verbose >= 4)
2032 {
2033 computeResOfCorrection(amrlev, mglev_bottom);
2034 RT norm = norminf(rescor[amrlev][mglev_bottom],0,ncomp,IntVect(0));
2035 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
2036 << " UP: Norm after bottom " << norm << "\n";
2037 }
2038 }
2039 else
2040 {
2041 if (verbose >= 4)
2042 {
2043 RT norm = norminf(res[amrlev][mglev_bottom],0,ncomp,IntVect(0));
2044 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
2045 << " Norm before smooth " << norm << "\n";
2046 }
2047 setVal(cor[amrlev][mglev_bottom], RT(0.0));
2048 bool skip_fillboundary = true;
2049 linop.smooth(amrlev, mglev_bottom, cor[amrlev][mglev_bottom],
2050 res[amrlev][mglev_bottom], skip_fillboundary, nu1);
2051 if (verbose >= 4)
2052 {
2053 computeResOfCorrection(amrlev, mglev_bottom);
2054 RT norm = norminf(rescor[amrlev][mglev_bottom],0,ncomp,IntVect(0));
2055 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
2056 << " Norm after smooth " << norm << "\n";
2057 }
2058 }
2059 BL_PROFILE_VAR_STOP(blp_bottom);
2060
2061 for (int mglev = mglev_bottom-1; mglev >= mglev_top; --mglev)
2062 {
2063 BL_PROFILE_VAR("MLMG::mgVcycle_up::"+std::to_string(mglev), blp_mgv_up_lev);
2064 // cor_fine += I(cor_crse)
2065 addInterpCorrection(amrlev, mglev);
2066 if (verbose >= 4)
2067 {
2068 computeResOfCorrection(amrlev, mglev);
2069 RT norm = norminf(rescor[amrlev][mglev],0,ncomp,IntVect(0));
2070 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
2071 << " UP: Norm before smooth " << norm << "\n";
2072 }
2073 linop.smooth(amrlev, mglev, cor[amrlev][mglev], res[amrlev][mglev], false, nu2);
2074
2075 if (cf_strategy == CFStrategy::ghostnodes) { computeResOfCorrection(amrlev, mglev); }
2076
2077 if (verbose >= 4)
2078 {
2079 computeResOfCorrection(amrlev, mglev);
2080 RT norm = norminf(rescor[amrlev][mglev],0,ncomp,IntVect(0));
2081 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
2082 << " UP: Norm after smooth " << norm << "\n";
2083 }
2084 }
2085}
2086
2087// FMG cycle on the coarsest AMR level.
2088// in: Residual on the top MG level (i.e., 0)
2089// out: Correction (cor) on all MG levels
2090template <typename MF>
2091void
2093{
2094 BL_PROFILE("MLMG::mgFcycle()");
2095
2096#ifdef AMREX_USE_EB
2097 auto* pf = linop.Factory(0);
2098 auto is_all_regular = [pf] () {
2099 const auto *const f = dynamic_cast<EBFArrayBoxFactory const*>(pf);
2100 if (f) {
2101 return f->isAllRegular();
2102 } else {
2103 return true;
2104 }
2105 };
2106 amrex::ignore_unused(pf, is_all_regular);
2107 AMREX_ASSERT(linop.isCellCentered() || is_all_regular());
2108#endif
2109
2110 const int amrlev = 0;
2111 const int mg_bottom_lev = linop.NMGLevels(amrlev) - 1;
2112 IntVect nghost(0);
2113 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(amrlev)); }
2114
2115 for (int mglev = 1; mglev <= mg_bottom_lev; ++mglev)
2116 {
2117 linop.avgDownResMG(mglev, res[amrlev][mglev], res[amrlev][mglev-1]);
2118 }
2119
2120 bottomSolve();
2121
2122 for (int mglev = mg_bottom_lev-1; mglev >= 0; --mglev)
2123 {
2124 // cor_fine = I(cor_crse)
2125 interpCorrection(amrlev, mglev);
2126
2127 // rescor = res - L(cor)
2128 computeResOfCorrection(amrlev, mglev);
2129 // res = rescor; this provides b to the vcycle below
2130 LocalCopy(res[amrlev][mglev], rescor[amrlev][mglev], 0, 0, ncomp, nghost);
2131
2132 // save cor; do v-cycle; add the saved to cor
2133 std::swap(cor[amrlev][mglev], cor_hold[amrlev][mglev]);
2134 mgVcycle(amrlev, mglev);
2135 LocalAdd(cor[amrlev][mglev], cor_hold[amrlev][mglev], 0, 0, ncomp, nghost);
2136 }
2137}
2138
2139// At the true bottom of the coarsest AMR level.
2140// in : Residual (res) as b
2141// out : Correction (cor) as x
2142template <typename MF>
2143void
2145{
2146 if (do_nsolve)
2147 {
2148 NSolve(*ns_mlmg, *ns_sol, *ns_rhs);
2149 }
2150 else
2151 {
2152 actualBottomSolve();
2153 }
2154}
2155
2156template <typename MF>
2157void
2158MLMGT<MF>::NSolve (MLMGT<MF>& a_solver, MF& a_sol, MF& a_rhs)
2159{
2160 BL_PROFILE("MLMG::NSolve()");
2161
2162 setVal(a_sol, RT(0.0));
2163
2164 MF const& res_bottom = res[0].back();
2165 if (BoxArray::SameRefs(boxArray(a_rhs),boxArray(res_bottom)) &&
2167 {
2168 LocalCopy(a_rhs, res_bottom, 0, 0, ncomp, IntVect(0));
2169 } else {
2170 setVal(a_rhs, RT(0.0));
2171 ParallelCopy(a_rhs, res_bottom, 0, 0, ncomp);
2172 }
2173
2174 a_solver.solve(Vector<MF*>{&a_sol}, Vector<MF const*>{&a_rhs},
2175 RT(-1.0), RT(-1.0));
2176
2177 linop.copyNSolveSolution(cor[0].back(), a_sol);
2178}
2179
2180template <typename MF>
2181void
2182MLMGT<MF>::postCG (int ret, int niters)
2183{
2184 if (niters >= 0) {
2185 m_niters_cg.push_back(niters);
2186 }
2187
2188 const int amrlev = 0;
2189 const int mglev = linop.NMGLevels(amrlev) - 1;
2190 auto& x = cor[amrlev][mglev];
2191 auto& b = res[amrlev][mglev];
2192 const int n = (ret==0) ? nub : nuf;
2193 linop.smooth(amrlev, mglev, x, b, false, n);
2194}
2195
2196template <typename MF>
2197void
2199{
2200 BL_PROFILE("MLMG::actualBottomSolve()");
2201
2202 if (!linop.isBottomActive()) { return; }
2203
2204 auto bottom_start_time = amrex::second();
2205
2206 ParallelContext::push(linop.BottomCommunicator());
2207 struct PopGuard { ~PopGuard () { ParallelContext::pop(); } } pop_guard; // NOLINT(cppcoreguidelines-special-member-functions)
2208
2209 const int amrlev = 0;
2210 const int mglev = linop.NMGLevels(amrlev) - 1;
2211 auto& x = cor[amrlev][mglev];
2212 auto& b = res[amrlev][mglev];
2213
2214 setVal(x, RT(0.0));
2215
2216 if (bottom_solver == BottomSolver::smoother)
2217 {
2218 bool skip_fillboundary = true;
2219 linop.smooth(amrlev, mglev, x, b, skip_fillboundary, nuf);
2220 }
2221 else
2222 {
2223 MF* bottom_b = &b;
2224 MF raii_b;
2225 if (linop.isBottomSingular() && linop.getEnforceSingularSolvable())
2226 {
2227 const IntVect ng = nGrowVect(b);
2228 raii_b = linop.make(amrlev, mglev, ng,
2229 MFInfo().SetArena(The_Async_Arena()));
2230 LocalCopy(raii_b, b, 0, 0, ncomp, ng);
2231 bottom_b = &raii_b;
2232
2233 makeSolvable(amrlev,mglev,*bottom_b);
2234 }
2235
2236 if (bottom_solver == BottomSolver::hypre)
2237 {
2238#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
2239 if constexpr (std::is_same<MF,MultiFab>()) {
2240 bottomSolveWithHypre(x, *bottom_b);
2241 } else
2242#endif
2243 {
2244 amrex::Abort("Using Hypre as bottom solver not supported in this case");
2245 }
2246 }
2247 else if (bottom_solver == BottomSolver::petsc)
2248 {
2249#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
2250 if constexpr (std::is_same<MF,MultiFab>()) {
2251 bottomSolveWithPETSc(x, *bottom_b);
2252 } else
2253#endif
2254 {
2255 amrex::Abort("Using PETSc as bottom solver not supported in this case");
2256 }
2257 }
2258 else if (bottom_solver == BottomSolver::algmg)
2259 {
2260 if constexpr (std::is_same<MF,MultiFab>()) {
2261 bottomSolveWithAlgMG(x, *bottom_b);
2262 } else {
2263 amrex::Abort("MLMG: AlgMG is only supported for MultiFab");
2264 }
2265 }
2266 else if (bottom_solver == BottomSolver::custom && linop.supportCustomBottomSolver())
2267 {
2268 linop.customBottomSolve(this, x, *bottom_b, bottom_reltol, bottom_abstol,
2269 bottom_maxiter);
2270 }
2271 else
2272 {
2273 typename MLCGSolverT<MF>::Type cg_type;
2274 if (bottom_solver == BottomSolver::cg ||
2275 bottom_solver == BottomSolver::cgbicg) {
2276 cg_type = MLCGSolverT<MF>::Type::CG;
2277 } else {
2279 }
2280
2281 int ret = bottomSolveWithCG(x, *bottom_b, cg_type);
2282
2283 if (ret != 0 && (bottom_solver == BottomSolver::cgbicg ||
2284 bottom_solver == BottomSolver::bicgcg))
2285 {
2286 if (bottom_solver == BottomSolver::cgbicg) {
2287 cg_type = MLCGSolverT<MF>::Type::BiCGStab; // switch to bicg
2288 } else {
2289 cg_type = MLCGSolverT<MF>::Type::CG; // switch to cg
2290 }
2291 if (ret == 9) {
2292 // if ret != 9 && ret != 0, x has been set to zero in bottomSolveWithCG
2293 setVal(x, RT(0));
2294 }
2295 ret = bottomSolveWithCG(x, *bottom_b, cg_type);
2296 if (ret == 0) { // switch permanently
2297 if (cg_type == MLCGSolverT<MF>::Type::CG) {
2298 bottom_solver = BottomSolver::cg;
2299 } else {
2300 bottom_solver = BottomSolver::bicgstab;
2301 }
2302 }
2303 }
2304
2305 postCG(ret);
2306 }
2307 }
2308
2309 if (! timer.empty()) {
2310 timer[bottom_time] += amrex::second() - bottom_start_time;
2311 }
2312}
2313
2314template <typename MF>
2315int
2317{
2318 MLCGSolverT<MF> cg_solver(linop);
2319 cg_solver.setSolver(type);
2320 cg_solver.setVerbose(bottom_verbose);
2321 cg_solver.setPrintIndentation(print_ident);
2322 cg_solver.setMaxIter(bottom_maxiter);
2323 cg_solver.setInitSolnZeroed(true);
2324 if (cf_strategy == CFStrategy::ghostnodes) { cg_solver.setNGhost(linop.getNGrow()); }
2325
2326 int ret = cg_solver.solve(x, b, bottom_reltol, bottom_abstol);
2327 if (ret != 0 && verbose > 1) {
2328 amrex::Print() << print_ident << "MLMG: Bottom solve failed.\n";
2329 }
2330 m_niters_cg.push_back(cg_solver.getNumIters());
2331
2332 if (ret != 0 && ret != 9) {
2333 setVal(x, RT(0));
2334 }
2335
2336 return ret;
2337}
2338
2339// Compute multi-level Residual (res) up to amrlevmax.
2340template <typename MF>
2341void
2343{
2344 BL_PROFILE("MLMG::computeMLResidual()");
2345
2346 const int mglev = 0;
2347 for (int alev = amrlevmax; alev >= 0; --alev) {
2348 const MF* crse_bcdata = (alev > 0) ? &(sol[alev-1]) : nullptr;
2349 linop.solutionResidual(alev, res[alev][mglev], sol[alev], rhs[alev], crse_bcdata);
2350 if (alev < finest_amr_lev) {
2351 linop.reflux(alev, res[alev][mglev], sol[alev], rhs[alev],
2352 res[alev+1][mglev], sol[alev+1], rhs[alev+1]);
2353 }
2354 }
2355}
2356
2357// Compute single AMR level residual without masking.
2358template <typename MF>
2359void
2361{
2362 BL_PROFILE("MLMG::computeResidual()");
2363 const MF* crse_bcdata = (alev > 0) ? &(sol[alev-1]) : nullptr;
2364 linop.solutionResidual(alev, res[alev][0], sol[alev], rhs[alev], crse_bcdata);
2365}
2366
2367// Compute coarse AMR level composite residual with coarse solution and fine correction
2368template <typename MF>
2369void
2371{
2372 BL_PROFILE("MLMG::computeResWithCrseSolFineCor()");
2373
2374 IntVect nghost(0);
2375 if (cf_strategy == CFStrategy::ghostnodes) {
2376 nghost = IntVect(std::min(linop.getNGrow(falev),linop.getNGrow(calev)));
2377 }
2378
2379 MF& crse_sol = sol[calev];
2380 const MF& crse_rhs = rhs[calev];
2381 MF& crse_res = res[calev][0];
2382
2383 MF& fine_sol = sol[falev];
2384 const MF& fine_rhs = rhs[falev];
2385 MF& fine_cor = cor[falev][0];
2386 MF& fine_res = res[falev][0];
2387 MF& fine_rescor = rescor[falev][0];
2388
2389 const MF* crse_bcdata = (calev > 0) ? &(sol[calev-1]) : nullptr;
2390 linop.solutionResidual(calev, crse_res, crse_sol, crse_rhs, crse_bcdata);
2391
2392 linop.correctionResidual(falev, 0, fine_rescor, fine_cor, fine_res, BCMode::Homogeneous);
2393 LocalCopy(fine_res, fine_rescor, 0, 0, ncomp, nghost);
2394
2395 linop.reflux(calev, crse_res, crse_sol, crse_rhs, fine_res, fine_sol, fine_rhs);
2396
2397 linop.avgDownResAmr(calev, crse_res, fine_res);
2398}
2399
2400// Compute fine AMR level residual fine_res = fine_res - L(fine_cor) with coarse providing BC.
2401template <typename MF>
2402void
2404{
2405 BL_PROFILE("MLMG::computeResWithCrseCorFineCor()");
2406
2407 IntVect nghost(0);
2408 if (cf_strategy == CFStrategy::ghostnodes) {
2409 nghost = IntVect(linop.getNGrow(falev));
2410 }
2411
2412 const MF& crse_cor = cor[falev-1][0];
2413
2414 MF& fine_cor = cor [falev][0];
2415 MF& fine_res = res [falev][0];
2416 MF& fine_rescor = rescor[falev][0];
2417
2418 // fine_rescor = fine_res - L(fine_cor)
2419 linop.correctionResidual(falev, 0, fine_rescor, fine_cor, fine_res,
2420 BCMode::Inhomogeneous, &crse_cor);
2421 LocalCopy(fine_res, fine_rescor, 0, 0, ncomp, nghost);
2422}
2423
2424// Interpolate correction from coarse to fine AMR level.
2425template <typename MF>
2426void
2428{
2429 BL_PROFILE("MLMG::interpCorrection_1");
2430
2431 IntVect nghost(0);
2432 if (cf_strategy == CFStrategy::ghostnodes) {
2433 nghost = IntVect(linop.getNGrow(alev));
2434 }
2435
2436 MF & crse_cor = cor[alev-1][0];
2437 MF & fine_cor = cor[alev ][0];
2438
2439 const Geometry& crse_geom = linop.Geom(alev-1,0);
2440
2441 int ng_src = 0;
2442 int ng_dst = linop.isCellCentered() ? 1 : 0;
2443 if (cf_strategy == CFStrategy::ghostnodes)
2444 {
2445 ng_src = linop.getNGrow(alev-1);
2446 ng_dst = linop.getNGrow(alev-1);
2447 if constexpr (IsMultiFabLike_v<MF>) {
2448 crse_cor.FillBoundary(0, ncomp, IntVect(ng_src), crse_geom.periodicity());
2449 } else {
2450 amrex::Abort("MLMG: CFStrategy::ghostnodes not supported for non-MultiFab like types");
2451 }
2452 }
2453
2454 MF cfine = linop.makeCoarseAmr(alev, IntVect(ng_dst),
2455 MFInfo().SetArena(The_Async_Arena()));
2456 setVal(cfine, RT(0.0));
2457 ParallelCopy(cfine, crse_cor, 0, 0, ncomp, IntVect(ng_src), IntVect(ng_dst),
2458 crse_geom.periodicity());
2459
2460 linop.interpolationAmr(alev, fine_cor, cfine, nghost); // NOLINT(readability-suspicious-call-argument)
2461}
2462
2463// Interpolate correction between MG levels
2464// inout: Correction (cor) on coarse MG lev. (out due to FillBoundary)
2465// out : Correction (cor) on fine MG lev.
2466template <typename MF>
2467void
2468MLMGT<MF>::interpCorrection (int alev, int mglev)
2469{
2470 BL_PROFILE("MLMG::interpCorrection_2");
2471
2472 MF& crse_cor = cor[alev][mglev+1];
2473 MF& fine_cor = cor[alev][mglev ];
2474 linop.interpAssign(alev, mglev, fine_cor, crse_cor);
2475}
2476
2477// (Fine MG level correction) += I(Coarse MG level correction)
2478template <typename MF>
2479void
2481{
2482 BL_PROFILE("MLMG::addInterpCorrection()");
2483
2484 const MF& crse_cor = cor[alev][mglev+1];
2485 MF& fine_cor = cor[alev][mglev ];
2486
2487 MF cfine;
2488 const MF* cmf;
2489
2490 if (linop.isMFIterSafe(alev, mglev, mglev+1))
2491 {
2492 cmf = &crse_cor;
2493 }
2494 else
2495 {
2496 cfine = linop.makeCoarseMG(alev, mglev, IntVect(0),
2497 MFInfo().SetArena(The_Async_Arena()));
2498 ParallelCopy(cfine, crse_cor, 0, 0, ncomp);
2499 cmf = &cfine;
2500 }
2501
2502 linop.interpolation(alev, mglev, fine_cor, *cmf);
2503}
2504
2505// Compute rescor = res - L(cor)
2506// in : res
2507// inout: cor (out due to FillBoundary in linop.correctionResidual)
2508// out : rescor
2509template <typename MF>
2510void
2512{
2513 BL_PROFILE("MLMG:computeResOfCorrection()");
2514 MF & x = cor[amrlev][mglev];
2515 const MF& b = res[amrlev][mglev];
2516 MF & r = rescor[amrlev][mglev];
2517 linop.correctionResidual(amrlev, mglev, r, x, b, BCMode::Homogeneous);
2518}
2519
2520// Compute single-level masked inf-norm of Residual (res).
2521template <typename MF>
2522auto
2523MLMGT<MF>::ResNormInf (int alev, bool local) -> RT
2524{
2525 BL_PROFILE("MLMG::ResNormInf()");
2526 return linop.normInf(alev, res[alev][0], local);
2527}
2528
2529// Computes multi-level masked inf-norm of Residual (res).
2530template <typename MF>
2531auto
2532MLMGT<MF>::MLResNormInf (int alevmax, bool local) -> RT
2533{
2534 BL_PROFILE("MLMG::MLResNormInf()");
2535 RT r = RT(0.0);
2536 for (int alev = 0; alev <= alevmax; ++alev)
2537 {
2538 r = std::max(r, ResNormInf(alev,true));
2539 }
2541 return r;
2542}
2543
2544// Compute multi-level masked inf-norm of RHS (rhs).
2545template <typename MF>
2546auto
2548{
2549 BL_PROFILE("MLMG::MLRhsNormInf()");
2550 RT r = RT(0.0);
2551 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
2552 auto t = linop.normInf(alev, rhs[alev], true);
2553 r = std::max(r, t);
2554 }
2556 return r;
2557}
2558
2559template <typename MF>
2560void
2562{
2563 auto const& offset = linop.getSolvabilityOffset(0, 0, rhs[0]);
2564 if (verbose >= 4) {
2565 for (int c = 0; c < ncomp; ++c) {
2566 amrex::Print() << print_ident << "MLMG: Subtracting " << offset[c] << " from rhs component "
2567 << c << "\n";
2568 }
2569 }
2570 for (int alev = 0; alev < namrlevs; ++alev) {
2571 linop.fixSolvabilityByOffset(alev, 0, rhs[alev], offset);
2572 }
2573}
2574
2575template <typename MF>
2576void
2577MLMGT<MF>::makeSolvable (int amrlev, int mglev, MF& mf)
2578{
2579 auto const& offset = linop.getSolvabilityOffset(amrlev, mglev, mf);
2580 if (verbose >= 4) {
2581 for (int c = 0; c < ncomp; ++c) {
2582 amrex::Print() << print_ident << "MLMG: Subtracting " << offset[c]
2583 << " from mf component c = " << c
2584 << " on level (" << amrlev << ", " << mglev << ")\n";
2585 }
2586 }
2587 linop.fixSolvabilityByOffset(amrlev, mglev, mf, offset);
2588}
2589
2590template <typename MF>
2591template <class TMF>
2592requires (std::same_as<TMF,MultiFab>)
2593void
2594MLMGT<MF>::configureAlgMG (MLAlgMG& s, bool singular, bool with_options)
2595{
2596 // Dependent type: AlgMG stays a forward declaration in this header.
2597 AlgMG<RT>& a = s.solver();
2598 a.setVerbose(bottom_verbose);
2599 a.setPrintIndentation(print_ident + " ");
2600 a.setThrowException(throw_exception);
2601 // Settings that select the setup (singular flag, Krylov type, the user's
2602 // options) are applied once per assembled matrix. Reapplying them on
2603 // every solve could toggle a value against the callback and redo the
2604 // expensive AlgMG setup each time.
2605 if (with_options) {
2606 a.setSingular(singular);
2608 if (m_algmg_options) { m_algmg_options(a); }
2609 }
2610}
2611
2612template <typename MF>
2613template <class TMF>
2614requires (std::same_as<TMF,MultiFab>)
2615void
2616MLMGT<MF>::algmgSolve (MLAlgMG& s, MF& x, MF const& b)
2617{
2618 // As a preconditioner MLMG must be a fixed linear operation: one V-cycle.
2619 if (precond_mode) {
2620 s.applyVcycle(x, b);
2621 } else {
2622 try {
2623 s.solve(x, b, bottom_reltol, std::max(bottom_abstol, RT(0)), bottom_maxiter);
2624 } catch (std::runtime_error const& e) {
2625 throw error(e.what());
2626 }
2627 }
2628}
2629
2630template <typename MF>
2631template <class TMF>
2632requires (std::same_as<TMF,MultiFab>)
2633void
2635{
2636 const int amrlev = 0;
2637 const int mglev = linop.NMGLevels(amrlev) - 1;
2638
2639 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ncomp == 1, "MLMG: AlgMG needs ncomp == 1");
2640
2641 bool const fresh = (m_algmg_bottom == nullptr);
2642 if (fresh) { m_algmg_bottom = linop.makeAlgMG(mglev); }
2643 configureAlgMG(*m_algmg_bottom, linop.isBottomSingular(), fresh);
2644 algmgSolve(*m_algmg_bottom, x, b);
2645
2646 if (linop.isSingular(amrlev) && linop.getEnforceSingularSolvable()) {
2647 makeSolvable(amrlev, mglev, x);
2648 }
2649}
2650
2651template <typename MF>
2652template <class TMF>
2653requires (std::same_as<TMF,MultiFab>)
2654void
2656{
2657 BL_PROFILE("MLMG::algmgLevel0Solve()");
2658
2659 auto t0 = amrex::second();
2660
2661 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ncomp == 1, "MLMG: AlgMG needs ncomp == 1");
2662
2663 bool const fresh = (m_algmg_level0 == nullptr);
2664 if (fresh) { m_algmg_level0 = linop.makeAlgMG(0); }
2665 configureAlgMG(*m_algmg_level0, linop.isSingular(0), fresh);
2666 algmgSolve(*m_algmg_level0, cor[0][0], res[0][0]);
2667
2668 // Known overset cells must not receive a correction; the matrix rows
2669 // there are identities with a zero rhs, so this is a safety pass.
2670 linop.applyOverset(0, cor[0][0]);
2671
2672 if (linop.isSingular(0) && linop.getEnforceSingularSolvable()) {
2673 makeSolvable(0, 0, cor[0][0]);
2674 }
2675
2676 if (! timer.empty()) {
2677 timer[bottom_time] += amrex::second() - t0;
2678 }
2679}
2680
2681template <typename MF>
2682bool
2684 RT best_norm, std::string& reason) const
2685{
2686 RT const rnorm = norms.back();
2687 if (!amrex::isfinite(rnorm)) {
2688 reason = "non-finite residual";
2689 return true;
2690 }
2691 if (rnorm > RT(1.e20)*max_norm ||
2692 rnorm > hybrid_divergence_factor*best_norm) {
2693 reason = "diverging";
2694 return true;
2695 }
2696 const int w = hybrid_stall_window;
2697 if (w > 0 && int(norms.size()) > w) {
2698 const RT r_old = norms[norms.size()-1-w];
2699 if (rnorm > std::pow(hybrid_stall_rate, RT(w)) * r_old) {
2700 reason = "stalling";
2701 return true;
2702 }
2703 }
2704 return false;
2705}
2706
2707#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
2708template <typename MF>
2709template <class TMF>
2710requires (std::same_as<TMF,MultiFab>)
2711void
2712MLMGT<MF>::bottomSolveWithHypre (MF& x, const MF& b)
2713{
2714 const int amrlev = 0;
2715 const int mglev = linop.NMGLevels(amrlev) - 1;
2716
2717 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ncomp == 1, "bottomSolveWithHypre doesn't work with ncomp > 1");
2718
2719 if (linop.isCellCentered())
2720 {
2721 if (hypre_solver == nullptr) // We should reuse the setup
2722 {
2723 hypre_solver = linop.makeHypre(hypre_interface);
2724
2725 hypre_solver->setVerbose(bottom_verbose);
2726 if (hypre_interface == amrex::Hypre::Interface::ij) {
2727 hypre_solver->setHypreOptionsNamespace(hypre_options_namespace);
2728 } else {
2729 hypre_solver->setHypreOldDefault(hypre_old_default);
2730 hypre_solver->setHypreRelaxType(hypre_relax_type);
2731 hypre_solver->setHypreRelaxOrder(hypre_relax_order);
2732 hypre_solver->setHypreNumSweeps(hypre_num_sweeps);
2733 hypre_solver->setHypreStrongThreshold(hypre_strong_threshold);
2734 }
2735
2736 const BoxArray& ba = linop.m_grids[amrlev].back();
2737 const DistributionMapping& dm = linop.m_dmap[amrlev].back();
2738 const Geometry& geom = linop.m_geom[amrlev].back();
2739
2740 hypre_bndry = std::make_unique<MLMGBndryT<MF>>(ba, dm, ncomp, geom);
2741 hypre_bndry->setHomogValues();
2742 const Real* dx = linop.m_geom[0][0].CellSize();
2743 IntVect crse_ratio = linop.m_coarse_data_crse_ratio.allGT(0) ? linop.m_coarse_data_crse_ratio : IntVect(1);
2744 RealVect bclocation(AMREX_D_DECL(0.5*dx[0]*crse_ratio[0],
2745 0.5*dx[1]*crse_ratio[1],
2746 0.5*dx[2]*crse_ratio[2]));
2747 hypre_bndry->setLOBndryConds(linop.m_lobc, linop.m_hibc, IntVect(-1), bclocation,
2748 linop.m_coarse_fine_bc_type);
2749 }
2750
2751 // IJ interface understands absolute tolerance API of HYPRE
2752 amrex::Real hypre_abstol =
2753 (hypre_interface == amrex::Hypre::Interface::ij)
2754 ? bottom_abstol : Real(-1.0);
2755 hypre_solver->solve(
2756 x, b, bottom_reltol, hypre_abstol, bottom_maxiter, *hypre_bndry,
2757 linop.getMaxOrder());
2758 }
2759 else
2760 {
2761 if (hypre_node_solver == nullptr)
2762 {
2763 hypre_node_solver =
2764 linop.makeHypreNodeLap(bottom_verbose, hypre_options_namespace);
2765 }
2766 hypre_node_solver->solve(x, b, bottom_reltol, bottom_abstol, bottom_maxiter);
2767 }
2768
2769 // For singular problems there may be a large constant added to all values of the solution
2770 // For precision reasons we enforce that the average of the correction from HYPRE is 0
2771 if (linop.isSingular(amrlev) && linop.getEnforceSingularSolvable())
2772 {
2773 makeSolvable(amrlev, mglev, x);
2774 }
2775}
2776#endif
2777
2778#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
2779template <typename MF>
2780template <class TMF>
2781requires (std::same_as<TMF,MultiFab>)
2782void
2783MLMGT<MF>::bottomSolveWithPETSc (MF& x, const MF& b)
2784{
2785 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ncomp == 1, "bottomSolveWithPETSc doesn't work with ncomp > 1");
2786
2787 if(petsc_solver == nullptr)
2788 {
2789 petsc_solver = linop.makePETSc();
2790 petsc_solver->setVerbose(bottom_verbose);
2791
2792 const BoxArray& ba = linop.m_grids[0].back();
2793 const DistributionMapping& dm = linop.m_dmap[0].back();
2794 const Geometry& geom = linop.m_geom[0].back();
2795
2796 petsc_bndry = std::make_unique<MLMGBndryT<MF>>(ba, dm, ncomp, geom);
2797 petsc_bndry->setHomogValues();
2798 const Real* dx = linop.m_geom[0][0].CellSize();
2799 auto crse_ratio = linop.m_coarse_data_crse_ratio.allGT(0) ? linop.m_coarse_data_crse_ratio : IntVect(1);
2800 RealVect bclocation(AMREX_D_DECL(0.5*dx[0]*crse_ratio[0],
2801 0.5*dx[1]*crse_ratio[1],
2802 0.5*dx[2]*crse_ratio[2]));
2803 petsc_bndry->setLOBndryConds(linop.m_lobc, linop.m_hibc, IntVect(-1), bclocation,
2804 linop.m_coarse_fine_bc_type);
2805 }
2806 petsc_solver->solve(x, b, bottom_reltol, Real(-1.), bottom_maxiter, *petsc_bndry,
2807 linop.getMaxOrder());
2808}
2809#endif
2810
2811template <typename MF>
2812void
2813MLMGT<MF>::checkPoint (const Vector<MultiFab*>& a_sol,
2814 const Vector<MultiFab const*>& a_rhs,
2815 RT a_tol_rel, RT a_tol_abs, const char* a_file_name) const
2816{
2817 std::string file_name(a_file_name);
2818 UtilCreateCleanDirectory(file_name, false);
2819
2821 {
2822 std::string HeaderFileName(std::string(a_file_name)+"/Header");
2823 std::ofstream HeaderFile;
2824 HeaderFile.open(HeaderFileName.c_str(), std::ofstream::out |
2825 std::ofstream::trunc |
2826 std::ofstream::binary);
2827 if( ! HeaderFile.good()) {
2828 FileOpenFailed(HeaderFileName);
2829 }
2830
2831 HeaderFile.precision(17);
2832
2833 std::string norm_name = getEnumNameString(norm_type);
2834
2835 HeaderFile << linop.name() << "\n"
2836 << "a_tol_rel = " << a_tol_rel << "\n"
2837 << "a_tol_abs = " << a_tol_abs << "\n"
2838 << "verbose = " << verbose << "\n"
2839 << "max_iters = " << max_iters << "\n"
2840 << "nu1 = " << nu1 << "\n"
2841 << "nu2 = " << nu2 << "\n"
2842 << "nuf = " << nuf << "\n"
2843 << "nub = " << nub << "\n"
2844 << "max_fmg_iters = " << max_fmg_iters << "\n"
2845 << "bottom_solver = " << static_cast<int>(bottom_solver) << "\n"
2846 << "bottom_verbose = " << bottom_verbose << "\n"
2847 << "bottom_maxiter = " << bottom_maxiter << "\n"
2848 << "bottom_reltol = " << bottom_reltol << "\n"
2849 << "convergence_norm = " << norm_name << "\n"
2850 << "namrlevs = " << namrlevs << "\n"
2851 << "finest_amr_lev = " << finest_amr_lev << "\n"
2852 << "linop_prepared = " << linop_prepared << "\n"
2853 << "solve_called = " << solve_called << "\n";
2854
2855 for (int ilev = 0; ilev <= finest_amr_lev; ++ilev) {
2856 UtilCreateCleanDirectory(file_name+"/Level_"+std::to_string(ilev), false);
2857 }
2858 }
2859
2861
2862 for (int ilev = 0; ilev <= finest_amr_lev; ++ilev) {
2863 VisMF::Write(*a_sol[ilev], file_name+"/Level_"+std::to_string(ilev)+"/sol");
2864 VisMF::Write(*a_rhs[ilev], file_name+"/Level_"+std::to_string(ilev)+"/rhs");
2865 }
2866
2867 linop.checkPoint(file_name+"/linop");
2868}
2869
2870template <typename MF>
2871void
2873{
2874 print_ident.resize(print_ident.size()+4, ' ');
2875}
2876
2877template <typename MF>
2878void
2880{
2881 if (print_ident.size() > 4) {
2882 print_ident.resize(print_ident.size()-4, ' ');
2883 } else {
2884 print_ident.clear();
2885 }
2886}
2887
2888extern template class MLMGT<MultiFab>;
2889
2892
2893}
2894
2895#endif
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:562
#define BL_PROFILE_VAR_STOP(vname)
Definition AMReX_BLProfiler.H:574
#define BL_PROFILE_VAR(fname, vname)
Definition AMReX_BLProfiler.H:571
#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.
Array4< int const > offset
Definition AMReX_HypreMLABecLap.cpp:1139
GpuArray< MultiArray4< Real const >, 3 > s
Definition AMReX_MLEBNodeFDLaplacian.cpp:214
#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
bool isAllRegular() const noexcept
Definition AMReX_EBFabFactory.cpp:232
Solve using GMRES with multigrid as preconditioner.
Definition AMReX_GMRES_MLMG.H:28
Rectangular problem domain geometry.
Definition AMReX_Geometry.H:85
Periodicity periodicity() const noexcept
Return the Periodicity based on the length of the domain.
Definition AMReX_Geometry.H:424
Interface
HYPRE interface modes supported.
Definition AMReX_Hypre.H:70
__host__ __device__ constexpr bool allGT(const IntVectND< dim > &rhs) const noexcept
Returns true if this is greater than argument for all components. NOTE: This is NOT a strict weak ord...
Definition AMReX_IntVect.H:517
__host__ __device__ constexpr IntVectND< new_dim > resize(int fill_extra=0) const noexcept
Returns a new IntVectND of size new_dim by either shrinking or expanding this IntVectND.
Definition AMReX_IntVect.H:889
__host__ static __device__ constexpr IntVectND< dim > TheDimensionVector(int d) noexcept
This static member function returns a reference to a constant IntVectND object, all of whose dim argu...
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
CG-family solvers (BiCGStab or CG) for use as the bottom solver in MLMG.
Definition AMReX_MLCGSolver.H:21
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
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
Abstract base class for multilevel linear operators used by MLMG and the bottom solvers.
Definition AMReX_MLLinOp.H:139
typename FabDataType< MF >::fab_type FAB
Definition AMReX_MLLinOp.H:149
typename FabDataType< MF >::value_type RT
Definition AMReX_MLLinOp.H:150
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:1231
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:2532
void postCG(int ret, int niters=-1)
Post CG smoothing.
Definition AMReX_MLMG.H:2182
RT MLRhsNormInf(bool local=false)
Composite infinity norm of the RHS.
Definition AMReX_MLMG.H:2547
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:2683
void actualBottomSolve()
Execute the actual bottom solve after pre-smoothing and restriction.
Definition AMReX_MLMG.H:2198
MF MFType
Definition AMReX_MLMG.H:52
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 setBottomToleranceAbs(RT t) noexcept
Absolute tolerance for the bottom solver.
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:2427
void getEBFluxes(const Vector< MF * > &a_eb_flux)
Flux into the EB wall using the internally stored solution.
Definition AMReX_MLMG.H:1452
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:1242
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:2511
void applyPrecond(const Vector< MF * > &out, const Vector< MF * > &in)
Apply the linear operator as a preconditioner (out = L(in)).
Definition AMReX_MLMG.H:1623
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:2403
void NSolve(MLMGT< MF > &a_solver, MF &a_sol, MF &a_rhs)
Perform an NSolve using an MLMGT wrapper.
Definition AMReX_MLMG.H:2158
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:1546
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:1273
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:2342
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:1986
void prepareForNSolve()
Prepare the NSolve path.
Definition AMReX_MLMG.H:1865
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:1213
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:2561
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:1857
void incPrintIdentation()
Increase the indentation used when printing solver logs.
Definition AMReX_MLMG.H:2872
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:2879
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:1829
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
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:1635
int bottomSolveWithCG(MF &x, const MF &b, typename MLCGSolverT< MF >::Type type)
Bottom solve using CG/BiCGStab implemented in MLCGSolverT.
Definition AMReX_MLMG.H:2316
void setAlwaysUseBNorm(int flag) noexcept
Deprecated flag for forcing B-norm convergence checks.
Definition AMReX_MLMG.H:867
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:1478
void miniCycle(int amrlev)
Execute a per-level mini cycle.
Definition AMReX_MLMG.H:1975
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 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:2370
MLMGT< MF > & operator=(MLMGT< MF > const &)=delete
MLMGT(MLLinOpT< MF > &a_lp)
Definition AMReX_MLMG.H:858
void computeResidual(int alev)
Compute the residual on AMR level alev.
Definition AMReX_MLMG.H:2360
MLLinOpT< MF > & getLinOp()
Definition AMReX_MLMG.H:730
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 mgFcycle()
Run an FMG cycle starting from the coarsest grid.
Definition AMReX_MLMG.H:2092
RT getInitRHS() const noexcept
Definition AMReX_MLMG.H:720
void algmgSolve(MLAlgMG &s, MF &x, MF const &b)
Definition AMReX_MLMG.H:2616
RT ResNormInf(int alev, bool local=false)
Infinity norm of the residual on level alev.
Definition AMReX_MLMG.H:2523
Vector< int > const & getNumCGIters() const noexcept
Definition AMReX_MLMG.H:728
void bottomSolveWithAlgMG(MF &x, const MF &b)
AlgMG on the bottom MG level (MultiFab, single component only).
Definition AMReX_MLMG.H:2634
void bottomSolve()
Execute the configured bottom solver (Hypre, PETSc, CG, etc.).
Definition AMReX_MLMG.H:2144
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:2480
int getVerbose() const
Definition AMReX_MLMG.H:261
void algmgLevel0Solve()
Definition AMReX_MLMG.H:2655
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 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)
Definition AMReX_MLMG.H:2594
void oneIter(int iter)
Execute a single multigrid iteration (FMG or V-cycle).
Definition AMReX_MLMG.H:1898
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:980
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:1630
std::array< T, N > Array
Definition AMReX_Array.H:31
Arena * The_Async_Arena()
Definition AMReX_Arena.cpp:839
void Max(KeyValuePair< K, V > &vi, MPI_Comm comm)
Definition AMReX_ParallelReduce.H:133
#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
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
__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:359
void Scale(MF &dst, typename MF::value_type val, int scomp, int ncomp, int nghost)
dst *= val
Definition AMReX_FabArrayUtility.H:2175
BoxArray const & boxArray(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.boxArray().
Definition AMReX_FabArrayBase.cpp:2949
DistributionMapping const & DistributionMap(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.DistributionMap().
Definition AMReX_FabArrayBase.cpp:2954
void average_face_to_cellcenter(MultiFab &cc, int dcomp, const Vector< const MultiFab * > &fc, IntVect const &ng_vect)
Definition AMReX_MultiFabUtil.cpp:156
void EB_set_covered(MultiFab &mf, Real val)
Fill all covered cells with a single value val.
Definition AMReX_EBMultiFabUtil.cpp:21
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:2939
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:2251
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
BottomSolver
Definition AMReX_MLLinOp.H:42
IntVectND< 3 > IntVect
IntVect is an alias for amrex::IntVectND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:38
RealVectND< 3 > RealVect
Definition AMReX_ParmParse.H:39
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:2944
void LocalCopy(DMF &dst, SMF const &src, int scomp, int dcomp, int ncomp, IntVect const &nghost)
dst = src
Definition AMReX_FabArrayUtility.H:2182
void setBndry(MF &dst, typename MF::value_type val, int scomp, int ncomp)
dst = val in ghost cells.
Definition AMReX_FabArrayUtility.H:2168
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:2262
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
void LocalAdd(MF &dst, MF const &src, int scomp, int dcomp, int ncomp, IntVect const &nghost)
dst += src
Definition AMReX_FabArrayUtility.H:2190
void setVal(MF &dst, typename MF::value_type val)
dst = val
Definition AMReX_FabArrayUtility.H:2161
BCMode
Definition AMReX_MLLinOp.H:119
Location
Definition AMReX_MLLinOp.H:121
FabArray memory allocation information.
Definition AMReX_FabArray.H:73
MFInfo & SetArena(Arena *ar) noexcept
Select the Arena used for FAB storage.
Definition AMReX_FabArray.H:87