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 <optional>
10
11namespace amrex {
12
20// Norm used to evaluate the target convergence criteria
22
24template <typename MF>
25class MLMGT
26{
27public:
28
29 class error
30 : public std::runtime_error
31 {
32 public :
33 using std::runtime_error::runtime_error;
34 };
35
36 template <typename T> friend class MLCGSolverT;
37 template <typename M> friend class GMRESMLMGT;
38
39 using MFType = MF;
40 using FAB = typename MLLinOpT<MF>::FAB;
41 using RT = typename MLLinOpT<MF>::RT;
42
43 using BCMode = typename MLLinOpT<MF>::BCMode;
45
47 enum class CFStrategy : int {none,ghostnodes};
48
49 MLMGT (MLLinOpT<MF>& a_lp);
51
52 MLMGT (MLMGT<MF> const&) = delete;
53 MLMGT (MLMGT<MF> &&) = delete;
54 MLMGT<MF>& operator= (MLMGT<MF> const&) = delete;
56
67 template <typename AMF>
68 RT solve (const Vector<AMF*>& a_sol, const Vector<AMF const*>& a_rhs,
69 RT a_tol_rel, RT a_tol_abs, const char* checkpoint_file = nullptr);
70
81 template <typename AMF>
82 RT solve (std::initializer_list<AMF*> a_sol,
83 std::initializer_list<AMF const*> a_rhs,
84 RT a_tol_rel, RT a_tol_abs, const char* checkpoint_file = nullptr);
85
95 RT precond (Vector<MF*> const& a_sol, Vector<MF const*> const& a_rhs,
96 RT a_tol_rel, RT a_tol_abs);
97
104 template <typename AMF>
105 void getGradSolution (const Vector<Array<AMF*,AMREX_SPACEDIM> >& a_grad_sol,
106 Location a_loc = Location::FaceCenter);
107
114 template <typename AMF>
115 void getGradSolution (std::initializer_list<Array<AMF*,AMREX_SPACEDIM>> a_grad_sol,
116 Location a_loc = Location::FaceCenter);
117
124 template <typename AMF>
125 void getFluxes (const Vector<Array<AMF*,AMREX_SPACEDIM> >& a_flux,
126 Location a_loc = Location::FaceCenter);
127
134 template <typename AMF>
135 void getFluxes (std::initializer_list<Array<AMF*,AMREX_SPACEDIM>> a_flux,
136 Location a_loc = Location::FaceCenter);
137
145 template <typename AMF>
146 void getFluxes (const Vector<Array<AMF*,AMREX_SPACEDIM> >& a_flux,
147 const Vector<AMF*> & a_sol,
148 Location a_loc = Location::FaceCenter);
149
157 template <typename AMF>
158 void getFluxes (std::initializer_list<Array<AMF*,AMREX_SPACEDIM>> a_flux,
159 std::initializer_list<AMF*> a_sol,
160 Location a_loc = Location::FaceCenter);
161
168 template <typename AMF>
169 void getFluxes (const Vector<AMF*> & a_flux,
170 Location a_loc = Location::CellCenter);
171
178 template <typename AMF>
179 void getFluxes (std::initializer_list<AMF*> a_flux,
180 Location a_loc = Location::CellCenter);
181
189 template <typename AMF>
190 void getFluxes (const Vector<AMF*> & a_flux,
191 const Vector<AMF*> & a_sol,
192 Location a_loc = Location::CellCenter);
193
201 template <typename AMF>
202 void getFluxes (std::initializer_list<AMF*> a_flux,
203 std::initializer_list<AMF*> a_sol,
204 Location a_loc = Location::CellCenter);
205
213 void compResidual (const Vector<MF*>& a_res, const Vector<MF*>& a_sol,
214 const Vector<MF const*>& a_rhs);
215
216#ifdef AMREX_USE_EB
222 void getEBFluxes (const Vector<MF*>& a_eb_flux);
229 void getEBFluxes (const Vector<MF*>& a_eb_flux, const Vector<MF*> & a_sol);
230#endif
231
237 void apply (const Vector<MF*>& out, const Vector<MF*>& in);
238
245 void applyPrecond (const Vector<MF*>& out, const Vector<MF*>& in);
246
247 [[nodiscard]] int getVerbose () const { return verbose; }
248 [[nodiscard]] int getBottomVerbose () const { return bottom_verbose; }
249
251 void incPrintIdentation ();
253 void decPrintIdentation ();
254
262 void setThrowException (bool t) noexcept { throw_exception = t; }
268 void setVerbose (int v) noexcept { verbose = v; }
274 void setMaxIter (int n) noexcept { max_iters = n; }
280 void setMaxFmgIter (int n) noexcept { max_fmg_iters = n; }
287 void setFixedIter (int nit) noexcept { do_fixed_number_of_iters = nit; }
294 void setPrecondIter (int nit) noexcept { max_precond_iters = nit; }
295
301 void setPreSmooth (int n) noexcept { nu1 = n; }
307 void setPostSmooth (int n) noexcept { nu2 = n; }
313 void setFinalSmooth (int n) noexcept { nuf = n; }
319 void setBottomSmooth (int n) noexcept { nub = n; }
320
326 void setBottomSolver (BottomSolver s) noexcept { bottom_solver = s; }
327 [[nodiscard]] BottomSolver getBottomSolver () const noexcept { return bottom_solver; }
333 void setCFStrategy (CFStrategy a_cf_strategy) noexcept {cf_strategy = a_cf_strategy;}
339 void setBottomVerbose (int v) noexcept { bottom_verbose = v; }
345 void setBottomMaxIter (int n) noexcept { bottom_maxiter = n; }
351 void setBottomTolerance (RT t) noexcept { bottom_reltol = t; }
357 void setBottomToleranceAbs (RT t) noexcept { bottom_abstol = t;}
358 [[nodiscard]] RT getBottomToleranceAbs () const noexcept{ return bottom_abstol; }
359
360 [[deprecated("Use MLMG::setConvergenceNormType() instead.")]]
366 void setAlwaysUseBNorm (int flag) noexcept;
367
373 void setConvergenceNormType (MLMGNormType norm) noexcept { norm_type = norm; }
374
380 void setFinalFillBC (int flag) noexcept { final_fill_bc = flag; }
381
382 [[nodiscard]] int numAMRLevels () const noexcept { return namrlevs; }
383
389 void setNSolve (int flag) noexcept { do_nsolve = flag; }
395 void setNSolveGridSize (int s) noexcept { nsolve_grid_size = s; }
396
412 void setNoGpuSync (bool do_not_sync) noexcept { do_no_sync_gpu = do_not_sync; }
413
414#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
420 void setHypreInterface (Hypre::Interface f) noexcept {
421 // must use ij interface for EB
422#ifndef AMREX_USE_EB
423 hypre_interface = f;
424#else
426#endif
427 }
428
434 void setHypreOptionsNamespace(const std::string& prefix) noexcept
435 {
436 hypre_options_namespace = prefix;
437 }
438
440 void setHypreOldDefault (bool l) noexcept {hypre_old_default = l;}
442 void setHypreRelaxType (int n) noexcept {hypre_relax_type = n;}
444 void setHypreRelaxOrder (int n) noexcept {hypre_relax_order = n;}
446 void setHypreNumSweeps (int n) noexcept {hypre_num_sweeps = n;}
448 void setHypreStrongThreshold (Real t) noexcept {hypre_strong_threshold = t;}
449#endif
450
456 void prepareForFluxes (Vector<MF const*> const& a_sol);
457
464 template <typename AMF>
465 void prepareForSolve (Vector<AMF*> const& a_sol, Vector<AMF const*> const& a_rhs);
466
468 void prepareForNSolve ();
469
471 void prepareLinOp ();
472
474 void preparePrecond ();
475
481 void oneIter (int iter);
482
488 void miniCycle (int amrlev);
489
496 void mgVcycle (int amrlev, int mglev);
498 void mgFcycle ();
499
501 void bottomSolve ();
509 void NSolve (MLMGT<MF>& a_solver, MF& a_sol, MF& a_rhs);
511 void actualBottomSolve ();
512
521 void postCG (int ret, int niters = -1);
522
528 void computeMLResidual (int amrlevmax);
534 void computeResidual (int alev);
541 void computeResWithCrseSolFineCor (int calev, int falev);
547 void computeResWithCrseCorFineCor (int falev);
553 void interpCorrection (int alev);
560 void interpCorrection (int alev, int mglev);
567 void addInterpCorrection (int alev, int mglev);
568
575 void computeResOfCorrection (int amrlev, int mglev);
576
583 RT ResNormInf (int alev, bool local = false);
590 RT MLResNormInf (int alevmax, bool local = false);
596 RT MLRhsNormInf (bool local = false);
597
599 void makeSolvable ();
607 void makeSolvable (int amrlev, int mglev, MF& mf);
608
609#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
617 template <class TMF=MF>
618 requires (std::same_as<TMF,MultiFab>)
619 void bottomSolveWithHypre (MF& x, const MF& b);
620#endif
621
622#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
630 template <class TMF=MF>
631 requires (std::same_as<TMF,MultiFab>)
632 void bottomSolveWithPETSc (MF& x, const MF& b);
633#endif
634
643 int bottomSolveWithCG (MF& x, const MF& b, typename MLCGSolverT<MF>::Type type);
644
645 [[nodiscard]] RT getInitRHS () const noexcept { return m_rhsnorm0; }
646 // Initial composite residual
647 [[nodiscard]] RT getInitResidual () const noexcept { return m_init_resnorm0; }
648 // Final composite residual
649 [[nodiscard]] RT getFinalResidual () const noexcept { return m_final_resnorm0; }
650 // Residuals on the *finest* AMR level after each iteration
651 [[nodiscard]] Vector<RT> const& getResidualHistory () const noexcept { return m_iter_fine_resnorm0; }
652 [[nodiscard]] int getNumIters () const noexcept { return m_iter_fine_resnorm0.size(); }
653 [[nodiscard]] Vector<int> const& getNumCGIters () const noexcept { return m_niters_cg; }
654
655 MLLinOpT<MF>& getLinOp () { return linop; }
656
657private:
658
659 bool precond_mode = false;
660 bool throw_exception = false;
661 int verbose = 1;
662
663 int max_iters = 200;
664 int do_fixed_number_of_iters = 0;
665 int max_precond_iters = 1;
666
667 int nu1 = 2;
668 int nu2 = 2;
669 int nuf = 8;
670 int nub = 0;
671
672 int max_fmg_iters = 0;
673
674 BottomSolver bottom_solver = BottomSolver::Default;
675 CFStrategy cf_strategy = CFStrategy::none;
676 int bottom_verbose = 0;
677 int bottom_maxiter = 200;
678 RT bottom_reltol = std::is_same<RT,double>() ? RT(1.e-4) : RT(1.e-3);
679 RT bottom_abstol = RT(-1.0);
680
682
683 int final_fill_bc = 0;
684
685 MLLinOpT<MF>& linop;
686 int ncomp;
687 int namrlevs;
688 int finest_amr_lev;
689
690 bool linop_prepared = false;
691 Long solve_called = 0;
692
694 int do_nsolve = false;
695 int nsolve_grid_size = 16;
696 std::unique_ptr<MLLinOpT<MF>> ns_linop;
697 std::unique_ptr<MLMGT<MF>> ns_mlmg;
698 std::unique_ptr<MF> ns_sol;
699 std::unique_ptr<MF> ns_rhs;
700
701 std::string print_ident;
702
703 bool do_no_sync_gpu = false;
704
706#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
707 // Hypre::Interface hypre_interface = Hypre::Interface::structed;
708 // Hypre::Interface hypre_interface = Hypre::Interface::semi_structed;
709 Hypre::Interface hypre_interface = Hypre::Interface::ij;
710
711 std::unique_ptr<Hypre> hypre_solver;
712 std::unique_ptr<MLMGBndryT<MF>> hypre_bndry;
713 std::unique_ptr<HypreNodeLap> hypre_node_solver;
714
715 std::string hypre_options_namespace = "hypre";
716 bool hypre_old_default = true; // Falgout coarsening with modified classical interpolation
717 int hypre_relax_type = 6; // G-S/Jacobi hybrid relaxation
718 int hypre_relax_order = 1; // uses C/F relaxation
719 int hypre_num_sweeps = 2; // Sweeps on each level
720 Real hypre_strong_threshold = 0.25; // HYPRE default is 0.25
721#endif
722
724#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
725 std::unique_ptr<PETScABecLap> petsc_solver;
726 std::unique_ptr<MLMGBndryT<MF>> petsc_bndry;
727#endif
728
733 Vector<MF> sol;
734 Vector<MF> rhs;
736
737 Vector<int> sol_is_alias;
738
743 Vector<Vector<MF> > res;
744 Vector<Vector<MF> > cor;
745 Vector<Vector<MF> > cor_hold;
746 Vector<Vector<MF> > rescor;
748
749 enum timer_types { solve_time=0, iter_time, bottom_time, ntimers };
750 Vector<double> timer;
751
752 RT m_rhsnorm0 = RT(-1.0);
753 RT m_init_resnorm0 = RT(-1.0);
754 RT m_final_resnorm0 = RT(-1.0);
755 Vector<int> m_niters_cg;
756 Vector<RT> m_iter_fine_resnorm0; // Residual for each iteration at the finest level
757
767 void checkPoint (const Vector<MultiFab*>& a_sol,
768 const Vector<MultiFab const*>& a_rhs,
769 RT a_tol_rel, RT a_tol_abs, const char* a_file_name) const;
770
771};
772
773template <typename MF>
775 : linop(a_lp), ncomp(a_lp.getNComp()), namrlevs(a_lp.NAMRLevels()),
776 finest_amr_lev(a_lp.NAMRLevels()-1)
777{}
778
779template <typename MF> MLMGT<MF>::~MLMGT () = default;
780
781template <typename MF>
782void
784{
785 if (flag) {
786 norm_type = MLMGNormType::bnorm;
787 } else {
788 norm_type = MLMGNormType::greater;
789 }
790}
791
792template <typename MF>
793template <typename AMF>
794auto
795MLMGT<MF>::solve (std::initializer_list<AMF*> a_sol,
796 std::initializer_list<AMF const*> a_rhs,
797 RT a_tol_rel, RT a_tol_abs, const char* checkpoint_file) -> RT
798{
799 return solve(Vector<AMF*>(std::move(a_sol)),
800 Vector<AMF const*>(std::move(a_rhs)),
801 a_tol_rel, a_tol_abs, checkpoint_file);
802}
803
804template <typename MF>
805template <typename AMF>
806auto
808 RT a_tol_rel, RT a_tol_abs, const char* checkpoint_file) -> RT
809{
810 BL_PROFILE("MLMG::solve()");
811
812 // If requested with setNoGpuSync(true), run the whole solve in a
813 // single-stream, no-implicit-sync region. The RAII objects restore the
814 // previous state (also when an exception is thrown), and SyncAtExitOnly
815 // synchronizes the GPU streams on exit unless the caller was already in
816 // a NoSync region. The order matters:
817 // no_sync_region is destroyed last, i.e., after the single stream region
818 // has been popped, so that all streams are synchronized.
819 std::optional<Gpu::SyncAtExitOnly> no_sync_region;
820 std::optional<Gpu::SingleStreamRegion> single_stream_region;
821 if (do_no_sync_gpu) {
822 no_sync_region.emplace();
823 single_stream_region.emplace();
824 }
825
826 if constexpr (std::is_same<AMF,MultiFab>()) {
827 if (checkpoint_file != nullptr) {
828 checkPoint(a_sol, a_rhs, a_tol_rel, a_tol_abs, checkpoint_file);
829 }
830 }
831
832 if ((bottom_solver == BottomSolver::custom) && !linop.supportCustomBottomSolver()) {
833 bottom_solver = BottomSolver::Default;
834 }
835
836 if (bottom_solver == BottomSolver::Default) {
837 bottom_solver = linop.getDefaultBottomSolver();
838 }
839
840#if (defined(AMREX_USE_HYPRE) || defined(AMREX_USE_PETSC)) && (AMREX_SPACEDIM > 1)
841 if constexpr (IsFabArray_v<AMF>) {
842 if (bottom_solver == BottomSolver::hypre || bottom_solver == BottomSolver::petsc) {
843 int mo = linop.getMaxOrder();
844 if (a_sol[0]->hasEBFabFactory()) {
845 linop.setMaxOrder(2);
846 } else {
847 linop.setMaxOrder(std::min(3,mo)); // maxorder = 4 not supported
848 }
849 }
850 }
851#endif
852
853 bool is_nsolve = linop.m_parent;
854
855 auto solve_start_time = amrex::second();
856
857 RT& composite_norminf = m_final_resnorm0;
858
859 m_niters_cg.clear();
860 m_iter_fine_resnorm0.clear();
861
862 prepareForSolve(a_sol, a_rhs);
863
864 computeMLResidual(finest_amr_lev);
865
866 bool local = true;
867 RT resnorm0 = MLResNormInf(finest_amr_lev, local);
868 RT rhsnorm0 = MLRhsNormInf(local);
869 if (!is_nsolve) {
870 ParallelAllReduce::Max<RT>({resnorm0, rhsnorm0}, ParallelContext::CommunicatorSub());
871
872 if (verbose >= 1)
873 {
874 amrex::Print() << print_ident << "MLMG: Initial rhs = " << rhsnorm0 << "\n"
875 << print_ident << "MLMG: Initial residual (resid0) = " << resnorm0 << "\n";
876 }
877 }
878
879 m_init_resnorm0 = resnorm0;
880 m_rhsnorm0 = rhsnorm0;
881
882 RT max_norm = resnorm0;
883 std::string norm_name = "resid0";
884 switch (norm_type) {
886 if (rhsnorm0 >= resnorm0) {
887 norm_name = "bnorm";
888 max_norm = rhsnorm0;
889 } else {
890 norm_name = "resid0";
891 max_norm = resnorm0;
892 }
893 break;
895 norm_name = "bnorm";
896 max_norm = rhsnorm0;
897 break;
899 norm_name = "resid0";
900 max_norm = resnorm0;
901 break;
902 }
903
904 const RT res_target = std::max(a_tol_abs, std::max(a_tol_rel,RT(1.e-16))*max_norm);
905
906 if (!is_nsolve && resnorm0 <= res_target) {
907 composite_norminf = resnorm0;
908 if (verbose >= 1) {
909 amrex::Print() << print_ident << "MLMG: No iterations needed\n";
910 }
911 } else {
912 auto iter_start_time = amrex::second();
913 bool converged = false;
914
915 const int niters = do_fixed_number_of_iters ? do_fixed_number_of_iters : max_iters;
916 for (int iter = 0; iter < niters; ++iter)
917 {
918 oneIter(iter);
919
920 converged = false;
921
922 // Test convergence on the fine amr level
923 computeResidual(finest_amr_lev);
924
925 if (is_nsolve) { continue; }
926
927 RT fine_norminf = ResNormInf(finest_amr_lev);
928 m_iter_fine_resnorm0.push_back(fine_norminf);
929 composite_norminf = fine_norminf;
930 if (verbose >= 2) {
931 amrex::Print() << print_ident << "MLMG: Iteration " << std::setw(3) << iter+1 << " Fine resid/"
932 << norm_name << " = " << fine_norminf/max_norm << "\n";
933 }
934 bool fine_converged = (fine_norminf <= res_target);
935
936 if (namrlevs == 1 && fine_converged) {
937 converged = true;
938 } else if (fine_converged) {
939 // finest level is converged, but we still need to test the coarse levels
940 computeMLResidual(finest_amr_lev-1);
941 RT crse_norminf = MLResNormInf(finest_amr_lev-1);
942 if (verbose >= 2) {
943 amrex::Print() << print_ident << "MLMG: Iteration " << std::setw(3) << iter+1
944 << " Crse resid/" << norm_name << " = "
945 << crse_norminf/max_norm << "\n";
946 }
947 converged = (crse_norminf <= res_target);
948 composite_norminf = std::max(fine_norminf, crse_norminf);
949 } else {
950 converged = false;
951 }
952
953 if (converged) {
954 if (verbose >= 1) {
955 amrex::Print() << print_ident << "MLMG: Final Iter. " << iter+1
956 << " resid, resid/" << norm_name << " = "
957 << composite_norminf << ", "
958 << composite_norminf/max_norm << "\n";
959 }
960 break;
961 } else {
962 if (composite_norminf > RT(1.e20)*max_norm)
963 {
964 if (verbose > 0) {
965 amrex::Print() << print_ident << "MLMG: Failing to converge after " << iter+1 << " iterations."
966 << " resid, resid/" << norm_name << " = "
967 << composite_norminf << ", "
968 << composite_norminf/max_norm << "\n";
969 }
970
971 if ( throw_exception ) {
972 throw error("MLMG blew up.");
973 } else {
974 amrex::Abort("MLMG failing so lets stop here");
975 }
976 }
977 }
978 }
979
980 if (!converged && do_fixed_number_of_iters == 0) {
981 if (verbose > 0) {
982 amrex::Print() << print_ident << "MLMG: Failed to converge after " << max_iters << " iterations."
983 << " resid, resid/" << norm_name << " = "
984 << composite_norminf << ", "
985 << composite_norminf/max_norm << "\n";
986 }
987
988 if ( throw_exception ) {
989 throw error("MLMG failed to converge.");
990 } else {
991 amrex::Abort("MLMG failed.");
992 }
993 }
994 timer[iter_time] = amrex::second() - iter_start_time;
995 }
996
997 linop.postSolve(GetVecOfPtrs(sol));
998
999 IntVect ng_back = final_fill_bc ? IntVect(1) : IntVect(0);
1000 if (linop.hasHiddenDimension()) {
1001 ng_back[linop.hiddenDirection()] = 0;
1002 }
1003 for (int alev = 0; alev < namrlevs; ++alev)
1004 {
1005 if (!sol_is_alias[alev]) {
1006 LocalCopy(*a_sol[alev], sol[alev], 0, 0, ncomp, ng_back);
1007 }
1008 }
1009
1010 timer[solve_time] = amrex::second() - solve_start_time;
1011 if (verbose >= 1) {
1012 ParallelReduce::Max<double>(timer.data(), timer.size(), 0,
1014 if (ParallelContext::MyProcSub() == 0)
1015 {
1016 amrex::AllPrint() << print_ident << "MLMG: Timers: Solve = " << timer[solve_time]
1017 << " Iter = " << timer[iter_time]
1018 << " Bottom = " << timer[bottom_time] << "\n";
1019 }
1020 }
1021
1022 ++solve_called;
1023
1024 return composite_norminf;
1025}
1026
1027template <typename MF>
1028auto
1030 RT a_tol_rel, RT a_tol_abs) -> RT
1031{
1032 precond_mode = true;
1033 std::swap(max_precond_iters, do_fixed_number_of_iters);
1034 linop.beginPrecondBC();
1035
1036 auto r = solve(a_sol, a_rhs, a_tol_rel, a_tol_abs);
1037
1038 linop.endPrecondBC();
1039 std::swap(max_precond_iters, do_fixed_number_of_iters);
1040 precond_mode = false;
1041
1042 return r;
1043}
1044
1045template <typename MF>
1046void
1048{
1049 for (int alev = finest_amr_lev; alev >= 0; --alev) {
1050 const MF* crse_bcdata = (alev > 0) ? a_sol[alev-1] : nullptr;
1051 linop.prepareForFluxes(alev, crse_bcdata);
1052 }
1053}
1054
1055template <typename MF>
1056template <typename AMF>
1057void
1059{
1060 BL_PROFILE("MLMG::getGradSolution()");
1061 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1062 if constexpr (std::is_same<AMF,MF>()) {
1063 linop.compGrad(alev, a_grad_sol[alev], sol[alev], a_loc);
1064 } else {
1065 Array<MF,AMREX_SPACEDIM> grad_sol;
1066 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1067 auto const& amf = *(a_grad_sol[alev][idim]);
1068 grad_sol[idim].define(boxArray(amf), DistributionMap(amf), ncomp, 0);
1069 }
1070 linop.compGrad(alev, GetArrOfPtrs(grad_sol), sol[alev], a_loc);
1071 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1072 LocalCopy(*a_grad_sol[alev][idim], grad_sol[idim], 0, 0, ncomp, IntVect(0));
1073 }
1074 }
1075 }
1076}
1077
1078template <typename MF>
1079template <typename AMF>
1080void
1081MLMGT<MF>::getGradSolution (std::initializer_list<Array<AMF*,AMREX_SPACEDIM>> a_grad_sol, Location a_loc)
1082{
1083 getGradSolution(Vector<Array<AMF*,AMREX_SPACEDIM>>(std::move(a_grad_sol)), a_loc);
1084}
1085
1086template <typename MF>
1087template <typename AMF>
1088void
1090 Location a_loc)
1091{
1092 if (!linop.isCellCentered()) {
1093 amrex::Abort("Calling wrong getFluxes for nodal solver");
1094 }
1095
1096 AMREX_ASSERT(sol.size() == a_flux.size());
1097
1098 if constexpr (std::is_same<AMF,MF>()) {
1099 getFluxes(a_flux, GetVecOfPtrs(sol), a_loc);
1100 } else {
1101 Vector<Array<MF,AMREX_SPACEDIM>> fluxes(namrlevs);
1102 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1103 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1104 auto const& amf = *(a_flux[ilev][idim]);
1105 fluxes[ilev][idim].define(boxArray(amf), DistributionMap(amf), ncomp, 0);
1106 }
1107 }
1108 getFluxes(GetVecOfArrOfPtrs(fluxes), GetVecOfPtrs(sol), a_loc);
1109 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1110 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1111 LocalCopy(*a_flux[ilev][idim], fluxes[ilev][idim], 0, 0, ncomp, IntVect(0));
1112 }
1113 }
1114 }
1115}
1116
1117template <typename MF>
1118template <typename AMF>
1119void
1121 Location a_loc)
1122{
1123 getFluxes(Vector<Array<AMF*,AMREX_SPACEDIM>>(std::move(a_flux)), a_loc);
1124}
1125
1126template <typename MF>
1127template <typename AMF>
1128void
1130 const Vector<AMF*>& a_sol, Location a_loc)
1131{
1132 BL_PROFILE("MLMG::getFluxes()");
1133
1134 if (!linop.isCellCentered()) {
1135 amrex::Abort("Calling wrong getFluxes for nodal solver");
1136 }
1137
1138 if constexpr (std::is_same<AMF,MF>()) {
1139 linop.getFluxes(a_flux, a_sol, a_loc);
1140 } else {
1141 Vector<Array<MF,AMREX_SPACEDIM>> fluxes(namrlevs);
1142 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1143 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1144 auto const& amf = *(a_flux[ilev][idim]);
1145 fluxes[ilev][idim].define(boxArray(amf), DistributionMap(amf), ncomp, 0);
1146 }
1147 LocalCopy(sol[ilev], *a_sol[ilev], 0, 0, ncomp, nGrowVect(sol[ilev]));
1148 }
1149 linop.getFluxes(GetVecOfArrOfPtrs(fluxes), GetVecOfPtrs(sol), a_loc);
1150 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1151 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1152 LocalCopy(*a_flux[ilev][idim], fluxes[ilev][idim], 0, 0, ncomp, IntVect(0));
1153 }
1154 }
1155 }
1156}
1157
1158template <typename MF>
1159template <typename AMF>
1160void
1162 std::initializer_list<AMF*> a_sol, Location a_loc)
1163{
1164 getFluxes(Vector<Array<AMF*,AMREX_SPACEDIM>>(std::move(a_flux)),
1165 Vector<AMF*>(std::move(a_sol)), a_loc);
1166}
1167
1168template <typename MF>
1169template <typename AMF>
1170void
1172{
1173 AMREX_ASSERT(sol.size() == a_flux.size());
1174 if constexpr (std::is_same<AMF,MF>()) {
1175 getFluxes(a_flux, GetVecOfPtrs(sol), a_loc);
1176 } else {
1177 Vector<MF> fluxes(namrlevs);
1178 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1179 auto const& amf = *a_flux[ilev];
1180 fluxes[ilev].define(boxArray(amf), DistributionMap(amf), AMREX_SPACEDIM, 0);
1181 }
1182 getFluxes(GetVecOfPtrs(fluxes), GetVecOfPtrs(sol), a_loc);
1183 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1184 LocalCopy(*a_flux[ilev], fluxes[ilev], 0, 0, AMREX_SPACEDIM, IntVect(0));
1185 }
1186 }
1187}
1188
1189template <typename MF>
1190template <typename AMF>
1191void
1192MLMGT<MF>::getFluxes (std::initializer_list<AMF*> a_flux, Location a_loc)
1193{
1194 getFluxes(Vector<AMF*>(std::move(a_flux)), a_loc);
1195}
1196
1197template <typename MF>
1198template <typename AMF>
1199void
1201 const Vector<AMF*>& a_sol, Location /*a_loc*/)
1202{
1203 AMREX_ASSERT(nComp(*a_flux[0]) >= AMREX_SPACEDIM);
1204
1205 if constexpr (! std::is_same<AMF,MF>()) {
1206 for (int alev = 0; alev < namrlevs; ++alev) {
1207 LocalCopy(sol[alev], *a_sol[alev], 0, 0, ncomp, nGrowVect(sol[alev]));
1208 }
1209 }
1210
1211 if (linop.isCellCentered())
1212 {
1213 Vector<Array<MF,AMREX_SPACEDIM> > ffluxes(namrlevs);
1214 for (int alev = 0; alev < namrlevs; ++alev) {
1215 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1216 const int mglev = 0;
1217 int nghost = 0;
1218 if (cf_strategy == CFStrategy::ghostnodes) { nghost = linop.getNGrow(alev); }
1219 ffluxes[alev][idim].define(amrex::convert(linop.m_grids[alev][mglev],
1221 linop.m_dmap[alev][mglev], ncomp, nghost, MFInfo(),
1222 *linop.m_factory[alev][mglev]);
1223 }
1224 }
1225 if constexpr (std::is_same<AMF,MF>()) {
1226 getFluxes(amrex::GetVecOfArrOfPtrs(ffluxes), a_sol, Location::FaceCenter);
1227 } else {
1228 getFluxes(amrex::GetVecOfArrOfPtrs(ffluxes), GetVecOfPtrs(sol), Location::FaceCenter);
1229 }
1230 for (int alev = 0; alev < namrlevs; ++alev) {
1231#ifdef AMREX_USE_EB
1232 EB_average_face_to_cellcenter(*a_flux[alev], 0, amrex::GetArrOfConstPtrs(ffluxes[alev]));
1233#else
1234 average_face_to_cellcenter(*a_flux[alev], 0, amrex::GetArrOfConstPtrs(ffluxes[alev]));
1235#endif
1236 }
1237
1238 } else {
1239 if constexpr (std::is_same<AMF,MF>()) {
1240 linop.getFluxes(a_flux, a_sol);
1241 } else {
1242 Vector<MF> fluxes(namrlevs);
1243 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1244 auto const& amf = *a_flux[ilev];
1245 fluxes[ilev].define(boxArray(amf), DistributionMap(amf), AMREX_SPACEDIM, 0);
1246 }
1247 linop.getFluxes(GetVecOfPtrs(fluxes), GetVecOfPtrs(sol));
1248 for (int ilev = 0; ilev < namrlevs; ++ilev) {
1249 LocalCopy(*a_flux[ilev], fluxes[ilev], 0, 0, AMREX_SPACEDIM, IntVect(0));
1250 }
1251 }
1252 }
1253}
1254
1255template <typename MF>
1256template <typename AMF>
1257void
1258MLMGT<MF>::getFluxes (std::initializer_list<AMF*> a_flux,
1259 std::initializer_list<AMF*> a_sol, Location a_loc)
1260{
1261 getFluxes(Vector<AMF*>(std::move(a_flux)),
1262 Vector<AMF*>(std::move(a_sol)), a_loc);
1263}
1264
1265#ifdef AMREX_USE_EB
1266template <typename MF>
1267void
1269{
1270 if (!linop.isCellCentered()) {
1271 amrex::Abort("getEBFluxes is for cell-centered only");
1272 }
1273
1274 AMREX_ASSERT(sol.size() == a_eb_flux.size());
1275 getEBFluxes(a_eb_flux, GetVecOfPtrs(sol));
1276}
1277
1278template <typename MF>
1279void
1280MLMGT<MF>::getEBFluxes (const Vector<MF*>& a_eb_flux, const Vector<MF*>& a_sol)
1281{
1282 BL_PROFILE("MLMG::getEBFluxes()");
1283
1284 if (!linop.isCellCentered()) {
1285 amrex::Abort("getEBFluxes is for cell-centered only");
1286 }
1287
1288 linop.getEBFluxes(a_eb_flux, a_sol);
1289}
1290#endif
1291
1292template <typename MF>
1293void
1295 const Vector<MF const*>& a_rhs)
1296{
1297 BL_PROFILE("MLMG::compResidual()");
1298
1299 IntVect ng_sol(1);
1300 if (linop.hasHiddenDimension()) { ng_sol[linop.hiddenDirection()] = 0; }
1301
1302 sol.resize(namrlevs);
1303 sol_is_alias.resize(namrlevs,true);
1304 for (int alev = 0; alev < namrlevs; ++alev)
1305 {
1306 if (cf_strategy == CFStrategy::ghostnodes || nGrowVect(*a_sol[alev]) == ng_sol)
1307 {
1308 sol[alev] = linop.makeAlias(*a_sol[alev]);
1309 sol_is_alias[alev] = true;
1310 }
1311 else
1312 {
1313 if (sol_is_alias[alev])
1314 {
1315 sol[alev] = linop.make(alev, 0, ng_sol);
1316 sol_is_alias[alev] = false;
1317 }
1318 LocalCopy(sol[alev], *a_sol[alev], 0, 0, ncomp, IntVect(0));
1319 }
1320 }
1321
1322 prepareLinOp();
1323
1324
1325 for (int alev = finest_amr_lev; alev >= 0; --alev) {
1326 const MF* crse_bcdata = (alev > 0) ? &(sol[alev-1]) : nullptr;
1327 const MF* prhs = a_rhs[alev];
1328#if (AMREX_SPACEDIM != 3)
1329 int nghost = (cf_strategy == CFStrategy::ghostnodes) ? linop.getNGrow(alev) : 0;
1330 MF rhstmp(boxArray(*prhs), DistributionMap(*prhs), ncomp, nghost,
1331 MFInfo(), *linop.Factory(alev));
1332 LocalCopy(rhstmp, *prhs, 0, 0, ncomp, IntVect(nghost));
1333 linop.applyMetricTerm(alev, 0, rhstmp);
1334 linop.unimposeNeumannBC(alev, rhstmp);
1335 linop.applyInhomogNeumannTerm(alev, rhstmp);
1336 prhs = &rhstmp;
1337#endif
1338 linop.solutionResidual(alev, *a_res[alev], sol[alev], *prhs, crse_bcdata);
1339 if (alev < finest_amr_lev) {
1340 linop.reflux(alev, *a_res[alev], sol[alev], *prhs,
1341 *a_res[alev+1], sol[alev+1], *a_rhs[alev+1]);
1342 if (linop.isCellCentered()) {
1343#ifdef AMREX_USE_EB
1344 EB_average_down(*a_res[alev+1], *a_res[alev], 0, ncomp, linop.AMRRefRatioVect(alev));
1345#else
1346 average_down(*a_res[alev+1], *a_res[alev], 0, ncomp, linop.AMRRefRatioVect(alev));
1347#endif
1348 }
1349 }
1350 }
1351
1352
1353#if (AMREX_SPACEDIM != 3)
1354 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1355 linop.unapplyMetricTerm(alev, 0, *a_res[alev]);
1356 }
1357#endif
1358}
1359
1360template <typename MF>
1361void
1363{
1364 BL_PROFILE("MLMG::apply()");
1365
1366 Vector<MF*> in(namrlevs);
1367 Vector<MF> in_raii(namrlevs);
1368 Vector<MF> rh(namrlevs);
1369 int nghost = 0;
1370 IntVect ng_sol(1);
1371 if (linop.hasHiddenDimension()) { ng_sol[linop.hiddenDirection()] = 0; }
1372
1373 for (int alev = 0; alev < namrlevs; ++alev)
1374 {
1375 if (cf_strategy == CFStrategy::ghostnodes)
1376 {
1377 nghost = linop.getNGrow(alev);
1378 in[alev] = a_in[alev];
1379 }
1380 else if (nGrowVect(*a_in[alev]) == ng_sol)
1381 {
1382 in[alev] = a_in[alev];
1383 }
1384 else
1385 {
1386 IntVect ng = ng_sol;
1387 if (cf_strategy == CFStrategy::ghostnodes) { ng = IntVect(nghost); }
1388 in_raii[alev] = linop.make(alev, 0, ng,
1389 MFInfo().SetArena(The_Async_Arena()));
1390 LocalCopy(in_raii[alev], *a_in[alev], 0, 0, ncomp, IntVect(nghost));
1391 in[alev] = &(in_raii[alev]);
1392 }
1393 rh[alev] = linop.make(alev, 0, IntVect(nghost),
1395 setVal(rh[alev], RT(0.0));
1396 }
1397
1398 prepareLinOp();
1399
1400 for (int alev = 0; alev < namrlevs; ++alev) {
1401 linop.applyInhomogNeumannTerm(alev, rh[alev]);
1402 }
1403
1404
1405 for (int alev = finest_amr_lev; alev >= 0; --alev) {
1406 const MF* crse_bcdata = (alev > 0) ? in[alev-1] : nullptr;
1407 linop.solutionResidual(alev, *out[alev], *in[alev], rh[alev], crse_bcdata);
1408 if (alev < finest_amr_lev) {
1409 linop.reflux(alev, *out[alev], *in[alev], rh[alev],
1410 *out[alev+1], *in[alev+1], rh[alev+1]);
1411 if (linop.isCellCentered()) {
1412 if constexpr (IsMultiFabLike_v<MF>) {
1413#ifdef AMREX_USE_EB
1414 EB_average_down(*out[alev+1], *out[alev], 0, nComp(*out[alev]), linop.AMRRefRatioVect(alev));
1415#else
1416 average_down(*out[alev+1], *out[alev], 0, nComp(*out[alev]), linop.AMRRefRatioVect(alev));
1417#endif
1418 } else {
1419 amrex::Abort("MLMG: TODO average_down for non-MultiFab");
1420 }
1421 }
1422 }
1423 }
1424
1425#if (AMREX_SPACEDIM != 3)
1426 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1427 linop.unapplyMetricTerm(alev, 0, *out[alev]);
1428 }
1429#endif
1430
1431 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
1432 if (cf_strategy == CFStrategy::ghostnodes) { nghost = linop.getNGrow(alev); }
1433 Scale(*out[alev], RT(-1), 0, nComp(*out[alev]), nghost);
1434 }
1435}
1436
1437template <typename MF>
1438void
1440{
1441 precond_mode = true;
1442 linop.beginPrecondBC();
1443 apply(out, in);
1444 linop.endPrecondBC();
1445 precond_mode = false;
1446}
1447
1448template <typename MF>
1449template <typename AMF>
1450void
1452{
1453 BL_PROFILE("MLMG::prepareForSolve()");
1454
1455 AMREX_ASSERT(namrlevs <= a_sol.size());
1456 AMREX_ASSERT(namrlevs <= a_rhs.size());
1457
1458 timer.assign(ntimers, 0.0);
1459
1460 IntVect ng_rhs(0);
1461 IntVect ng_sol(1);
1462 if (linop.hasHiddenDimension()) { ng_sol[linop.hiddenDirection()] = 0; }
1463
1464 if (!linop_prepared) {
1465 linop.prepareForSolve();
1466 linop_prepared = true;
1467 } else if (linop.needsUpdate()) {
1468 linop.update();
1469
1470#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
1471 hypre_solver.reset();
1472 hypre_bndry.reset();
1473 hypre_node_solver.reset();
1474#endif
1475
1476#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
1477 petsc_solver.reset();
1478 petsc_bndry.reset();
1479#endif
1480 }
1481
1482 sol.resize(namrlevs);
1483 sol_is_alias.resize(namrlevs,false);
1484 for (int alev = 0; alev < namrlevs; ++alev)
1485 {
1486 if (cf_strategy == CFStrategy::ghostnodes)
1487 {
1488 if constexpr (std::is_same<AMF,MF>()) {
1489 sol[alev] = linop.makeAlias(*a_sol[alev]);
1490 sol_is_alias[alev] = true;
1491 } else {
1492 amrex::Abort("Type conversion not supported for CFStrategy::ghostnodes");
1493 }
1494 }
1495 else
1496 {
1497 bool alias_made = false;
1498 if (nGrowVect(*a_sol[alev]) == ng_sol) {
1499 if constexpr (std::is_same<AMF,MF>()) {
1500 sol[alev] = linop.makeAlias(*a_sol[alev]);
1501 sol_is_alias[alev] = true;
1502 alias_made = true;
1503 }
1504 }
1505 if (!alias_made) {
1506 if (sol_is_alias[alev] || !solve_called) {
1507 sol[alev] = linop.make(alev, 0, ng_sol);
1508 sol_is_alias[alev] = false;
1509 }
1510 LocalCopy(sol[alev], *a_sol[alev], 0, 0, ncomp, IntVect(0));
1511 setBndry(sol[alev], RT(0.0), 0, ncomp);
1512 }
1513 }
1514 }
1515
1516 rhs.resize(namrlevs);
1517 for (int alev = 0; alev < namrlevs; ++alev)
1518 {
1519 if (cf_strategy == CFStrategy::ghostnodes) { ng_rhs = IntVect(linop.getNGrow(alev)); }
1520 if (!solve_called) {
1521 rhs[alev] = linop.make(alev, 0, ng_rhs);
1522 }
1523 LocalCopy(rhs[alev], *a_rhs[alev], 0, 0, ncomp, ng_rhs);
1524 linop.applyMetricTerm(alev, 0, rhs[alev]);
1525 linop.unimposeNeumannBC(alev, rhs[alev]);
1526 linop.applyInhomogNeumannTerm(alev, rhs[alev]);
1527 linop.applyOverset(alev, rhs[alev]);
1528 if ( ! precond_mode) {
1529 bool r = linop.scaleRHS(alev, &(rhs[alev]));
1531 }
1532
1533#ifdef AMREX_USE_EB
1534 const auto *factory = dynamic_cast<EBFArrayBoxFactory const*>(linop.Factory(alev));
1535 if (factory && !factory->isAllRegular()) {
1536 if constexpr (std::is_same<MF,MultiFab>()) {
1537 EB_set_covered(rhs[alev], 0, ncomp, 0, RT(0.0));
1538 EB_set_covered(sol[alev], 0, ncomp, 0, RT(0.0));
1539 } else {
1540 amrex::Abort("TODO: MLMG with EB only works with MultiFab");
1541 }
1542 }
1543#endif
1544 }
1545
1546 for (int falev = finest_amr_lev; falev > 0; --falev)
1547 {
1548 linop.averageDownSolutionRHS(falev-1, sol[falev-1], rhs[falev-1], sol[falev], rhs[falev]);
1549 }
1550
1551 // enforce solvability if appropriate
1552 if (linop.isSingular(0) && linop.getEnforceSingularSolvable())
1553 {
1554 makeSolvable();
1555 }
1556
1557 IntVect ng = linop.getNGrowVectRestriction();
1558 if (cf_strategy == CFStrategy::ghostnodes) { ng = ng_rhs; }
1559 if (!solve_called) {
1560 linop.make(res, ng);
1561 linop.make(rescor, ng);
1562 }
1563 for (int alev = 0; alev <= finest_amr_lev; ++alev)
1564 {
1565 const int nmglevs = linop.NMGLevels(alev);
1566 for (int mglev = 0; mglev < nmglevs; ++mglev)
1567 {
1568 setVal(res [alev][mglev], RT(0.0));
1569 setVal(rescor[alev][mglev], RT(0.0));
1570 }
1571 }
1572
1573 if (cf_strategy != CFStrategy::ghostnodes) { ng = ng_sol; }
1574 cor.resize(namrlevs);
1575 for (int alev = 0; alev <= finest_amr_lev; ++alev)
1576 {
1577 const int nmglevs = linop.NMGLevels(alev);
1578 cor[alev].resize(nmglevs);
1579 for (int mglev = 0; mglev < nmglevs; ++mglev)
1580 {
1581 if (!solve_called) {
1582 IntVect _ng = ng;
1583 if (cf_strategy == CFStrategy::ghostnodes) { _ng=IntVect(linop.getNGrow(alev,mglev)); }
1584 cor[alev][mglev] = linop.make(alev, mglev, _ng);
1585 }
1586 setVal(cor[alev][mglev], RT(0.0));
1587 }
1588 }
1589
1590 cor_hold.resize(std::max(namrlevs-1,1));
1591 {
1592 const int alev = 0;
1593 const int nmglevs = linop.NMGLevels(alev);
1594 cor_hold[alev].resize(nmglevs);
1595 for (int mglev = 0; mglev < nmglevs-1; ++mglev)
1596 {
1597 if (!solve_called) {
1598 IntVect _ng = ng;
1599 if (cf_strategy == CFStrategy::ghostnodes) { _ng=IntVect(linop.getNGrow(alev,mglev)); }
1600 cor_hold[alev][mglev] = linop.make(alev, mglev, _ng);
1601 }
1602 setVal(cor_hold[alev][mglev], RT(0.0));
1603 }
1604 }
1605 for (int alev = 1; alev < finest_amr_lev; ++alev)
1606 {
1607 cor_hold[alev].resize(1);
1608 if (!solve_called) {
1609 IntVect _ng = ng;
1610 if (cf_strategy == CFStrategy::ghostnodes) { _ng=IntVect(linop.getNGrow(alev)); }
1611 cor_hold[alev][0] = linop.make(alev, 0, _ng);
1612 }
1613 setVal(cor_hold[alev][0], RT(0.0));
1614 }
1615
1616 if (linop.m_parent // no embedded N-Solve
1617 || !linop.supportNSolve())
1618 {
1619 do_nsolve = false;
1620 }
1621
1622 if (do_nsolve && ns_linop == nullptr)
1623 {
1624 prepareForNSolve();
1625 }
1626
1627 if (verbose >= 2) {
1628 amrex::Print() << print_ident << "MLMG: # of AMR levels: " << namrlevs << "\n"
1629 << print_ident << " # of MG levels on the coarsest AMR level: " << linop.NMGLevels(0)
1630 << "\n";
1631 if (ns_linop) {
1632 amrex::Print() << print_ident << " # of MG levels in N-Solve: " << ns_linop->NMGLevels(0) << "\n"
1633 << print_ident << " # of grids in N-Solve: " << ns_linop->m_grids[0][0].size() << "\n";
1634 }
1635 }
1636}
1637
1638template <typename MF>
1639void
1641{
1642 if (!linop_prepared) {
1643 linop.prepareForSolve();
1644 linop_prepared = true;
1645 } else if (linop.needsUpdate()) {
1646 linop.update();
1647
1648#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
1649 hypre_solver.reset();
1650 hypre_bndry.reset();
1651 hypre_node_solver.reset();
1652#endif
1653
1654#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
1655 petsc_solver.reset();
1656 petsc_bndry.reset();
1657#endif
1658 }
1659}
1660
1661template <typename MF>
1662void
1664{
1665 prepareLinOp();
1666 linop.preparePrecond();
1667}
1668
1669template <typename MF>
1670void
1672{
1673 if constexpr (IsMultiFabLike_v<MF>) {
1674 ns_linop = linop.makeNLinOp(nsolve_grid_size);
1675
1676 int nghost = 0;
1677 if (cf_strategy == CFStrategy::ghostnodes) { nghost = linop.getNGrow(); }
1678
1679 const BoxArray& ba = (*ns_linop).m_grids[0][0];
1680 const DistributionMapping& dm =(*ns_linop).m_dmap[0][0];
1681
1682 int ng = 1;
1683 if (cf_strategy == CFStrategy::ghostnodes) { ng = nghost; }
1684 ns_sol = std::make_unique<MF>(ba, dm, ncomp, ng, MFInfo(), *(ns_linop->Factory(0,0)));
1685 ng = 0;
1686 if (cf_strategy == CFStrategy::ghostnodes) { ng = nghost; }
1687 ns_rhs = std::make_unique<MF>(ba, dm, ncomp, ng, MFInfo(), *(ns_linop->Factory(0,0)));
1688 setVal(*ns_sol, RT(0.0));
1689 setVal(*ns_rhs, RT(0.0));
1690
1691 ns_linop->setLevelBC(0, ns_sol.get());
1692
1693 ns_mlmg = std::make_unique<MLMGT<MF>>(*ns_linop);
1694 ns_mlmg->setVerbose(0);
1695 ns_mlmg->setFixedIter(1);
1696 ns_mlmg->setMaxFmgIter(20);
1697 ns_mlmg->setBottomSolver(BottomSolver::smoother);
1698 }
1699}
1700
1701// in : Residual (res) on the finest AMR level
1702// out : sol on all AMR levels
1703template <typename MF>
1704void MLMGT<MF>::oneIter (int iter)
1705{
1706 BL_PROFILE("MLMG::oneIter()");
1707
1708 for (int alev = finest_amr_lev; alev > 0; --alev)
1709 {
1710 miniCycle(alev);
1711
1712 IntVect nghost(0);
1713 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(alev)); }
1714 LocalAdd(sol[alev], cor[alev][0], 0, 0, ncomp, nghost);
1715
1716 // compute residual for the coarse AMR level
1717 computeResWithCrseSolFineCor(alev-1,alev);
1718
1719 if (alev != finest_amr_lev) {
1720 std::swap(cor_hold[alev][0], cor[alev][0]); // save it for the up cycle
1721 }
1722 }
1723
1724 // coarsest amr level
1725 {
1726 // enforce solvability if appropriate
1727 if (linop.isSingular(0) && linop.getEnforceSingularSolvable())
1728 {
1729 makeSolvable(0,0,res[0][0]);
1730 }
1731
1732 if (iter < max_fmg_iters) {
1733 mgFcycle();
1734 } else {
1735 mgVcycle(0, 0);
1736 }
1737
1738 IntVect nghost(0);
1739 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(0)); }
1740 LocalAdd(sol[0], cor[0][0], 0, 0, ncomp, nghost);
1741 }
1742
1743 for (int alev = 1; alev <= finest_amr_lev; ++alev)
1744 {
1745 // (Fine AMR correction) = I(Coarse AMR correction)
1746 interpCorrection(alev);
1747
1748 IntVect nghost(0);
1749 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(alev)); }
1750 LocalAdd(sol[alev], cor[alev][0], 0, 0, ncomp, nghost);
1751
1752 if (alev != finest_amr_lev) {
1753 LocalAdd(cor_hold[alev][0], cor[alev][0], 0, 0, ncomp, nghost);
1754 }
1755
1756 // Update fine AMR level correction
1757 computeResWithCrseCorFineCor(alev);
1758
1759 miniCycle(alev);
1760
1761 LocalAdd(sol[alev], cor[alev][0], 0, 0, ncomp, nghost);
1762
1763 if (alev != finest_amr_lev) {
1764 LocalAdd(cor[alev][0], cor_hold[alev][0], 0, 0, ncomp, nghost);
1765 }
1766 }
1767
1768 linop.averageDownAndSync(sol);
1769}
1770
1771template <typename MF>
1772void
1774{
1775 BL_PROFILE("MLMG::miniCycle()");
1776 const int mglev = 0;
1777 mgVcycle(amrlev, mglev);
1778}
1779
1780// in : Residual (res)
1781// out : Correction (cor) from bottom to this function's local top
1782template <typename MF>
1783void
1784MLMGT<MF>::mgVcycle (int amrlev, int mglev_top)
1785{
1786 BL_PROFILE("MLMG::mgVcycle()");
1787
1788 const int mglev_bottom = linop.NMGLevels(amrlev) - 1;
1789
1790 for (int mglev = mglev_top; mglev < mglev_bottom; ++mglev)
1791 {
1792 BL_PROFILE_VAR("MLMG::mgVcycle_down::"+std::to_string(mglev), blp_mgv_down_lev);
1793
1794 if (verbose >= 4)
1795 {
1796 RT norm = norminf(res[amrlev][mglev],0,ncomp,IntVect(0));
1797 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
1798 << " DN: Norm before smooth " << norm << "\n";
1799 }
1800
1801 setVal(cor[amrlev][mglev], RT(0.0));
1802 bool skip_fillboundary = true;
1803 linop.smooth(amrlev, mglev, cor[amrlev][mglev], res[amrlev][mglev], skip_fillboundary, nu1);
1804
1805 // rescor = res - L(cor)
1806 computeResOfCorrection(amrlev, mglev);
1807
1808 if (verbose >= 4)
1809 {
1810 RT norm = norminf(rescor[amrlev][mglev],0,ncomp,IntVect(0));
1811 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
1812 << " DN: Norm after smooth " << norm << "\n";
1813 }
1814
1815 // res_crse = R(rescor_fine); this provides res/b to the level below
1816 linop.restriction(amrlev, mglev+1, res[amrlev][mglev+1], rescor[amrlev][mglev]);
1817 }
1818
1819 BL_PROFILE_VAR("MLMG::mgVcycle_bottom", blp_bottom);
1820 if (amrlev == 0)
1821 {
1822 if (verbose >= 4)
1823 {
1824 RT norm = norminf(res[amrlev][mglev_bottom],0,ncomp,IntVect(0));
1825 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
1826 << " DN: Norm before bottom " << norm << "\n";
1827 }
1828 bottomSolve();
1829 if (verbose >= 4)
1830 {
1831 computeResOfCorrection(amrlev, mglev_bottom);
1832 RT norm = norminf(rescor[amrlev][mglev_bottom],0,ncomp,IntVect(0));
1833 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
1834 << " UP: Norm after bottom " << norm << "\n";
1835 }
1836 }
1837 else
1838 {
1839 if (verbose >= 4)
1840 {
1841 RT norm = norminf(res[amrlev][mglev_bottom],0,ncomp,IntVect(0));
1842 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
1843 << " Norm before smooth " << norm << "\n";
1844 }
1845 setVal(cor[amrlev][mglev_bottom], RT(0.0));
1846 bool skip_fillboundary = true;
1847 linop.smooth(amrlev, mglev_bottom, cor[amrlev][mglev_bottom],
1848 res[amrlev][mglev_bottom], skip_fillboundary, nu1);
1849 if (verbose >= 4)
1850 {
1851 computeResOfCorrection(amrlev, mglev_bottom);
1852 RT norm = norminf(rescor[amrlev][mglev_bottom],0,ncomp,IntVect(0));
1853 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev_bottom
1854 << " Norm after smooth " << norm << "\n";
1855 }
1856 }
1857 BL_PROFILE_VAR_STOP(blp_bottom);
1858
1859 for (int mglev = mglev_bottom-1; mglev >= mglev_top; --mglev)
1860 {
1861 BL_PROFILE_VAR("MLMG::mgVcycle_up::"+std::to_string(mglev), blp_mgv_up_lev);
1862 // cor_fine += I(cor_crse)
1863 addInterpCorrection(amrlev, mglev);
1864 if (verbose >= 4)
1865 {
1866 computeResOfCorrection(amrlev, mglev);
1867 RT norm = norminf(rescor[amrlev][mglev],0,ncomp,IntVect(0));
1868 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
1869 << " UP: Norm before smooth " << norm << "\n";
1870 }
1871 linop.smooth(amrlev, mglev, cor[amrlev][mglev], res[amrlev][mglev], false, nu2);
1872
1873 if (cf_strategy == CFStrategy::ghostnodes) { computeResOfCorrection(amrlev, mglev); }
1874
1875 if (verbose >= 4)
1876 {
1877 computeResOfCorrection(amrlev, mglev);
1878 RT norm = norminf(rescor[amrlev][mglev],0,ncomp,IntVect(0));
1879 amrex::Print() << print_ident << "AT LEVEL " << amrlev << " " << mglev
1880 << " UP: Norm after smooth " << norm << "\n";
1881 }
1882 }
1883}
1884
1885// FMG cycle on the coarsest AMR level.
1886// in: Residual on the top MG level (i.e., 0)
1887// out: Correction (cor) on all MG levels
1888template <typename MF>
1889void
1891{
1892 BL_PROFILE("MLMG::mgFcycle()");
1893
1894#ifdef AMREX_USE_EB
1895 auto* pf = linop.Factory(0);
1896 auto is_all_regular = [pf] () {
1897 const auto *const f = dynamic_cast<EBFArrayBoxFactory const*>(pf);
1898 if (f) {
1899 return f->isAllRegular();
1900 } else {
1901 return true;
1902 }
1903 };
1904 amrex::ignore_unused(pf, is_all_regular);
1905 AMREX_ASSERT(linop.isCellCentered() || is_all_regular());
1906#endif
1907
1908 const int amrlev = 0;
1909 const int mg_bottom_lev = linop.NMGLevels(amrlev) - 1;
1910 IntVect nghost(0);
1911 if (cf_strategy == CFStrategy::ghostnodes) { nghost = IntVect(linop.getNGrow(amrlev)); }
1912
1913 for (int mglev = 1; mglev <= mg_bottom_lev; ++mglev)
1914 {
1915 linop.avgDownResMG(mglev, res[amrlev][mglev], res[amrlev][mglev-1]);
1916 }
1917
1918 bottomSolve();
1919
1920 for (int mglev = mg_bottom_lev-1; mglev >= 0; --mglev)
1921 {
1922 // cor_fine = I(cor_crse)
1923 interpCorrection(amrlev, mglev);
1924
1925 // rescor = res - L(cor)
1926 computeResOfCorrection(amrlev, mglev);
1927 // res = rescor; this provides b to the vcycle below
1928 LocalCopy(res[amrlev][mglev], rescor[amrlev][mglev], 0, 0, ncomp, nghost);
1929
1930 // save cor; do v-cycle; add the saved to cor
1931 std::swap(cor[amrlev][mglev], cor_hold[amrlev][mglev]);
1932 mgVcycle(amrlev, mglev);
1933 LocalAdd(cor[amrlev][mglev], cor_hold[amrlev][mglev], 0, 0, ncomp, nghost);
1934 }
1935}
1936
1937// At the true bottom of the coarsest AMR level.
1938// in : Residual (res) as b
1939// out : Correction (cor) as x
1940template <typename MF>
1941void
1943{
1944 if (do_nsolve)
1945 {
1946 NSolve(*ns_mlmg, *ns_sol, *ns_rhs);
1947 }
1948 else
1949 {
1950 actualBottomSolve();
1951 }
1952}
1953
1954template <typename MF>
1955void
1956MLMGT<MF>::NSolve (MLMGT<MF>& a_solver, MF& a_sol, MF& a_rhs)
1957{
1958 BL_PROFILE("MLMG::NSolve()");
1959
1960 setVal(a_sol, RT(0.0));
1961
1962 MF const& res_bottom = res[0].back();
1963 if (BoxArray::SameRefs(boxArray(a_rhs),boxArray(res_bottom)) &&
1965 {
1966 LocalCopy(a_rhs, res_bottom, 0, 0, ncomp, IntVect(0));
1967 } else {
1968 setVal(a_rhs, RT(0.0));
1969 ParallelCopy(a_rhs, res_bottom, 0, 0, ncomp);
1970 }
1971
1972 a_solver.solve(Vector<MF*>{&a_sol}, Vector<MF const*>{&a_rhs},
1973 RT(-1.0), RT(-1.0));
1974
1975 linop.copyNSolveSolution(cor[0].back(), a_sol);
1976}
1977
1978template <typename MF>
1979void
1980MLMGT<MF>::postCG (int ret, int niters)
1981{
1982 if (niters >= 0) {
1983 m_niters_cg.push_back(niters);
1984 }
1985
1986 const int amrlev = 0;
1987 const int mglev = linop.NMGLevels(amrlev) - 1;
1988 auto& x = cor[amrlev][mglev];
1989 auto& b = res[amrlev][mglev];
1990 const int n = (ret==0) ? nub : nuf;
1991 linop.smooth(amrlev, mglev, x, b, false, n);
1992}
1993
1994template <typename MF>
1995void
1997{
1998 BL_PROFILE("MLMG::actualBottomSolve()");
1999
2000 if (!linop.isBottomActive()) { return; }
2001
2002 auto bottom_start_time = amrex::second();
2003
2004 ParallelContext::push(linop.BottomCommunicator());
2005
2006 const int amrlev = 0;
2007 const int mglev = linop.NMGLevels(amrlev) - 1;
2008 auto& x = cor[amrlev][mglev];
2009 auto& b = res[amrlev][mglev];
2010
2011 setVal(x, RT(0.0));
2012
2013 if (bottom_solver == BottomSolver::smoother)
2014 {
2015 bool skip_fillboundary = true;
2016 linop.smooth(amrlev, mglev, x, b, skip_fillboundary, nuf);
2017 }
2018 else
2019 {
2020 MF* bottom_b = &b;
2021 MF raii_b;
2022 if (linop.isBottomSingular() && linop.getEnforceSingularSolvable())
2023 {
2024 const IntVect ng = nGrowVect(b);
2025 raii_b = linop.make(amrlev, mglev, ng,
2026 MFInfo().SetArena(The_Async_Arena()));
2027 LocalCopy(raii_b, b, 0, 0, ncomp, ng);
2028 bottom_b = &raii_b;
2029
2030 makeSolvable(amrlev,mglev,*bottom_b);
2031 }
2032
2033 if (bottom_solver == BottomSolver::hypre)
2034 {
2035#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
2036 if constexpr (std::is_same<MF,MultiFab>()) {
2037 bottomSolveWithHypre(x, *bottom_b);
2038 } else
2039#endif
2040 {
2041 amrex::Abort("Using Hypre as bottom solver not supported in this case");
2042 }
2043 }
2044 else if (bottom_solver == BottomSolver::petsc)
2045 {
2046#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
2047 if constexpr (std::is_same<MF,MultiFab>()) {
2048 bottomSolveWithPETSc(x, *bottom_b);
2049 } else
2050#endif
2051 {
2052 amrex::Abort("Using PETSc as bottom solver not supported in this case");
2053 }
2054 }
2055 else if (bottom_solver == BottomSolver::custom && linop.supportCustomBottomSolver())
2056 {
2057 linop.customBottomSolve(this, x, *bottom_b, bottom_reltol, bottom_abstol,
2058 bottom_maxiter);
2059 }
2060 else
2061 {
2062 typename MLCGSolverT<MF>::Type cg_type;
2063 if (bottom_solver == BottomSolver::cg ||
2064 bottom_solver == BottomSolver::cgbicg) {
2065 cg_type = MLCGSolverT<MF>::Type::CG;
2066 } else {
2068 }
2069
2070 int ret = bottomSolveWithCG(x, *bottom_b, cg_type);
2071
2072 if (ret != 0 && (bottom_solver == BottomSolver::cgbicg ||
2073 bottom_solver == BottomSolver::bicgcg))
2074 {
2075 if (bottom_solver == BottomSolver::cgbicg) {
2076 cg_type = MLCGSolverT<MF>::Type::BiCGStab; // switch to bicg
2077 } else {
2078 cg_type = MLCGSolverT<MF>::Type::CG; // switch to cg
2079 }
2080 if (ret == 9) {
2081 // if ret != 9 && ret != 0, x has been set to zero in bottomSolveWithCG
2082 setVal(x, RT(0));
2083 }
2084 ret = bottomSolveWithCG(x, *bottom_b, cg_type);
2085 if (ret == 0) { // switch permanently
2086 if (cg_type == MLCGSolverT<MF>::Type::CG) {
2087 bottom_solver = BottomSolver::cg;
2088 } else {
2089 bottom_solver = BottomSolver::bicgstab;
2090 }
2091 }
2092 }
2093
2094 postCG(ret);
2095 }
2096 }
2097
2099
2100 if (! timer.empty()) {
2101 timer[bottom_time] += amrex::second() - bottom_start_time;
2102 }
2103}
2104
2105template <typename MF>
2106int
2108{
2109 MLCGSolverT<MF> cg_solver(linop);
2110 cg_solver.setSolver(type);
2111 cg_solver.setVerbose(bottom_verbose);
2112 cg_solver.setPrintIdentation(print_ident);
2113 cg_solver.setMaxIter(bottom_maxiter);
2114 cg_solver.setInitSolnZeroed(true);
2115 if (cf_strategy == CFStrategy::ghostnodes) { cg_solver.setNGhost(linop.getNGrow()); }
2116
2117 int ret = cg_solver.solve(x, b, bottom_reltol, bottom_abstol);
2118 if (ret != 0 && verbose > 1) {
2119 amrex::Print() << print_ident << "MLMG: Bottom solve failed.\n";
2120 }
2121 m_niters_cg.push_back(cg_solver.getNumIters());
2122
2123 if (ret != 0 && ret != 9) {
2124 setVal(x, RT(0));
2125 }
2126
2127 return ret;
2128}
2129
2130// Compute multi-level Residual (res) up to amrlevmax.
2131template <typename MF>
2132void
2134{
2135 BL_PROFILE("MLMG::computeMLResidual()");
2136
2137 const int mglev = 0;
2138 for (int alev = amrlevmax; alev >= 0; --alev) {
2139 const MF* crse_bcdata = (alev > 0) ? &(sol[alev-1]) : nullptr;
2140 linop.solutionResidual(alev, res[alev][mglev], sol[alev], rhs[alev], crse_bcdata);
2141 if (alev < finest_amr_lev) {
2142 linop.reflux(alev, res[alev][mglev], sol[alev], rhs[alev],
2143 res[alev+1][mglev], sol[alev+1], rhs[alev+1]);
2144 }
2145 }
2146}
2147
2148// Compute single AMR level residual without masking.
2149template <typename MF>
2150void
2152{
2153 BL_PROFILE("MLMG::computeResidual()");
2154 const MF* crse_bcdata = (alev > 0) ? &(sol[alev-1]) : nullptr;
2155 linop.solutionResidual(alev, res[alev][0], sol[alev], rhs[alev], crse_bcdata);
2156}
2157
2158// Compute coarse AMR level composite residual with coarse solution and fine correction
2159template <typename MF>
2160void
2162{
2163 BL_PROFILE("MLMG::computeResWithCrseSolFineCor()");
2164
2165 IntVect nghost(0);
2166 if (cf_strategy == CFStrategy::ghostnodes) {
2167 nghost = IntVect(std::min(linop.getNGrow(falev),linop.getNGrow(calev)));
2168 }
2169
2170 MF& crse_sol = sol[calev];
2171 const MF& crse_rhs = rhs[calev];
2172 MF& crse_res = res[calev][0];
2173
2174 MF& fine_sol = sol[falev];
2175 const MF& fine_rhs = rhs[falev];
2176 MF& fine_cor = cor[falev][0];
2177 MF& fine_res = res[falev][0];
2178 MF& fine_rescor = rescor[falev][0];
2179
2180 const MF* crse_bcdata = (calev > 0) ? &(sol[calev-1]) : nullptr;
2181 linop.solutionResidual(calev, crse_res, crse_sol, crse_rhs, crse_bcdata);
2182
2183 linop.correctionResidual(falev, 0, fine_rescor, fine_cor, fine_res, BCMode::Homogeneous);
2184 LocalCopy(fine_res, fine_rescor, 0, 0, ncomp, nghost);
2185
2186 linop.reflux(calev, crse_res, crse_sol, crse_rhs, fine_res, fine_sol, fine_rhs);
2187
2188 linop.avgDownResAmr(calev, crse_res, fine_res);
2189}
2190
2191// Compute fine AMR level residual fine_res = fine_res - L(fine_cor) with coarse providing BC.
2192template <typename MF>
2193void
2195{
2196 BL_PROFILE("MLMG::computeResWithCrseCorFineCor()");
2197
2198 IntVect nghost(0);
2199 if (cf_strategy == CFStrategy::ghostnodes) {
2200 nghost = IntVect(linop.getNGrow(falev));
2201 }
2202
2203 const MF& crse_cor = cor[falev-1][0];
2204
2205 MF& fine_cor = cor [falev][0];
2206 MF& fine_res = res [falev][0];
2207 MF& fine_rescor = rescor[falev][0];
2208
2209 // fine_rescor = fine_res - L(fine_cor)
2210 linop.correctionResidual(falev, 0, fine_rescor, fine_cor, fine_res,
2211 BCMode::Inhomogeneous, &crse_cor);
2212 LocalCopy(fine_res, fine_rescor, 0, 0, ncomp, nghost);
2213}
2214
2215// Interpolate correction from coarse to fine AMR level.
2216template <typename MF>
2217void
2219{
2220 BL_PROFILE("MLMG::interpCorrection_1");
2221
2222 IntVect nghost(0);
2223 if (cf_strategy == CFStrategy::ghostnodes) {
2224 nghost = IntVect(linop.getNGrow(alev));
2225 }
2226
2227 MF & crse_cor = cor[alev-1][0];
2228 MF & fine_cor = cor[alev ][0];
2229
2230 const Geometry& crse_geom = linop.Geom(alev-1,0);
2231
2232 int ng_src = 0;
2233 int ng_dst = linop.isCellCentered() ? 1 : 0;
2234 if (cf_strategy == CFStrategy::ghostnodes)
2235 {
2236 ng_src = linop.getNGrow(alev-1);
2237 ng_dst = linop.getNGrow(alev-1);
2238 if constexpr (IsMultiFabLike_v<MF>) {
2239 crse_cor.FillBoundary(0, ncomp, IntVect(ng_src), crse_geom.periodicity());
2240 } else {
2241 amrex::Abort("MLMG: CFStrategy::ghostnodes not supported for non-MultiFab like types");
2242 }
2243 }
2244
2245 MF cfine = linop.makeCoarseAmr(alev, IntVect(ng_dst),
2246 MFInfo().SetArena(The_Async_Arena()));
2247 setVal(cfine, RT(0.0));
2248 ParallelCopy(cfine, crse_cor, 0, 0, ncomp, IntVect(ng_src), IntVect(ng_dst),
2249 crse_geom.periodicity());
2250
2251 linop.interpolationAmr(alev, fine_cor, cfine, nghost); // NOLINT(readability-suspicious-call-argument)
2252}
2253
2254// Interpolate correction between MG levels
2255// inout: Correction (cor) on coarse MG lev. (out due to FillBoundary)
2256// out : Correction (cor) on fine MG lev.
2257template <typename MF>
2258void
2259MLMGT<MF>::interpCorrection (int alev, int mglev)
2260{
2261 BL_PROFILE("MLMG::interpCorrection_2");
2262
2263 MF& crse_cor = cor[alev][mglev+1];
2264 MF& fine_cor = cor[alev][mglev ];
2265 linop.interpAssign(alev, mglev, fine_cor, crse_cor);
2266}
2267
2268// (Fine MG level correction) += I(Coarse MG level correction)
2269template <typename MF>
2270void
2272{
2273 BL_PROFILE("MLMG::addInterpCorrection()");
2274
2275 const MF& crse_cor = cor[alev][mglev+1];
2276 MF& fine_cor = cor[alev][mglev ];
2277
2278 MF cfine;
2279 const MF* cmf;
2280
2281 if (linop.isMFIterSafe(alev, mglev, mglev+1))
2282 {
2283 cmf = &crse_cor;
2284 }
2285 else
2286 {
2287 cfine = linop.makeCoarseMG(alev, mglev, IntVect(0),
2288 MFInfo().SetArena(The_Async_Arena()));
2289 ParallelCopy(cfine, crse_cor, 0, 0, ncomp);
2290 cmf = &cfine;
2291 }
2292
2293 linop.interpolation(alev, mglev, fine_cor, *cmf);
2294}
2295
2296// Compute rescor = res - L(cor)
2297// in : res
2298// inout: cor (out due to FillBoundary in linop.correctionResidual)
2299// out : rescor
2300template <typename MF>
2301void
2303{
2304 BL_PROFILE("MLMG:computeResOfCorrection()");
2305 MF & x = cor[amrlev][mglev];
2306 const MF& b = res[amrlev][mglev];
2307 MF & r = rescor[amrlev][mglev];
2308 linop.correctionResidual(amrlev, mglev, r, x, b, BCMode::Homogeneous);
2309}
2310
2311// Compute single-level masked inf-norm of Residual (res).
2312template <typename MF>
2313auto
2314MLMGT<MF>::ResNormInf (int alev, bool local) -> RT
2315{
2316 BL_PROFILE("MLMG::ResNormInf()");
2317 return linop.normInf(alev, res[alev][0], local);
2318}
2319
2320// Computes multi-level masked inf-norm of Residual (res).
2321template <typename MF>
2322auto
2323MLMGT<MF>::MLResNormInf (int alevmax, bool local) -> RT
2324{
2325 BL_PROFILE("MLMG::MLResNormInf()");
2326 RT r = RT(0.0);
2327 for (int alev = 0; alev <= alevmax; ++alev)
2328 {
2329 r = std::max(r, ResNormInf(alev,true));
2330 }
2332 return r;
2333}
2334
2335// Compute multi-level masked inf-norm of RHS (rhs).
2336template <typename MF>
2337auto
2339{
2340 BL_PROFILE("MLMG::MLRhsNormInf()");
2341 RT r = RT(0.0);
2342 for (int alev = 0; alev <= finest_amr_lev; ++alev) {
2343 auto t = linop.normInf(alev, rhs[alev], true);
2344 r = std::max(r, t);
2345 }
2347 return r;
2348}
2349
2350template <typename MF>
2351void
2353{
2354 auto const& offset = linop.getSolvabilityOffset(0, 0, rhs[0]);
2355 if (verbose >= 4) {
2356 for (int c = 0; c < ncomp; ++c) {
2357 amrex::Print() << print_ident << "MLMG: Subtracting " << offset[c] << " from rhs component "
2358 << c << "\n";
2359 }
2360 }
2361 for (int alev = 0; alev < namrlevs; ++alev) {
2362 linop.fixSolvabilityByOffset(alev, 0, rhs[alev], offset);
2363 }
2364}
2365
2366template <typename MF>
2367void
2368MLMGT<MF>::makeSolvable (int amrlev, int mglev, MF& mf)
2369{
2370 auto const& offset = linop.getSolvabilityOffset(amrlev, mglev, mf);
2371 if (verbose >= 4) {
2372 for (int c = 0; c < ncomp; ++c) {
2373 amrex::Print() << print_ident << "MLMG: Subtracting " << offset[c]
2374 << " from mf component c = " << c
2375 << " on level (" << amrlev << ", " << mglev << ")\n";
2376 }
2377 }
2378 linop.fixSolvabilityByOffset(amrlev, mglev, mf, offset);
2379}
2380
2381#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
2382template <typename MF>
2383template <class TMF>
2384requires (std::same_as<TMF,MultiFab>)
2385void
2386MLMGT<MF>::bottomSolveWithHypre (MF& x, const MF& b)
2387{
2388 const int amrlev = 0;
2389 const int mglev = linop.NMGLevels(amrlev) - 1;
2390
2391 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ncomp == 1, "bottomSolveWithHypre doesn't work with ncomp > 1");
2392
2393 if (linop.isCellCentered())
2394 {
2395 if (hypre_solver == nullptr) // We should reuse the setup
2396 {
2397 hypre_solver = linop.makeHypre(hypre_interface);
2398
2399 hypre_solver->setVerbose(bottom_verbose);
2400 if (hypre_interface == amrex::Hypre::Interface::ij) {
2401 hypre_solver->setHypreOptionsNamespace(hypre_options_namespace);
2402 } else {
2403 hypre_solver->setHypreOldDefault(hypre_old_default);
2404 hypre_solver->setHypreRelaxType(hypre_relax_type);
2405 hypre_solver->setHypreRelaxOrder(hypre_relax_order);
2406 hypre_solver->setHypreNumSweeps(hypre_num_sweeps);
2407 hypre_solver->setHypreStrongThreshold(hypre_strong_threshold);
2408 }
2409
2410 const BoxArray& ba = linop.m_grids[amrlev].back();
2411 const DistributionMapping& dm = linop.m_dmap[amrlev].back();
2412 const Geometry& geom = linop.m_geom[amrlev].back();
2413
2414 hypre_bndry = std::make_unique<MLMGBndryT<MF>>(ba, dm, ncomp, geom);
2415 hypre_bndry->setHomogValues();
2416 const Real* dx = linop.m_geom[0][0].CellSize();
2417 IntVect crse_ratio = linop.m_coarse_data_crse_ratio.allGT(0) ? linop.m_coarse_data_crse_ratio : IntVect(1);
2418 RealVect bclocation(AMREX_D_DECL(0.5*dx[0]*crse_ratio[0],
2419 0.5*dx[1]*crse_ratio[1],
2420 0.5*dx[2]*crse_ratio[2]));
2421 hypre_bndry->setLOBndryConds(linop.m_lobc, linop.m_hibc, IntVect(-1), bclocation,
2422 linop.m_coarse_fine_bc_type);
2423 }
2424
2425 // IJ interface understands absolute tolerance API of HYPRE
2426 amrex::Real hypre_abstol =
2427 (hypre_interface == amrex::Hypre::Interface::ij)
2428 ? bottom_abstol : Real(-1.0);
2429 hypre_solver->solve(
2430 x, b, bottom_reltol, hypre_abstol, bottom_maxiter, *hypre_bndry,
2431 linop.getMaxOrder());
2432 }
2433 else
2434 {
2435 if (hypre_node_solver == nullptr)
2436 {
2437 hypre_node_solver =
2438 linop.makeHypreNodeLap(bottom_verbose, hypre_options_namespace);
2439 }
2440 hypre_node_solver->solve(x, b, bottom_reltol, bottom_abstol, bottom_maxiter);
2441 }
2442
2443 // For singular problems there may be a large constant added to all values of the solution
2444 // For precision reasons we enforce that the average of the correction from HYPRE is 0
2445 if (linop.isSingular(amrlev) && linop.getEnforceSingularSolvable())
2446 {
2447 makeSolvable(amrlev, mglev, x);
2448 }
2449}
2450#endif
2451
2452#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
2453template <typename MF>
2454template <class TMF>
2455requires (std::same_as<TMF,MultiFab>)
2456void
2457MLMGT<MF>::bottomSolveWithPETSc (MF& x, const MF& b)
2458{
2459 AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ncomp == 1, "bottomSolveWithPETSc doesn't work with ncomp > 1");
2460
2461 if(petsc_solver == nullptr)
2462 {
2463 petsc_solver = linop.makePETSc();
2464 petsc_solver->setVerbose(bottom_verbose);
2465
2466 const BoxArray& ba = linop.m_grids[0].back();
2467 const DistributionMapping& dm = linop.m_dmap[0].back();
2468 const Geometry& geom = linop.m_geom[0].back();
2469
2470 petsc_bndry = std::make_unique<MLMGBndryT<MF>>(ba, dm, ncomp, geom);
2471 petsc_bndry->setHomogValues();
2472 const Real* dx = linop.m_geom[0][0].CellSize();
2473 auto crse_ratio = linop.m_coarse_data_crse_ratio.allGT(0) ? linop.m_coarse_data_crse_ratio : IntVect(1);
2474 RealVect bclocation(AMREX_D_DECL(0.5*dx[0]*crse_ratio[0],
2475 0.5*dx[1]*crse_ratio[1],
2476 0.5*dx[2]*crse_ratio[2]));
2477 petsc_bndry->setLOBndryConds(linop.m_lobc, linop.m_hibc, IntVect(-1), bclocation,
2478 linop.m_coarse_fine_bc_type);
2479 }
2480 petsc_solver->solve(x, b, bottom_reltol, Real(-1.), bottom_maxiter, *petsc_bndry,
2481 linop.getMaxOrder());
2482}
2483#endif
2484
2485template <typename MF>
2486void
2487MLMGT<MF>::checkPoint (const Vector<MultiFab*>& a_sol,
2488 const Vector<MultiFab const*>& a_rhs,
2489 RT a_tol_rel, RT a_tol_abs, const char* a_file_name) const
2490{
2491 std::string file_name(a_file_name);
2492 UtilCreateCleanDirectory(file_name, false);
2493
2495 {
2496 std::string HeaderFileName(std::string(a_file_name)+"/Header");
2497 std::ofstream HeaderFile;
2498 HeaderFile.open(HeaderFileName.c_str(), std::ofstream::out |
2499 std::ofstream::trunc |
2500 std::ofstream::binary);
2501 if( ! HeaderFile.good()) {
2502 FileOpenFailed(HeaderFileName);
2503 }
2504
2505 HeaderFile.precision(17);
2506
2507 std::string norm_name = getEnumNameString(norm_type);
2508
2509 HeaderFile << linop.name() << "\n"
2510 << "a_tol_rel = " << a_tol_rel << "\n"
2511 << "a_tol_abs = " << a_tol_abs << "\n"
2512 << "verbose = " << verbose << "\n"
2513 << "max_iters = " << max_iters << "\n"
2514 << "nu1 = " << nu1 << "\n"
2515 << "nu2 = " << nu2 << "\n"
2516 << "nuf = " << nuf << "\n"
2517 << "nub = " << nub << "\n"
2518 << "max_fmg_iters = " << max_fmg_iters << "\n"
2519 << "bottom_solver = " << static_cast<int>(bottom_solver) << "\n"
2520 << "bottom_verbose = " << bottom_verbose << "\n"
2521 << "bottom_maxiter = " << bottom_maxiter << "\n"
2522 << "bottom_reltol = " << bottom_reltol << "\n"
2523 << "convergence_norm = " << norm_name << "\n"
2524 << "namrlevs = " << namrlevs << "\n"
2525 << "finest_amr_lev = " << finest_amr_lev << "\n"
2526 << "linop_prepared = " << linop_prepared << "\n"
2527 << "solve_called = " << solve_called << "\n";
2528
2529 for (int ilev = 0; ilev <= finest_amr_lev; ++ilev) {
2530 UtilCreateCleanDirectory(file_name+"/Level_"+std::to_string(ilev), false);
2531 }
2532 }
2533
2535
2536 for (int ilev = 0; ilev <= finest_amr_lev; ++ilev) {
2537 VisMF::Write(*a_sol[ilev], file_name+"/Level_"+std::to_string(ilev)+"/sol");
2538 VisMF::Write(*a_rhs[ilev], file_name+"/Level_"+std::to_string(ilev)+"/rhs");
2539 }
2540
2541 linop.checkPoint(file_name+"/linop");
2542}
2543
2544template <typename MF>
2545void
2547{
2548 print_ident.resize(print_ident.size()+4, ' ');
2549}
2550
2551template <typename MF>
2552void
2554{
2555 if (print_ident.size() > 4) {
2556 print_ident.resize(print_ident.size()-4, ' ');
2557 } else {
2558 print_ident.clear();
2559 }
2560}
2561
2562extern template class MLMGT<MultiFab>;
2563
2566
2567}
2568
2569#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:1131
#define AMREX_D_DECL(a, b, c)
Definition AMReX_SPACE.H:171
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:37
__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:867
__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
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
void setPrintIdentation(std::string s)
Prefix printed messages (e.g., to indent per level).
Definition AMReX_MLCGSolver.H:90
int solve(MF &solnL, const MF &rhsL, RT eps_rel, RT eps_abs)
Solve Lp(solnL)=rhsL to the requested tolerance.
Definition AMReX_MLCGSolver.H:176
Type
Definition AMReX_MLCGSolver.H:27
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
Abstract base class for multilevel linear operators used by MLMG and the bottom solvers.
Definition AMReX_MLLinOp.H:137
typename FabDataType< MF >::fab_type FAB
Definition AMReX_MLLinOp.H:147
typename FabDataType< MF >::value_type RT
Definition AMReX_MLLinOp.H:148
Definition AMReX_MLMG.H:31
Definition AMReX_MLMG.H:26
void prepareForFluxes(Vector< MF const * > const &a_sol)
Build boundary caches needed by getFluxes()/compFluxes.
Definition AMReX_MLMG.H:1047
void setBottomVerbose(int v) noexcept
Verbosity for the bottom solver (0 silent).
Definition AMReX_MLMG.H:339
void setMaxFmgIter(int n) noexcept
Cap the number of FMG cycles executed.
Definition AMReX_MLMG.H:280
RT MLResNormInf(int alevmax, bool local=false)
Composite infinity norm of the residual up to level alevmax.
Definition AMReX_MLMG.H:2323
void postCG(int ret, int niters=-1)
Post CG smoothing.
Definition AMReX_MLMG.H:1980
RT MLRhsNormInf(bool local=false)
Composite infinity norm of the RHS.
Definition AMReX_MLMG.H:2338
void setNoGpuSync(bool do_not_sync) noexcept
Control implicit GPU synchronization inside solve().
Definition AMReX_MLMG.H:412
MLMGT(MLMGT< MF > &&)=delete
void actualBottomSolve()
Execute the actual bottom solve after pre-smoothing and restriction.
Definition AMReX_MLMG.H:1996
MF MFType
Definition AMReX_MLMG.H:39
BottomSolver getBottomSolver() const noexcept
Definition AMReX_MLMG.H:327
void setPreSmooth(int n) noexcept
Number of pre-smoothing passes per V-cycle.
Definition AMReX_MLMG.H:301
void setBottomToleranceAbs(RT t) noexcept
Absolute tolerance for the bottom solver.
Definition AMReX_MLMG.H:357
RT getFinalResidual() const noexcept
Definition AMReX_MLMG.H:649
void interpCorrection(int alev)
Interpolate corrections onto AMR level alev.
Definition AMReX_MLMG.H:2218
void getEBFluxes(const Vector< MF * > &a_eb_flux)
Flux into the EB wall using the internally stored solution.
Definition AMReX_MLMG.H:1268
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:1058
void setBottomSmooth(int n) noexcept
Additional smoothing passes executed after the bottom solver.
Definition AMReX_MLMG.H:319
void setNSolve(int flag) noexcept
Enable (flag!=0) or disable the N-solve path.
Definition AMReX_MLMG.H:389
int getBottomVerbose() const
Definition AMReX_MLMG.H:248
void computeResOfCorrection(int amrlev, int mglev)
Compute the residual of the correction equation on (amrlev,mglev).
Definition AMReX_MLMG.H:2302
void applyPrecond(const Vector< MF * > &out, const Vector< MF * > &in)
Apply the linear operator as a preconditioner (out = L(in)).
Definition AMReX_MLMG.H:1439
void setCFStrategy(CFStrategy a_cf_strategy) noexcept
Select the coarse-fine synchronization strategy.
Definition AMReX_MLMG.H:333
void computeResWithCrseCorFineCor(int falev)
Residual update using coarse correction / fine correction.
Definition AMReX_MLMG.H:2194
void NSolve(MLMGT< MF > &a_solver, MF &a_sol, MF &a_rhs)
Perform an NSolve using an MLMGT wrapper.
Definition AMReX_MLMG.H:1956
typename MLLinOpT< MF >::Location Location
Definition AMReX_MLMG.H:44
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:1362
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:1089
void setNSolveGridSize(int s) noexcept
Set the tile size used for N-solve builds.
Definition AMReX_MLMG.H:395
void setVerbose(int v) noexcept
Set the main solver verbosity (0 silent).
Definition AMReX_MLMG.H:268
void computeMLResidual(int amrlevmax)
Compute the composite residual norm up to AMR level amrlevmax.
Definition AMReX_MLMG.H:2133
RT getInitResidual() const noexcept
Definition AMReX_MLMG.H:647
int getNumIters() const noexcept
Definition AMReX_MLMG.H:652
void setPostSmooth(int n) noexcept
Number of post-smoothing passes per V-cycle.
Definition AMReX_MLMG.H:307
void mgVcycle(int amrlev, int mglev)
Run a multigrid V-cycle on (amrlev,mglev).
Definition AMReX_MLMG.H:1784
void prepareForNSolve()
Prepare the NSolve path.
Definition AMReX_MLMG.H:1671
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:1029
void makeSolvable()
Adjust RHS/solution to satisfy null-space constraints.
Definition AMReX_MLMG.H:2352
void setBottomSolver(BottomSolver s) noexcept
Select the bottom solver type (e.g., CG, BiCGStab, Hypre, PETSc).
Definition AMReX_MLMG.H:326
void preparePrecond()
Prepare preconditioner-specific caches (e.g., boundary data).
Definition AMReX_MLMG.H:1663
void incPrintIdentation()
Increase the indentation used when printing solver logs.
Definition AMReX_MLMG.H:2546
typename MLLinOpT< MF >::RT RT
Definition AMReX_MLMG.H:41
void setThrowException(bool t) noexcept
Control behavior when the solve fails to converge or blows up.
Definition AMReX_MLMG.H:262
void decPrintIdentation()
Decrease the indentation used when printing solver logs.
Definition AMReX_MLMG.H:2553
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:287
Vector< RT > const & getResidualHistory() const noexcept
Definition AMReX_MLMG.H:651
void prepareLinOp()
Finalize operator-dependent metadata before iterating.
Definition AMReX_MLMG.H:1640
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:294
CFStrategy
Definition AMReX_MLMG.H:47
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:1451
int bottomSolveWithCG(MF &x, const MF &b, typename MLCGSolverT< MF >::Type type)
Bottom solve using CG/BiCGStab implemented in MLCGSolverT.
Definition AMReX_MLMG.H:2107
void setAlwaysUseBNorm(int flag) noexcept
Deprecated flag for forcing B-norm convergence checks.
Definition AMReX_MLMG.H:783
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:1294
void miniCycle(int amrlev)
Execute a per-level mini cycle.
Definition AMReX_MLMG.H:1773
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:380
typename MLLinOpT< MF >::BCMode BCMode
Definition AMReX_MLMG.H:43
void setConvergenceNormType(MLMGNormType norm) noexcept
Choose the norm used for convergence tests.
Definition AMReX_MLMG.H:373
void computeResWithCrseSolFineCor(int calev, int falev)
Residual update using coarse solution / fine correction.
Definition AMReX_MLMG.H:2161
MLMGT< MF > & operator=(MLMGT< MF > const &)=delete
MLMGT(MLLinOpT< MF > &a_lp)
Definition AMReX_MLMG.H:774
void computeResidual(int alev)
Compute the residual on AMR level alev.
Definition AMReX_MLMG.H:2151
MLLinOpT< MF > & getLinOp()
Definition AMReX_MLMG.H:655
typename MLLinOpT< MF >::FAB FAB
Definition AMReX_MLMG.H:40
RT getBottomToleranceAbs() const noexcept
Definition AMReX_MLMG.H:358
int numAMRLevels() const noexcept
Definition AMReX_MLMG.H:382
MLMGT(MLMGT< MF > const &)=delete
void mgFcycle()
Run an FMG cycle starting from the coarsest grid.
Definition AMReX_MLMG.H:1890
RT getInitRHS() const noexcept
Definition AMReX_MLMG.H:645
RT ResNormInf(int alev, bool local=false)
Infinity norm of the residual on level alev.
Definition AMReX_MLMG.H:2314
Vector< int > const & getNumCGIters() const noexcept
Definition AMReX_MLMG.H:653
void bottomSolve()
Execute the configured bottom solver (Hypre, PETSc, CG, etc.).
Definition AMReX_MLMG.H:1942
void setBottomTolerance(RT t) noexcept
Relative tolerance for the bottom solver.
Definition AMReX_MLMG.H:351
void setFinalSmooth(int n) noexcept
Number of smoothing passes when MLMG is used standalone (final smooth).
Definition AMReX_MLMG.H:313
void addInterpCorrection(int alev, int mglev)
Add interpolated corrections to (alev,mglev) data.
Definition AMReX_MLMG.H:2271
int getVerbose() const
Definition AMReX_MLMG.H:247
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:345
void oneIter(int iter)
Execute a single multigrid iteration (FMG or V-cycle).
Definition AMReX_MLMG.H:1704
void setMaxIter(int n) noexcept
Cap the number of MLMG iterations executed.
Definition AMReX_MLMG.H:274
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:825
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
void push(MPI_Comm c)
Definition AMReX_ParallelContext.H:105
void BarrierSub() noexcept
Definition AMReX_ParallelContext.H:88
MPI_Comm CommunicatorSub() noexcept
sub-communicator for current frame
Definition AMReX_ParallelContext.H:70
int MyProcSub() noexcept
my sub-rank in current frame
Definition AMReX_ParallelContext.H:76
void pop()
Note that it's the user's responsibility to free the MPI_Comm.
Definition AMReX_ParallelContext.H:111
bool IOProcessorSub() noexcept
Am IO processor for current frame?
Definition AMReX_ParallelContext.H:80
int verbose
Definition AMReX.cpp:112
Definition AMReX_Amr.cpp:50
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:259
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:2870
DistributionMapping const & DistributionMap(FabArrayBase const &fa)
Convenience wrapper that forwards to fa.DistributionMap().
Definition AMReX_FabArrayBase.cpp:2875
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:2860
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:331
BottomSolver
Definition AMReX_MLLinOp.H:40
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:2865
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:242
MLMGNormType
Definition AMReX_MLMG.H:21
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:117
Location
Definition AMReX_MLLinOp.H:119
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