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