Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_MLLinOp.H
Go to the documentation of this file.
1#ifndef AMREX_ML_LINOP_H_
2#define AMREX_ML_LINOP_H_
3#include <AMReX_Config.H>
4
5#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
6#include <AMReX_Hypre.H>
8#endif
9
10#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
11#include <AMReX_PETSc.H>
12#endif
13
14#ifdef AMREX_USE_EB
16#include <AMReX_MultiCutFab.H>
17#endif
18
19#include <AMReX_Any.H>
20#include <AMReX_BndryRegister.H>
21#include <AMReX_FabDataType.H>
22#include <AMReX_MLMGBndry.H>
23#include <AMReX_MultiFab.H>
24#include <AMReX_MultiFabUtil.H>
25
26#include <algorithm>
27#include <iterator>
28#include <string>
29
30namespace amrex {
31
43
50struct LPInfo
51{
52 bool do_agglomeration = true;
53 bool do_consolidation = true;
54 bool do_semicoarsening = false;
55 int agg_grid_size = -1;
56 int con_grid_size = -1;
57 int con_ratio = 2;
58 int con_strategy = 3;
59 bool has_metric_term = true;
64 bool deterministic = false;
65
67 LPInfo& setAgglomeration (bool x) noexcept { do_agglomeration = x; return *this; }
69 LPInfo& setConsolidation (bool x) noexcept { do_consolidation = x; return *this; }
71 LPInfo& setSemicoarsening (bool x) noexcept { do_semicoarsening = x; return *this; }
73 LPInfo& setAgglomerationGridSize (int x) noexcept { agg_grid_size = x; return *this; }
75 LPInfo& setConsolidationGridSize (int x) noexcept { con_grid_size = x; return *this; }
77 LPInfo& setConsolidationRatio (int x) noexcept { con_ratio = x; return *this; }
79 LPInfo& setConsolidationStrategy (int x) noexcept { con_strategy = x; return *this; }
81 LPInfo& setMetricTerm (bool x) noexcept { has_metric_term = x; return *this; }
83 LPInfo& setMaxCoarseningLevel (int n) noexcept { max_coarsening_level = n; return *this; }
85 LPInfo& setMaxSemicoarseningLevel (int n) noexcept { max_semicoarsening_level = n; return *this; }
87 LPInfo& setSemicoarseningDirection (int n) noexcept { semicoarsening_direction = n; return *this; }
89 LPInfo& setHiddenDirection (int n) noexcept { hidden_direction = n; return *this; }
91 LPInfo& setDeterministic (bool x) noexcept { deterministic = x; return *this; }
92
94 [[nodiscard]] bool hasHiddenDimension () const noexcept {
95 return hidden_direction >=0 && hidden_direction < AMREX_SPACEDIM;
96 }
97
98 static constexpr int getDefaultAgglomerationGridSize () {
99#ifdef AMREX_USE_GPU
100 return 32;
101#else
102 return AMREX_D_PICK(32, 16, 8);
103#endif
104 }
105
106 static constexpr int getDefaultConsolidationGridSize () {
107#ifdef AMREX_USE_GPU
108 return 32;
109#else
110 return AMREX_D_PICK(32, 16, 8);
111#endif
112 }
113};
114
121
122template <typename T> class MLMGT;
123template <typename T> class MLCGSolverT;
124template <typename T> class MLPoissonT;
125template <typename T> class MLABecLaplacianT;
126template <typename T> class GMRESMLMGT;
127
129
135template <typename MF>
137{
138public:
139
140 template <typename T> friend class MLMGT;
141 template <typename T> friend class MLCGSolverT;
142 template <typename T> friend class MLPoissonT;
143 template <typename T> friend class MLABecLaplacianT;
144 template <typename T> friend class GMRESMLMGT;
145
146 using MFType = MF;
149
154
155 MLLinOpT () = default;
156 virtual ~MLLinOpT () = default;
157
158 MLLinOpT (const MLLinOpT<MF>&) = delete;
159 MLLinOpT (MLLinOpT<MF>&&) = delete;
162
174 void define (const Vector<Geometry>& a_geom,
175 const Vector<BoxArray>& a_grids,
176 const Vector<DistributionMapping>& a_dmap,
177 const LPInfo& a_info,
178 const Vector<FabFactory<FAB> const*>& a_factory,
179 bool eb_limit_coarsening = true);
180
181 [[nodiscard]] virtual std::string name () const { return std::string("Unspecified"); }
182
194 const Array<BCType,AMREX_SPACEDIM>& hibc) noexcept;
195
207
219 const Array<Real,AMREX_SPACEDIM>& hi_bcloc) noexcept;
220
228 [[nodiscard]] bool needsCoarseDataForBC () const noexcept { return m_needs_coarse_data_for_bc; }
229
251 void setCoarseFineBC (const MF* crse, int crse_ratio,
252 LinOpBCType bc_type = LinOpBCType::Dirichlet) noexcept;
253
254 void setCoarseFineBC (const MF* crse, IntVect const& crse_ratio,
255 LinOpBCType bc_type = LinOpBCType::Dirichlet) noexcept;
256
257 template <typename AMF>
258 requires (!std::same_as<MF,AMF>)
259 void setCoarseFineBC (const AMF* crse, int crse_ratio,
260 LinOpBCType bc_type = LinOpBCType::Dirichlet) noexcept;
261
262 template <typename AMF>
263 requires (!std::same_as<MF,AMF>)
264 void setCoarseFineBC (const AMF* crse, IntVect const& crse_ratio,
265 LinOpBCType bc_type = LinOpBCType::Dirichlet) noexcept;
266
267
286 virtual void setLevelBC (int /*amrlev*/, const MF* /*levelbcdata*/,
287 const MF* /*robinbc_a*/ = nullptr,
288 const MF* /*robinbc_b*/ = nullptr,
289 const MF* /*robinbc_f*/ = nullptr) = 0;
290
291 template <MultiFabLike AMF>
292 requires (!std::same_as<MF,AMF>)
293 void setLevelBC (int amrlev, const AMF* levelbcdata,
294 const AMF* robinbc_a = nullptr,
295 const AMF* robinbc_b = nullptr,
296 const AMF* robinbc_f = nullptr);
297
303 void setVerbose (int v) noexcept { verbose = v; }
304
310 void setMaxOrder (int o) noexcept { maxorder = o; }
312 [[nodiscard]] int getMaxOrder () const noexcept { return maxorder; }
313
322 [[nodiscard]] bool getEnforceSingularSolvable () const noexcept { return enforceSingularSolvable; }
323
324 [[nodiscard]] virtual BottomSolver getDefaultBottomSolver () const { return BottomSolver::bicgstab; }
325
327 [[nodiscard]] virtual bool supportCustomBottomSolver () const { return false; }
328
340 virtual void customBottomSolve (MLMGT<MF>* mlmg, MF& x, const MF& b,
341 RT eps_rel, RT eps_abs, int maxiter)
342 {
343 amrex::ignore_unused(mlmg, x, b, eps_rel, eps_abs, maxiter);
344 amrex::Abort("customBottomSolve not implemented");
345 }
346
348 [[nodiscard]] virtual int getNComp () const { return 1; }
349
350 [[nodiscard]] virtual int getNGrow (int /*a_lev*/ = 0, int /*mg_lev*/ = 0) const { return 0; }
351
353 [[nodiscard]] virtual bool needsUpdate () const { return false; }
355 virtual void update () {}
356
366 virtual void restriction (int amrlev, int cmglev, MF& crse, MF& fine) const = 0;
367
376 virtual void interpolation (int amrlev, int fmglev, MF& fine, const MF& crse) const = 0;
377
386 virtual void interpAssign (int amrlev, int fmglev, MF& fine, MF& crse) const
387 {
388 amrex::ignore_unused(amrlev, fmglev, fine, crse);
389 amrex::Abort("MLLinOpT::interpAssign: Must be implemented for FMG cycle");
390 }
391
400 virtual void interpolationAmr (int famrlev, MF& fine, const MF& crse,
401 IntVect const& nghost) const
402 {
403 amrex::ignore_unused(famrlev, fine, crse, nghost);
404 amrex::Abort("MLLinOpT::interpolationAmr: Must be implemented for composite solves across multiple AMR levels");
405 }
406
416 virtual void averageDownSolutionRHS (int camrlev, MF& crse_sol, MF& crse_rhs,
417 const MF& fine_sol, const MF& fine_rhs)
418 {
419 amrex::ignore_unused(camrlev, crse_sol, crse_rhs, fine_sol, fine_rhs);
420 amrex::Abort("MLLinOpT::averageDownSolutionRHS: Must be implemented for composite solves across multiple AMR levels");
421 }
422
434 virtual void apply (int amrlev, int mglev, MF& out, MF& in, BCMode bc_mode,
435 StateMode s_mode, const MLMGBndryT<MF>* bndry=nullptr) const = 0;
436
447 virtual void smooth (int amrlev, int mglev, MF& sol, const MF& rhs,
448 bool skip_fillboundary, int niter) const = 0;
449
457 virtual void normalize (int amrlev, int mglev, MF& mf) const {
458 amrex::ignore_unused(amrlev, mglev, mf);
459 }
460
470 virtual void solutionResidual (int amrlev, MF& resid, MF& x, const MF& b,
471 const MF* crse_bcdata=nullptr) = 0;
472
479 virtual void prepareForFluxes (int amrlev, const MF* crse_bcdata = nullptr) {
480 amrex::ignore_unused(amrlev, crse_bcdata);
481 }
482
494 virtual void correctionResidual (int amrlev, int mglev, MF& resid, MF& x, const MF& b,
495 BCMode bc_mode, const MF* crse_bcdata=nullptr) = 0;
496
508 virtual void reflux (int crse_amrlev,
509 MF& res, const MF& crse_sol, const MF& crse_rhs,
510 MF& fine_res, MF& fine_sol, const MF& fine_rhs) const
511 {
512 amrex::ignore_unused(crse_amrlev, res, crse_sol, crse_rhs, fine_res,
513 fine_sol, fine_rhs);
514 amrex::Abort("MLLinOpT::reflux: Must be implemented for composite solves across multiple AMR levels");
515 }
516
525 virtual void compFlux (int amrlev, const Array<MF*,AMREX_SPACEDIM>& fluxes,
526 MF& sol, Location loc) const
527 {
528 amrex::ignore_unused(amrlev, fluxes, sol, loc);
529 amrex::Abort("AMReX_MLLinOp::compFlux::How did we get here?");
530 }
531
540 virtual void compGrad (int amrlev, const Array<MF*,AMREX_SPACEDIM>& grad,
541 MF& sol, Location loc) const
542 {
543 amrex::ignore_unused(amrlev, grad, sol, loc);
544 amrex::Abort("AMReX_MLLinOp::compGrad::How did we get here?");
545 }
546
554 virtual void applyMetricTerm (int amrlev, int mglev, MF& rhs) const {
555 amrex::ignore_unused(amrlev, mglev, rhs);
556 }
564 virtual void unapplyMetricTerm (int amrlev, int mglev, MF& rhs) const {
565 amrex::ignore_unused(amrlev, mglev, rhs);
566 }
567
574 virtual void unimposeNeumannBC (int amrlev, MF& rhs) const {
575 amrex::ignore_unused(amrlev, rhs);
576 }
577
584 virtual void applyInhomogNeumannTerm (int amrlev, MF& rhs) const {
585 amrex::ignore_unused(amrlev, rhs);
586 }
587
594 virtual void applyOverset (int amrlev, MF& rhs) const {
595 amrex::ignore_unused(amrlev, rhs);
596 }
597
605 [[nodiscard]] virtual bool scaleRHS (int amrlev, MF* rhs) const {
606 amrex::ignore_unused(amrlev, rhs);
607 return false;
608 }
609
618 virtual Vector<RT> getSolvabilityOffset (int amrlev, int mglev,
619 MF const& rhs) const {
620 amrex::ignore_unused(amrlev, mglev, rhs);
621 return {};
622 }
623
632 virtual void fixSolvabilityByOffset (int amrlev, int mglev, MF& rhs,
633 Vector<RT> const& offset) const {
634 amrex::ignore_unused(amrlev, mglev, rhs, offset);
635 }
636
640 virtual void prepareForSolve () = 0;
641
646 virtual void preparePrecond () {}
647
656 virtual void setDirichletNodesToZero (int amrlev, int mglev,
657 MF& mf) const
658 {
659 amrex::ignore_unused(amrlev, mglev, mf);
660 amrex::Warning("This function might need to be implemented for GMRES to work with this LinOp.");
661 }
662
664 [[nodiscard]] virtual bool isSingular (int amrlev) const = 0;
666 [[nodiscard]] virtual bool isBottomSingular () const = 0;
667
677 virtual RT xdoty (int amrlev, int mglev, const MF& x, const MF& y, bool local) const = 0;
678
688
696 virtual RT norm2Precond (Vector<MF const*> const& x) const;
697
703 virtual std::unique_ptr<MLLinOpT<MF>> makeNLinOp (int grid_size) const
704 {
705 amrex::ignore_unused(grid_size);
706 amrex::Abort("MLLinOp::makeNLinOp: NSolve not supported");
707 return nullptr;
708 }
709
717 virtual void getFluxes (const Vector<Array<MF*,AMREX_SPACEDIM> >& a_flux,
718 const Vector<MF*>& a_sol,
719 Location a_loc) const {
720 amrex::ignore_unused(a_flux, a_sol, a_loc);
721 amrex::Abort("MLLinOp::getFluxes: How did we get here?");
722 }
729 virtual void getFluxes (const Vector<MF*>& a_flux,
730 const Vector<MF*>& a_sol) const {
731 amrex::ignore_unused(a_flux, a_sol);
732 amrex::Abort("MLLinOp::getFluxes: How did we get here?");
733 }
734
735#ifdef AMREX_USE_EB
742 virtual void getEBFluxes (const Vector<MF*>& a_flux,
743 const Vector<MF*>& a_sol) const {
744 amrex::ignore_unused(a_flux, a_sol);
745 amrex::Abort("MLLinOp::getEBFluxes: How did we get here?");
746 }
747#endif
748
749#if defined(AMREX_USE_HYPRE) && (AMREX_SPACEDIM > 1)
755 [[nodiscard]] virtual std::unique_ptr<Hypre> makeHypre (Hypre::Interface hypre_interface) const {
756 amrex::ignore_unused(hypre_interface);
757 amrex::Abort("MLLinOp::makeHypre: How did we get here?");
758 return {nullptr};
759 }
766 [[nodiscard]] virtual std::unique_ptr<HypreNodeLap> makeHypreNodeLap(
767 int bottom_verbose,
768 const std::string& options_namespace) const
769 {
770 amrex::ignore_unused(bottom_verbose, options_namespace);
771 amrex::Abort("MLLinOp::makeHypreNodeLap: How did we get here?");
772 return {nullptr};
773 }
774#endif
775
776#if defined(AMREX_USE_PETSC) && (AMREX_SPACEDIM > 1)
780 [[nodiscard]] virtual std::unique_ptr<PETScABecLap> makePETSc () const {
781 amrex::Abort("MLLinOp::makePETSc: How did we get here?");
782 return {nullptr};
783 }
784#endif
785
789 [[nodiscard]] virtual bool supportNSolve () const { return false; }
790
797 virtual void copyNSolveSolution (MF& dst, MF const& src) const {
798 amrex::ignore_unused(dst, src);
799 }
800
806 virtual void postSolve (Vector<MF*> const& sol) const {
808 }
809
817 [[nodiscard]] virtual RT normInf (int amrlev, MF const& mf, bool local) const = 0;
818
824 virtual void averageDownAndSync (Vector<MF>& sol) const = 0;
825
826 virtual void avgDownResAmr (int clev, MF& cres, MF const& fres) const
827 {
828 amrex::ignore_unused(clev, cres, fres);
829 amrex::Abort("MLLinOpT::avgDownResAmr: Must be implemented for composite solves across multiple AMR levels");
830 }
831
839 virtual void avgDownResMG (int clev, MF& cres, MF const& fres) const;
840
844 virtual void beginPrecondBC () { m_precond_mode = true; }
848 virtual void endPrecondBC () { m_precond_mode = false; }
849
857 [[nodiscard]] bool isMFIterSafe (int amrlev, int mglev1, int mglev2) const;
858
860 [[nodiscard]] int NAMRLevels () const noexcept { return m_num_amr_levels; }
861
863 [[nodiscard]] int NMGLevels (int amrlev) const noexcept { return m_num_mg_levels[amrlev]; }
864
866 [[nodiscard]] const Geometry& Geom (int amr_lev, int mglev=0) const noexcept { return m_geom[amr_lev][mglev]; }
867
868 // BC
871 // Need to save the original copy because we change the BC type to
872 // Neumann for inhomogeneous Neumann and Robin.
875
876protected:
877
878 static constexpr int mg_coarsen_ratio = 2;
879 static constexpr int mg_box_min_width = 2;
881
883
884 int verbose = 0;
885
886 int maxorder = 3;
887
889
892
894 const MLLinOpT<MF>* m_parent = nullptr;
895
897
898 bool m_do_agglomeration = false;
899 bool m_do_consolidation = false;
900
903
910
914 struct CommContainer {
915 MPI_Comm comm;
916 CommContainer (MPI_Comm m) noexcept : comm(m) {}
917 CommContainer (const CommContainer&) = delete;
918 CommContainer (CommContainer&&) = delete;
919 void operator= (const CommContainer&) = delete;
920 void operator= (CommContainer&&) = delete;
921 ~CommContainer () { // NOLINT(modernize-use-equals-default)
922#ifdef BL_USE_MPI
923 if (comm != MPI_COMM_NULL) { MPI_Comm_free(&comm); }
924#endif
925 }
926 };
928 std::unique_ptr<CommContainer> m_raii_comm;
929
932
937 const MF* m_coarse_data_for_bc = nullptr;
939
940 bool m_precond_mode = false;
941
943 [[nodiscard]] const Vector<int>& AMRRefRatio () const noexcept { return m_amr_ref_ratio; }
944
946 [[nodiscard]] int AMRRefRatio (int amr_lev) const noexcept { return m_amr_ref_ratio[amr_lev]; }
947
949 [[nodiscard]] IntVect AMRRefRatioVect (int amr_lev) const noexcept {
950 IntVect rr(m_amr_ref_ratio[amr_lev]);
951 if (info.hasHiddenDimension()) { rr[info.hidden_direction] = 1; }
952 return rr;
953 }
954
955 [[nodiscard]] FabFactory<FAB> const* Factory (int amr_lev, int mglev=0) const noexcept {
956 return m_factory[amr_lev][mglev].get();
957 }
958
959 [[nodiscard]] GpuArray<BCType,AMREX_SPACEDIM> LoBC (int icomp = 0) const noexcept {
961 m_lobc[icomp][1],
962 m_lobc[icomp][2])}};
963 }
964 [[nodiscard]] GpuArray<BCType,AMREX_SPACEDIM> HiBC (int icomp = 0) const noexcept {
966 m_hibc[icomp][1],
967 m_hibc[icomp][2])}};
968 }
969
970 [[nodiscard]] bool hasBC (BCType bct) const noexcept;
971 [[nodiscard]] bool hasInhomogNeumannBC () const noexcept;
972 [[nodiscard]] bool hasRobinBC () const noexcept;
973
974 [[nodiscard]] virtual bool supportRobinBC () const noexcept { return false; }
975 [[nodiscard]] virtual bool supportInhomogNeumannBC () const noexcept { return false; }
976
977#ifdef BL_USE_MPI
978 [[nodiscard]] bool isBottomActive () const noexcept { return m_bottom_comm != MPI_COMM_NULL; }
979#else
980 [[nodiscard]] bool isBottomActive () const noexcept { return true; }
981#endif
982 [[nodiscard]] MPI_Comm BottomCommunicator () const noexcept { return m_bottom_comm; }
983 [[nodiscard]] MPI_Comm Communicator () const noexcept { return m_default_comm; }
984
985 void setCoarseFineBCLocation (const RealVect& cloc) noexcept { m_coarse_bc_loc = cloc; }
986
987 [[nodiscard]] bool doAgglomeration () const noexcept { return m_do_agglomeration; }
988 [[nodiscard]] bool doConsolidation () const noexcept { return m_do_consolidation; }
989 [[nodiscard]] bool doSemicoarsening () const noexcept { return m_do_semicoarsening; }
990
991 [[nodiscard]] bool isCellCentered () const noexcept { return m_ixtype == 0; }
992
993 [[nodiscard]] virtual IntVect getNGrowVectRestriction () const {
994 return isCellCentered() ? IntVect(0) : IntVect(1);
995 }
996
997 virtual void make (Vector<Vector<MF> >& mf, IntVect const& ng) const;
998
999 [[nodiscard]] virtual MF make (int amrlev, int mglev, IntVect const& ng) const;
1000
1002 [[nodiscard]] virtual MF make (int amrlev, int mglev, IntVect const& ng,
1003 MFInfo const& mf_info) const;
1004
1005 [[nodiscard]] virtual MF makeAlias (MF const& mf) const;
1006
1008 [[nodiscard]] virtual MF makeCoarseMG (int amrlev, int mglev, IntVect const& ng) const;
1009
1011 [[nodiscard]] virtual MF makeCoarseMG (int amrlev, int mglev, IntVect const& ng,
1012 MFInfo const& mf_info) const;
1013
1015 [[nodiscard]] virtual MF makeCoarseAmr (int famrlev, IntVect const& ng) const;
1016
1018 [[nodiscard]] virtual MF makeCoarseAmr (int famrlev, IntVect const& ng,
1019 MFInfo const& mf_info) const;
1020
1021 [[nodiscard]] virtual std::unique_ptr<FabFactory<FAB> > makeFactory (int /*amrlev*/, int /*mglev*/) const {
1022 return std::make_unique<DefaultFabFactory<FAB>>();
1023 }
1024
1025 virtual void resizeMultiGrid (int new_size);
1026
1027 [[nodiscard]] bool hasHiddenDimension () const noexcept { return info.hasHiddenDimension(); }
1028 [[nodiscard]] int hiddenDirection () const noexcept { return info.hidden_direction; }
1029 [[nodiscard]] Box compactify (Box const& b) const noexcept;
1030
1031 template <typename T>
1032 [[nodiscard]] Array4<T> compactify (Array4<T> const& a) const noexcept
1033 {
1034 if (info.hidden_direction == 0) {
1035 return Array4<T>(a.dataPtr(), {a.begin[1],a.begin[2],0}, {a.end[1],a.end[2],1}, a.nComp());
1036 } else if (info.hidden_direction == 1) {
1037 return Array4<T>(a.dataPtr(), {a.begin[0],a.begin[2],0}, {a.end[0],a.end[2],1}, a.nComp());
1038 } else if (info.hidden_direction == 2) {
1039 return Array4<T>(a.dataPtr(), {a.begin[0],a.begin[1],0}, {a.end[0],a.end[1],1}, a.nComp());
1040 } else {
1041 return a;
1042 }
1043 }
1044
1045 template <typename T>
1046 [[nodiscard]] T get_d0 (T const& dx, T const& dy, T const&) const noexcept
1047 {
1048 if (info.hidden_direction == 0) {
1049 return dy;
1050 } else {
1051 return dx;
1052 }
1053 }
1054
1055 template <typename T>
1056 [[nodiscard]] T get_d1 (T const&, T const& dy, T const& dz) const noexcept
1057 {
1058 if (info.hidden_direction == 0 || info.hidden_direction == 1) {
1059 return dz;
1060 } else {
1061 return dy;
1062 }
1063 }
1064
1065private:
1066
1067 void defineGrids (const Vector<Geometry>& a_geom,
1068 const Vector<BoxArray>& a_grids,
1069 const Vector<DistributionMapping>& a_dmap,
1070 const Vector<FabFactory<FAB> const*>& a_factory);
1071 void defineBC ();
1072 static void makeAgglomeratedDMap (const Vector<BoxArray>& ba, Vector<DistributionMapping>& dm);
1073 static void makeConsolidatedDMap (const Vector<BoxArray>& ba, Vector<DistributionMapping>& dm,
1074 int ratio, int strategy);
1075 [[nodiscard]] MPI_Comm makeSubCommunicator (const DistributionMapping& dm);
1076
1077 virtual void checkPoint (std::string const& /*file_name*/) const {
1078 amrex::Abort("MLLinOp:checkPoint: not implemented");
1079 }
1080
1081 Vector<std::unique_ptr<MF>> levelbc_raii;
1082 Vector<std::unique_ptr<MF>> robin_a_raii;
1083 Vector<std::unique_ptr<MF>> robin_b_raii;
1084 Vector<std::unique_ptr<MF>> robin_f_raii;
1085};
1086
1087template <typename MF>
1088void
1090 const Vector<BoxArray>& a_grids,
1091 const Vector<DistributionMapping>& a_dmap,
1092 const LPInfo& a_info,
1093 const Vector<FabFactory<FAB> const*>& a_factory,
1094 [[maybe_unused]] bool eb_limit_coarsening)
1095{
1096 BL_PROFILE("MLLinOp::define()");
1097
1098 info = a_info;
1099#ifdef AMREX_USE_GPU
1101 {
1102 if (info.agg_grid_size <= 0) { info.agg_grid_size = AMREX_D_PICK(32, 16, 8); }
1103 if (info.con_grid_size <= 0) { info.con_grid_size = AMREX_D_PICK(32, 16, 8); }
1104 }
1105 else
1106#endif
1107 {
1108 if (info.agg_grid_size <= 0) { info.agg_grid_size = LPInfo::getDefaultAgglomerationGridSize(); }
1109 if (info.con_grid_size <= 0) { info.con_grid_size = LPInfo::getDefaultConsolidationGridSize(); }
1110 }
1111
1112#ifdef AMREX_USE_EB
1113 if (!a_factory.empty() && eb_limit_coarsening) {
1114 const auto *f = dynamic_cast<EBFArrayBoxFactory const*>(a_factory[0]);
1115 if (f) {
1116 info.max_coarsening_level = std::min(info.max_coarsening_level,
1117 f->maxCoarseningLevel());
1118 }
1119 }
1120#endif
1121 defineGrids(a_geom, a_grids, a_dmap, a_factory);
1122 defineBC();
1123}
1124
1125template <typename MF>
1126void
1128 const Vector<BoxArray>& a_grids,
1129 const Vector<DistributionMapping>& a_dmap,
1130 const Vector<FabFactory<FAB> const*>& a_factory)
1131{
1132 BL_PROFILE("MLLinOp::defineGrids()");
1133
1134#ifdef AMREX_USE_EB
1135 if ( ! a_factory.empty() ) {
1136 auto const* ebf = dynamic_cast<EBFArrayBoxFactory const*>(a_factory[0]);
1137 if (ebf && !(ebf->isAllRegular())) { // Has non-trivial EB
1138 mg_domain_min_width = 4;
1139 }
1140 }
1141#endif
1142
1143 m_num_amr_levels = 0;
1144 for (int amrlev = 0; amrlev < std::ssize(a_geom); amrlev++) {
1145 if (!a_grids[amrlev].empty()) {
1146 m_num_amr_levels++;
1147 }
1148 }
1149
1150 m_amr_ref_ratio.resize(m_num_amr_levels);
1151 m_num_mg_levels.resize(m_num_amr_levels);
1152
1153 m_geom.clear();
1154 m_grids.clear();
1155 m_dmap.clear();
1156 m_factory.clear();
1157 m_domain_covered.clear();
1158 mg_coarsen_ratio_vec.clear();
1159
1160 m_geom.resize(m_num_amr_levels);
1161 m_grids.resize(m_num_amr_levels);
1162 m_dmap.resize(m_num_amr_levels);
1163 m_factory.resize(m_num_amr_levels);
1164
1165 m_default_comm = ParallelContext::CommunicatorSub();
1166
1167 const RealBox& rb = a_geom[0].ProbDomain();
1168 const int coord = a_geom[0].Coord();
1169 const Array<int,AMREX_SPACEDIM>& is_per = a_geom[0].isPeriodic();
1170
1171 IntVect mg_coarsen_ratio_v(mg_coarsen_ratio);
1172 IntVect mg_box_min_width_v(mg_box_min_width);
1173 IntVect mg_domain_min_width_v(mg_domain_min_width);
1174 if (hasHiddenDimension()) {
1175 AMREX_ASSERT_WITH_MESSAGE(AMREX_SPACEDIM == 3,
1176 "Hidden direction only supported for 3d");
1177 mg_coarsen_ratio_v[info.hidden_direction] = 1;
1178 mg_box_min_width_v[info.hidden_direction] = 0;
1179 mg_domain_min_width_v[info.hidden_direction] = 0;
1180 }
1181
1182 // fine amr levels
1183 for (int amrlev = m_num_amr_levels-1; amrlev > 0; --amrlev)
1184 {
1185 m_num_mg_levels[amrlev] = 1;
1186 m_geom[amrlev].push_back(a_geom[amrlev]);
1187 m_grids[amrlev].push_back(a_grids[amrlev]);
1188 m_dmap[amrlev].push_back(a_dmap[amrlev]);
1189 if (amrlev < std::ssize(a_factory)) {
1190 m_factory[amrlev].emplace_back(a_factory[amrlev]->clone());
1191 } else {
1192 m_factory[amrlev].push_back(std::make_unique<DefaultFabFactory<FAB>>());
1193 }
1194
1195 IntVect rr = mg_coarsen_ratio_v;
1196 const Box& dom = a_geom[amrlev].Domain();
1197 for (int i = 0; i < 2; ++i)
1198 {
1199 if (!dom.coarsenable(rr)) { amrex::Abort("MLLinOp: Uncoarsenable domain"); }
1200
1201 const Box& cdom = amrex::coarsen(dom,rr);
1202 if (cdom == a_geom[amrlev-1].Domain()) { break; }
1203
1204 ++(m_num_mg_levels[amrlev]);
1205
1206 m_geom[amrlev].emplace_back(cdom, rb, coord, is_per);
1207
1208 m_grids[amrlev].push_back(a_grids[amrlev]);
1209 AMREX_ASSERT(m_grids[amrlev].back().coarsenable(rr));
1210 m_grids[amrlev].back().coarsen(rr);
1211
1212 m_dmap[amrlev].push_back(a_dmap[amrlev]);
1213
1214 rr *= mg_coarsen_ratio_v;
1215 }
1216
1217#if (AMREX_SPACEDIM > 1)
1218 if (hasHiddenDimension()) {
1219 m_amr_ref_ratio[amrlev-1] = rr[(info.hidden_direction+1) % AMREX_SPACEDIM];
1220 } else
1221#endif
1222 {
1223 m_amr_ref_ratio[amrlev-1] = rr[0];
1224 }
1225 }
1226
1227 // coarsest amr level
1228 m_num_mg_levels[0] = 1;
1229 m_geom[0].push_back(a_geom[0]);
1230 m_grids[0].push_back(a_grids[0]);
1231 m_dmap[0].push_back(a_dmap[0]);
1232 if (!a_factory.empty()) {
1233 m_factory[0].emplace_back(a_factory[0]->clone());
1234 } else {
1235 m_factory[0].push_back(std::make_unique<DefaultFabFactory<FAB>>());
1236 }
1237
1238 m_domain_covered.resize(m_num_amr_levels, false);
1239 auto npts0 = m_grids[0][0].numPts();
1240 m_domain_covered[0] = (npts0 == compactify(m_geom[0][0].Domain()).numPts());
1241 for (int amrlev = 1; amrlev < m_num_amr_levels; ++amrlev)
1242 {
1243 if (!m_domain_covered[amrlev-1]) { break; }
1244 m_domain_covered[amrlev] = (m_grids[amrlev][0].numPts() ==
1245 compactify(m_geom[amrlev][0].Domain()).numPts());
1246 }
1247
1248 Box aggbox;
1249 bool aggable = false;
1250
1251 if (m_grids[0][0].size() > 1 && info.do_agglomeration)
1252 {
1253 if (m_domain_covered[0])
1254 {
1255 aggbox = m_geom[0][0].Domain();
1256 if (hasHiddenDimension()) {
1257 aggbox.makeSlab(hiddenDirection(), m_grids[0][0][0].smallEnd(hiddenDirection()));
1258 }
1259 aggable = true;
1260 }
1261 else
1262 {
1263 aggbox = m_grids[0][0].minimalBox();
1264 aggable = (aggbox.numPts() == npts0);
1265 }
1266 }
1267
1268 bool agged = false;
1269 bool coned = false;
1270 int agg_lev = 0, con_lev = 0;
1271
1272 AMREX_ALWAYS_ASSERT( ! (info.do_semicoarsening && info.hasHiddenDimension())
1273 && info.semicoarsening_direction >= -1
1274 && info.semicoarsening_direction < AMREX_SPACEDIM );
1275
1276 if (info.do_agglomeration && aggable)
1277 {
1278 Box dbx = m_geom[0][0].Domain();
1279 Box bbx = aggbox;
1280 Real const nbxs = static_cast<Real>(m_grids[0][0].size());
1281 Long const threshold_npts = AMREX_D_TERM(Long(info.agg_grid_size),
1282 *info.agg_grid_size,
1283 *info.agg_grid_size);
1284 Vector<Box> domainboxes{dbx};
1285 Vector<Box> boundboxes{bbx};
1286 Vector<int> agg_flag{false};
1287 Vector<IntVect> accum_coarsen_ratio{IntVect(1)};
1288 int numsclevs = 0;
1289
1290 for (int lev = 0; lev < info.max_coarsening_level; ++lev)
1291 {
1292 IntVect rr_level = mg_coarsen_ratio_v;
1293 bool const do_semicoarsening_level = info.do_semicoarsening
1294 && numsclevs < info.max_semicoarsening_level;
1295 if (do_semicoarsening_level
1296 && info.semicoarsening_direction != -1)
1297 {
1298 rr_level[info.semicoarsening_direction] = 1;
1299 }
1300 IntVect is_coarsenable;
1301 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1302 IntVect rr_dir(1);
1303 rr_dir[idim] = rr_level[idim];
1304 is_coarsenable[idim] = dbx.coarsenable(rr_dir, mg_domain_min_width_v)
1305 && bbx.coarsenable(rr_dir, mg_box_min_width_v);
1306 if (!is_coarsenable[idim] && do_semicoarsening_level
1307 && info.semicoarsening_direction == -1)
1308 {
1309 is_coarsenable[idim] = true;
1310 rr_level[idim] = 1;
1311 }
1312 }
1313 if (is_coarsenable != IntVect(1) || rr_level == IntVect(1)) {
1314 break;
1315 }
1316 if (do_semicoarsening_level && info.semicoarsening_direction == -1) {
1317 // make sure there is at most one direction that is not coarsened
1318 int n_ones = AMREX_D_TERM( static_cast<int>(rr_level[0] == 1),
1319 + static_cast<int>(rr_level[1] == 1),
1320 + static_cast<int>(rr_level[2] == 1));
1321 if (n_ones > 1) { break; }
1322 }
1323 if (rr_level != mg_coarsen_ratio_v) {
1324 ++numsclevs;
1325 }
1326
1327 accum_coarsen_ratio.push_back(accum_coarsen_ratio.back()*rr_level);
1328 domainboxes.push_back(dbx.coarsen(rr_level));
1329 boundboxes.push_back(bbx.coarsen(rr_level));
1330 bool to_agg = (bbx.d_numPts() / nbxs)
1331 < Real(0.999)*static_cast<Real>(threshold_npts);
1332 agg_flag.push_back(to_agg);
1333 }
1334
1335 for (int lev = 1, nlevs = static_cast<int>(domainboxes.size()); lev < nlevs; ++lev) {
1336 if (!agged && !agg_flag[lev] &&
1337 a_grids[0].coarsenable(accum_coarsen_ratio[lev], mg_box_min_width_v))
1338 {
1339 m_grids[0].push_back(amrex::coarsen(a_grids[0], accum_coarsen_ratio[lev]));
1340 m_dmap[0].push_back(a_dmap[0]);
1341 } else {
1342 IntVect cr = domainboxes[lev-1].length() / domainboxes[lev].length();
1343 if (!m_grids[0].back().coarsenable(cr)) {
1344 break; // average_down would fail if fine boxarray is not coarsenable.
1345 }
1346 m_grids[0].emplace_back(boundboxes[lev]);
1347 Box const cell_box = amrex::enclosedCells(boundboxes[lev]);
1348 if (cell_box.numPts() > threshold_npts) {
1349 IntVect max_grid_size(info.agg_grid_size);
1350 if (info.do_semicoarsening && info.max_semicoarsening_level >= lev
1351 && info.semicoarsening_direction != -1)
1352 {
1353 IntVect blen = cell_box.size();
1354 AMREX_D_TERM(int mgs_0 = (max_grid_size[0]+blen[0]-1) / blen[0];,
1355 int mgs_1 = (max_grid_size[1]+blen[1]-1) / blen[1];,
1356 int mgs_2 = (max_grid_size[2]+blen[2]-1) / blen[2]);
1357 max_grid_size[info.semicoarsening_direction]
1358 *= AMREX_D_TERM(mgs_0, *mgs_1, *mgs_2);
1359 }
1360 m_grids[0].back().maxSize(max_grid_size);
1361 }
1362 m_dmap[0].push_back(DistributionMapping());
1363 if (!agged) {
1364 agged = true;
1365 agg_lev = lev;
1366 }
1367 }
1368 m_geom[0].emplace_back(domainboxes[lev],rb,coord,is_per);
1369 }
1370 }
1371 else
1372 {
1373 Long consolidation_threshold = 0;
1374 Real avg_npts = 0.0;
1375 if (info.do_consolidation) {
1376 avg_npts = static_cast<Real>(a_grids[0].d_numPts()) / static_cast<Real>(ParallelContext::NProcsSub());
1377 consolidation_threshold = AMREX_D_TERM(Long(info.con_grid_size),
1378 *info.con_grid_size,
1379 *info.con_grid_size);
1380 }
1381
1382 Box const& dom0 = a_geom[0].Domain();
1383 IntVect rr_vec(1);
1384 int numsclevs = 0;
1385 for (int lev = 0; lev < info.max_coarsening_level; ++lev)
1386 {
1387 IntVect rr_level = mg_coarsen_ratio_v;
1388 bool do_semicoarsening_level = info.do_semicoarsening
1389 && numsclevs < info.max_semicoarsening_level;
1390 if (do_semicoarsening_level
1391 && info.semicoarsening_direction != -1)
1392 {
1393 rr_level[info.semicoarsening_direction] = 1;
1394 }
1395 IntVect is_coarsenable;
1396 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1397 IntVect rr_dir(1);
1398 rr_dir[idim] = rr_vec[idim] * rr_level[idim];
1399 is_coarsenable[idim] = dom0.coarsenable(rr_dir, mg_domain_min_width_v)
1400 && a_grids[0].coarsenable(rr_dir, mg_box_min_width_v);
1401 if (!is_coarsenable[idim] && do_semicoarsening_level
1402 && info.semicoarsening_direction == -1)
1403 {
1404 is_coarsenable[idim] = true;
1405 rr_level[idim] = 1;
1406 }
1407 }
1408 if (is_coarsenable != IntVect(1) || rr_level == IntVect(1)) {
1409 break;
1410 }
1411 if (do_semicoarsening_level && info.semicoarsening_direction == -1) {
1412 // make sure there is at most one direction that is not coarsened
1413 int n_ones = AMREX_D_TERM( static_cast<int>(rr_level[0] == 1),
1414 + static_cast<int>(rr_level[1] == 1),
1415 + static_cast<int>(rr_level[2] == 1));
1416 if (n_ones > 1) { break; }
1417 }
1418 if (rr_level != mg_coarsen_ratio_v) {
1419 ++numsclevs;
1420 }
1421 rr_vec *= rr_level;
1422
1423 m_geom[0].emplace_back(amrex::coarsen(dom0, rr_vec), rb, coord, is_per);
1424 m_grids[0].push_back(amrex::coarsen(a_grids[0], rr_vec));
1425
1426 if (info.do_consolidation)
1427 {
1428 if (avg_npts/static_cast<Real>(AMREX_D_TERM(rr_vec[0], *rr_vec[1], *rr_vec[2]))
1429 < Real(0.999)*static_cast<Real>(consolidation_threshold))
1430 {
1431 coned = true;
1432 con_lev = m_dmap[0].size();
1433 m_dmap[0].push_back(DistributionMapping());
1434 }
1435 else
1436 {
1437 m_dmap[0].push_back(m_dmap[0].back());
1438 }
1439 }
1440 else
1441 {
1442 m_dmap[0].push_back(a_dmap[0]);
1443 }
1444 }
1445 }
1446
1447 m_num_mg_levels[0] = m_grids[0].size();
1448
1449 for (int mglev = 0; mglev < m_num_mg_levels[0] - 1; mglev++){
1450 const Box& fine_domain = m_geom[0][mglev].Domain();
1451 const Box& crse_domain = m_geom[0][mglev+1].Domain();
1452 mg_coarsen_ratio_vec.push_back(fine_domain.length()/crse_domain.length());
1453 }
1454
1455 for (int amrlev = 0; amrlev < m_num_amr_levels; ++amrlev) {
1456 if (AMRRefRatio(amrlev) == 4 && mg_coarsen_ratio_vec.empty()) {
1457 mg_coarsen_ratio_vec.push_back(IntVect(2));
1458 }
1459 }
1460
1461 if (agged)
1462 {
1463 makeAgglomeratedDMap(m_grids[0], m_dmap[0]);
1464 }
1465 else if (coned)
1466 {
1467 makeConsolidatedDMap(m_grids[0], m_dmap[0], info.con_ratio, info.con_strategy);
1468 }
1469
1470 if (agged || coned)
1471 {
1472 m_bottom_comm = makeSubCommunicator(m_dmap[0].back());
1473 }
1474 else
1475 {
1476 m_bottom_comm = m_default_comm;
1477 }
1478
1479 m_do_agglomeration = agged;
1480 m_do_consolidation = coned;
1481
1482 if (verbose > 1) {
1483 if (agged) {
1484 Print() << "MLLinOp::defineGrids(): agglomerated AMR level 0 starting at MG level "
1485 << agg_lev << " of " << m_num_mg_levels[0] << "\n";
1486 } else if (coned) {
1487 Print() << "MLLinOp::defineGrids(): consolidated AMR level 0 starting at MG level "
1488 << con_lev << " of " << m_num_mg_levels[0]
1489 << " (ratio = " << info.con_ratio << ")" << "\n";
1490 } else {
1491 Print() << "MLLinOp::defineGrids(): no agglomeration or consolidation of AMR level 0\n";
1492 }
1493 }
1494
1495 for (int amrlev = 0; amrlev < m_num_amr_levels; ++amrlev)
1496 {
1497 for (int mglev = 1; mglev < m_num_mg_levels[amrlev]; ++mglev)
1498 {
1499 m_factory[amrlev].emplace_back(makeFactory(amrlev,mglev));
1500 }
1501 }
1502
1503 for (int amrlev = 1; amrlev < m_num_amr_levels; ++amrlev)
1504 {
1505 AMREX_ASSERT_WITH_MESSAGE(m_grids[amrlev][0].coarsenable(AMRRefRatioVect(amrlev-1)),
1506 "MLLinOp: grids not coarsenable between AMR levels");
1507 }
1508}
1509
1510template <typename MF>
1511void
1512MLLinOpT<MF>::defineBC ()
1513{
1514 m_needs_coarse_data_for_bc = !m_domain_covered[0];
1515
1516 levelbc_raii.resize(m_num_amr_levels);
1517 robin_a_raii.resize(m_num_amr_levels);
1518 robin_b_raii.resize(m_num_amr_levels);
1519 robin_f_raii.resize(m_num_amr_levels);
1520}
1521
1522template <typename MF>
1523void
1525 const Array<BCType,AMREX_SPACEDIM>& a_hibc) noexcept
1526{
1527 const int ncomp = getNComp();
1528 setDomainBC(Vector<Array<BCType,AMREX_SPACEDIM> >(ncomp,a_lobc),
1529 Vector<Array<BCType,AMREX_SPACEDIM> >(ncomp,a_hibc));
1530}
1531
1532template <typename MF>
1533void
1535 const Vector<Array<BCType,AMREX_SPACEDIM> >& a_hibc)
1536{
1537 const int ncomp = getNComp();
1538 AMREX_ASSERT_WITH_MESSAGE(ncomp == a_lobc.size() && ncomp == a_hibc.size(),
1539 "MLLinOp::setDomainBC: wrong size");
1540 m_lobc = a_lobc;
1541 m_hibc = a_hibc;
1542 m_lobc_orig = m_lobc;
1543 m_hibc_orig = m_hibc;
1544 for (int icomp = 0; icomp < ncomp; ++icomp) {
1545 for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
1546 if (m_geom[0][0].isPeriodic(idim)) {
1547 AMREX_ALWAYS_ASSERT(m_lobc[icomp][idim] == BCType::Periodic &&
1548 m_hibc[icomp][idim] == BCType::Periodic);
1549 } else {
1550 AMREX_ALWAYS_ASSERT(m_lobc[icomp][idim] != BCType::Periodic &&
1551 m_hibc[icomp][idim] != BCType::Periodic);
1552 }
1553
1554 if (m_lobc[icomp][idim] == LinOpBCType::inhomogNeumann ||
1555 m_lobc[icomp][idim] == LinOpBCType::Robin)
1556 {
1557 m_lobc[icomp][idim] = LinOpBCType::Neumann;
1558 }
1559
1560 if (m_hibc[icomp][idim] == LinOpBCType::inhomogNeumann ||
1561 m_hibc[icomp][idim] == LinOpBCType::Robin)
1562 {
1563 m_hibc[icomp][idim] = LinOpBCType::Neumann;
1564 }
1565 }
1566 }
1567
1568 if (hasHiddenDimension()) {
1569 const int hd = hiddenDirection();
1570 for (int n = 0; n < ncomp; ++n) {
1571 m_lobc[n][hd] = LinOpBCType::Neumann;
1572 m_hibc[n][hd] = LinOpBCType::Neumann;
1573 }
1574 }
1575
1576 if (hasInhomogNeumannBC() && !supportInhomogNeumannBC()) {
1577 amrex::Abort("Inhomogeneous Neumann BC not supported");
1578 }
1579 if (hasRobinBC() && !supportRobinBC()) {
1580 amrex::Abort("Robin BC not supported");
1581 }
1582}
1583
1584template <typename MF>
1585bool
1586MLLinOpT<MF>::hasBC (BCType bct) const noexcept
1587{
1588 int ncomp = m_lobc_orig.size();
1589 for (int n = 0; n < ncomp; ++n) {
1590 for (int idim = 0; idim <AMREX_SPACEDIM; ++idim) {
1591 if (m_lobc_orig[n][idim] == bct || m_hibc_orig[n][idim] == bct) {
1592 return true;
1593 }
1594 }
1595 }
1596 return false;
1597}
1598
1599template <typename MF>
1600bool
1602{
1603 return hasBC(BCType::inhomogNeumann);
1604}
1605
1606template <typename MF>
1607bool
1609{
1610 return hasBC(BCType::Robin);
1611}
1612
1613template <typename MF>
1614Box
1615MLLinOpT<MF>::compactify (Box const& b) const noexcept
1616{
1617#if (AMREX_SPACEDIM == 3)
1618 if (info.hasHiddenDimension()) {
1619 const auto& lo = b.smallEnd();
1620 const auto& hi = b.bigEnd();
1621 if (info.hidden_direction == 0) {
1622 return Box(IntVect(lo[1],lo[2],0), IntVect(hi[1],hi[2],0), b.ixType());
1623 } else if (info.hidden_direction == 1) {
1624 return Box(IntVect(lo[0],lo[2],0), IntVect(hi[0],hi[2],0), b.ixType());
1625 } else {
1626 return Box(IntVect(lo[0],lo[1],0), IntVect(hi[0],hi[1],0), b.ixType());
1627 }
1628 } else
1629#endif
1630 {
1631 return b;
1632 }
1633}
1634
1635template <typename MF>
1636void
1639{
1640 BL_PROFILE("MLLinOp::makeAgglomeratedDMap");
1641
1642 BL_ASSERT(!dm[0].empty());
1643 for (int i = 1, N=static_cast<int>(ba.size()); i < N; ++i)
1644 {
1645 if (dm[i].empty())
1646 {
1647 const std::vector< std::vector<int> >& sfc = DistributionMapping::makeSFC(ba[i]);
1648
1649 const int nprocs = ParallelContext::NProcsSub();
1650 AMREX_ASSERT(std::ssize(sfc) == nprocs);
1651
1652 Vector<int> pmap(ba[i].size());
1653 for (int iproc = 0; iproc < nprocs; ++iproc) {
1654 int grank = ParallelContext::local_to_global_rank(iproc);
1655 for (int ibox : sfc[iproc]) {
1656 pmap[ibox] = grank;
1657 }
1658 }
1659 dm[i].define(std::move(pmap));
1660 }
1661 }
1662}
1663
1664template <typename MF>
1665void
1666MLLinOpT<MF>::makeConsolidatedDMap (const Vector<BoxArray>& ba,
1667 Vector<DistributionMapping>& dm,
1668 int ratio, int strategy)
1669{
1670 BL_PROFILE("MLLinOp::makeConsolidatedDMap()");
1671
1672 int factor = 1;
1673 BL_ASSERT(!dm[0].empty());
1674 for (int i = 1, N=static_cast<int>(ba.size()); i < N; ++i)
1675 {
1676 if (dm[i].empty())
1677 {
1678 factor *= ratio;
1679
1680 const int nprocs = ParallelContext::NProcsSub();
1681 const auto& pmap_fine = dm[i-1].ProcessorMap();
1682 Vector<int> pmap(pmap_fine.size());
1683 ParallelContext::global_to_local_rank(pmap.data(), pmap_fine.data(), static_cast<int>(pmap.size()));
1684 if (strategy == 1) {
1685 for (auto& x: pmap) {
1686 x /= ratio;
1687 }
1688 } else if (strategy == 2) {
1689 int nprocs_con = static_cast<int>(std::ceil(static_cast<Real>(nprocs)
1690 / static_cast<Real>(factor)));
1691 for (auto& x: pmap) {
1692 auto d = std::div(x,nprocs_con);
1693 x = d.rem;
1694 }
1695 } else if (strategy == 3) {
1696 if (factor == ratio) {
1697 const std::vector< std::vector<int> >& sfc = DistributionMapping::makeSFC(ba[i]);
1698 for (int iproc = 0; iproc < nprocs; ++iproc) {
1699 for (int ibox : sfc[iproc]) {
1700 pmap[ibox] = iproc;
1701 }
1702 }
1703 }
1704 for (auto& x: pmap) {
1705 x /= ratio;
1706 }
1707 }
1708
1710 dm[i].define(std::move(pmap));
1711 } else {
1712 Vector<int> pmap_g(pmap.size());
1713 ParallelContext::local_to_global_rank(pmap_g.data(), pmap.data(), static_cast<int>(pmap.size()));
1714 dm[i].define(std::move(pmap_g));
1715 }
1716 }
1717 }
1718}
1719
1720template <typename MF>
1722MLLinOpT<MF>::makeSubCommunicator (const DistributionMapping& dm)
1723{
1724 BL_PROFILE("MLLinOp::makeSubCommunicator()");
1725
1726#ifdef BL_USE_MPI
1727
1728 Vector<int> newgrp_ranks = dm.ProcessorMap();
1729 std::ranges::sort(newgrp_ranks);
1730 auto last = std::unique(newgrp_ranks.begin(), newgrp_ranks.end());
1731 newgrp_ranks.erase(last, newgrp_ranks.end());
1732
1733 MPI_Comm newcomm;
1734 MPI_Group defgrp, newgrp;
1735 MPI_Comm_group(m_default_comm, &defgrp);
1737 MPI_Group_incl(defgrp, static_cast<int>(newgrp_ranks.size()), newgrp_ranks.data(), &newgrp);
1738 } else {
1739 Vector<int> local_newgrp_ranks(newgrp_ranks.size());
1740 ParallelContext::global_to_local_rank(local_newgrp_ranks.data(),
1741 newgrp_ranks.data(), static_cast<int>(newgrp_ranks.size()));
1742 MPI_Group_incl(defgrp, static_cast<int>(local_newgrp_ranks.size()), local_newgrp_ranks.data(), &newgrp);
1743 }
1744
1745 MPI_Comm_create(m_default_comm, newgrp, &newcomm);
1746
1747 m_raii_comm = std::make_unique<CommContainer>(newcomm);
1748
1749 MPI_Group_free(&defgrp);
1750 MPI_Group_free(&newgrp);
1751
1752 return newcomm;
1753#else
1755 return m_default_comm;
1756#endif
1757}
1758
1759template <typename MF>
1760void
1762 const Array<Real,AMREX_SPACEDIM>& hi_bcloc) noexcept
1763{
1764 m_domain_bloc_lo = lo_bcloc;
1765 m_domain_bloc_hi = hi_bcloc;
1766}
1767
1768template <typename MF>
1769void
1770MLLinOpT<MF>::setCoarseFineBC (const MF* crse, int crse_ratio,
1771 LinOpBCType bc_type) noexcept
1772{
1773 setCoarseFineBC(crse, IntVect(crse_ratio), bc_type);
1774}
1775
1776template <typename MF>
1777void
1778MLLinOpT<MF>::setCoarseFineBC (const MF* crse, IntVect const& crse_ratio,
1779 LinOpBCType bc_type) noexcept
1780{
1781 m_coarse_data_for_bc = crse;
1782 m_coarse_data_crse_ratio = crse_ratio;
1783 m_coarse_fine_bc_type = bc_type;
1784}
1785
1786template <typename MF>
1787template <typename AMF>
1788requires (!std::same_as<MF,AMF>)
1789void
1790MLLinOpT<MF>::setCoarseFineBC (const AMF* crse, int crse_ratio,
1791 LinOpBCType bc_type) noexcept
1792{
1793 setCoarseFineBC(crse, IntVect(crse_ratio), bc_type);
1794}
1795
1796template <typename MF>
1797template <typename AMF>
1798requires (!std::same_as<MF,AMF>)
1799void
1800MLLinOpT<MF>::setCoarseFineBC (const AMF* crse, IntVect const& crse_ratio,
1801 LinOpBCType bc_type) noexcept
1802{
1803 if (crse) {
1804 m_coarse_data_for_bc_raii = MF(crse->boxArray(), crse->DistributionMap(),
1805 crse->nComp(), crse->nGrowVect());
1806 m_coarse_data_for_bc_raii.LocalCopy(*crse, 0, 0, crse->nComp(),
1807 crse->nGrowVect());
1808 m_coarse_data_for_bc = &m_coarse_data_for_bc_raii;
1809 } else {
1810 m_coarse_data_for_bc = nullptr;
1811 }
1812 m_coarse_data_crse_ratio = crse_ratio;
1813 m_coarse_fine_bc_type = bc_type;
1814}
1815
1816template <typename MF>
1817void
1819{
1820 mf.clear();
1821 mf.resize(m_num_amr_levels);
1822 for (int alev = 0; alev < m_num_amr_levels; ++alev) {
1823 mf[alev].resize(m_num_mg_levels[alev]);
1824 for (int mlev = 0; mlev < m_num_mg_levels[alev]; ++mlev) {
1825 mf[alev][mlev] = make(alev, mlev, ng);
1826 }
1827 }
1828}
1829
1830template <typename MF>
1831MF
1832MLLinOpT<MF>::make (int amrlev, int mglev, IntVect const& ng) const
1833{
1834 return make(amrlev, mglev, ng, MFInfo());
1835}
1836
1837template <typename MF>
1838MF
1839MLLinOpT<MF>::make (int amrlev, int mglev, IntVect const& ng, MFInfo const& mf_info) const
1840{
1841 if constexpr (IsMultiFabLike_v<MF>) {
1842 return MF(amrex::convert(m_grids[amrlev][mglev], m_ixtype),
1843 m_dmap[amrlev][mglev], getNComp(), ng, mf_info,
1844 *m_factory[amrlev][mglev]);
1845 } else {
1846 amrex::ignore_unused(amrlev, mglev, ng);
1847 amrex::Abort("MLLinOpT::make: how did we get here?");
1848 return {};
1849 }
1850}
1851
1852template <typename MF>
1853MF
1854MLLinOpT<MF>::makeAlias (MF const& mf) const
1855{
1856 if constexpr (IsMultiFabLike_v<MF>) {
1857 return MF(mf, amrex::make_alias, 0, mf.nComp());
1858 } else {
1860 amrex::Abort("MLLinOpT::makeAlias: how did we get here?");
1861 return {};
1862 }
1863}
1864
1865template <typename MF>
1866MF
1867MLLinOpT<MF>::makeCoarseMG (int amrlev, int mglev, IntVect const& ng) const
1868{
1869 return makeCoarseMG(amrlev, mglev, ng, MFInfo());
1870}
1871
1872template <typename MF>
1873MF
1874MLLinOpT<MF>::makeCoarseMG (int amrlev, int mglev, IntVect const& ng,
1875 MFInfo const& mf_info) const
1876{
1877 if constexpr (IsMultiFabLike_v<MF>) {
1878 BoxArray cba = m_grids[amrlev][mglev];
1879 IntVect ratio = (amrlev > 0) ? IntVect(2) : mg_coarsen_ratio_vec[mglev];
1880 if (hasHiddenDimension()) { ratio[hiddenDirection()] = 1; }
1881 cba.coarsen(ratio);
1882 cba.convert(m_ixtype);
1883 return MF(cba, m_dmap[amrlev][mglev], getNComp(), ng, mf_info);
1884 } else {
1885 amrex::ignore_unused(amrlev, mglev, ng);
1886 amrex::Abort("MLLinOpT::makeCoarseMG: how did we get here?");
1887 return {};
1888 }
1889}
1890
1891template <typename MF>
1892MF
1893MLLinOpT<MF>::makeCoarseAmr (int famrlev, IntVect const& ng) const
1894{
1895 return makeCoarseAmr(famrlev, ng, MFInfo());
1896}
1897
1898template <typename MF>
1899MF
1900MLLinOpT<MF>::makeCoarseAmr (int famrlev, IntVect const& ng, MFInfo const& mf_info) const
1901{
1902 if constexpr (IsMultiFabLike_v<MF>) {
1903 BoxArray cba = m_grids[famrlev][0];
1904 IntVect ratio(AMRRefRatioVect(famrlev-1));
1905 cba.coarsen(ratio);
1906 cba.convert(m_ixtype);
1907 return MF(cba, m_dmap[famrlev][0], getNComp(), ng, mf_info);
1908 } else {
1909 amrex::ignore_unused(famrlev, ng);
1910 amrex::Abort("MLLinOpT::makeCoarseAmr: how did we get here?");
1911 return {};
1912 }
1913}
1914
1915template <typename MF>
1916void
1918{
1919 if (new_size <= 0 || new_size >= m_num_mg_levels[0]) { return; }
1920
1921 m_num_mg_levels[0] = new_size;
1922
1923 m_geom[0].resize(new_size);
1924 m_grids[0].resize(new_size);
1925 m_dmap[0].resize(new_size);
1926 m_factory[0].resize(new_size);
1927
1928 if (m_bottom_comm != m_default_comm) {
1929 m_bottom_comm = makeSubCommunicator(m_dmap[0].back());
1930 }
1931}
1932
1933template <typename MF>
1934void
1935MLLinOpT<MF>::avgDownResMG (int clev, MF& cres, MF const& fres) const
1936{
1937 amrex::ignore_unused(clev, cres, fres);
1938 if constexpr (amrex::IsFabArray<MF>::value) {
1939 const int ncomp = this->getNComp();
1940#ifdef AMREX_USE_EB
1941 if (!fres.isAllRegular()) {
1942 if constexpr (std::is_same<MF,MultiFab>()) {
1943 amrex::EB_average_down(fres, cres, 0, ncomp,
1944 mg_coarsen_ratio_vec[clev-1]);
1945 } else {
1946 amrex::Abort("EB_average_down only works with MultiFab");
1947 }
1948 } else
1949#endif
1950 {
1951 amrex::average_down(fres, cres, 0, ncomp, mg_coarsen_ratio_vec[clev-1]);
1952 }
1953 } else {
1954 amrex::Abort("For non-FabArray, MLLinOpT<MF>::avgDownResMG should be overridden.");
1955 }
1956}
1957
1958template <typename MF>
1959bool
1960MLLinOpT<MF>::isMFIterSafe (int amrlev, int mglev1, int mglev2) const
1961{
1962 return m_dmap[amrlev][mglev1] == m_dmap[amrlev][mglev2]
1963 && BoxArray::SameRefs(m_grids[amrlev][mglev1], m_grids[amrlev][mglev2]);
1964}
1965
1966template <typename MF>
1967template <MultiFabLike AMF>
1968requires (!std::same_as<MF,AMF>)
1969void
1970MLLinOpT<MF>::setLevelBC (int amrlev, const AMF* levelbcdata,
1971 const AMF* robinbc_a, const AMF* robinbc_b,
1972 const AMF* robinbc_f)
1973{
1974 const int ncomp = this->getNComp();
1975 if (levelbcdata) {
1976 levelbc_raii[amrlev] = std::make_unique<MF>(levelbcdata->boxArray(),
1977 levelbcdata->DistributionMap(),
1978 ncomp, levelbcdata->nGrowVect());
1979 levelbc_raii[amrlev]->LocalCopy(*levelbcdata, 0, 0, ncomp,
1980 levelbcdata->nGrowVect());
1981 } else {
1982 levelbc_raii[amrlev].reset();
1983 }
1984
1985 if (robinbc_a) {
1986 robin_a_raii[amrlev] = std::make_unique<MF>(robinbc_a->boxArray(),
1987 robinbc_a->DistributionMap(),
1988 ncomp, robinbc_a->nGrowVect());
1989 robin_a_raii[amrlev]->LocalCopy(*robinbc_a, 0, 0, ncomp,
1990 robinbc_a->nGrowVect());
1991 } else {
1992 robin_a_raii[amrlev].reset();
1993 }
1994
1995 if (robinbc_b) {
1996 robin_b_raii[amrlev] = std::make_unique<MF>(robinbc_b->boxArray(),
1997 robinbc_b->DistributionMap(),
1998 ncomp, robinbc_b->nGrowVect());
1999 robin_b_raii[amrlev]->LocalCopy(*robinbc_b, 0, 0, ncomp,
2000 robinbc_b->nGrowVect());
2001 } else {
2002 robin_b_raii[amrlev].reset();
2003 }
2004
2005 if (robinbc_f) {
2006 robin_f_raii[amrlev] = std::make_unique<MF>(robinbc_f->boxArray(),
2007 robinbc_f->DistributionMap(),
2008 ncomp, robinbc_f->nGrowVect());
2009 robin_f_raii[amrlev]->LocalCopy(*robinbc_f, 0, 0, ncomp,
2010 robinbc_f->nGrowVect());
2011 } else {
2012 robin_f_raii[amrlev].reset();
2013 }
2014
2015 this->setLevelBC(amrlev, levelbc_raii[amrlev].get(), robin_a_raii[amrlev].get(),
2016 robin_b_raii[amrlev].get(), robin_f_raii[amrlev].get());
2017}
2018
2019template <typename MF>
2020auto
2022{
2023 AMREX_ALWAYS_ASSERT(NAMRLevels() == 1);
2024 return xdoty(0,0,*x[0],*y[0],false);
2025}
2026
2027template <typename MF>
2028auto
2030{
2031 AMREX_ALWAYS_ASSERT(NAMRLevels() == 1);
2032 auto r = xdoty(0,0,*x[0],*x[0],false);
2033 return std::sqrt(r);
2034}
2035
2036extern template class MLLinOpT<MultiFab>;
2037
2040
2041}
2042
2043#endif
Type-erased container that supports move-only types.
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:562
#define BL_ASSERT(EX)
Definition AMReX_BLassert.H:39
#define AMREX_ASSERT_WITH_MESSAGE(EX, MSG)
Definition AMReX_BLassert.H:37
#define AMREX_ASSERT(EX)
Definition AMReX_BLassert.H:38
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
Infrastructure for storing per-face boundary data in FabSets.
Type trait that exposes the FAB and value types of MultiFab-like containers.
Array4< int const > offset
Definition AMReX_HypreMLABecLap.cpp:1131
Array4< Real > fine
Definition AMReX_InterpFaceRegister.cpp:90
Array4< Real const > crse
Definition AMReX_InterpFaceRegister.cpp:92
#define AMREX_D_TERM(a, b, c)
Definition AMReX_SPACE.H:172
#define AMREX_D_PICK(a, b, c)
Definition AMReX_SPACE.H:173
#define AMREX_D_DECL(a, b, c)
Definition AMReX_SPACE.H:171
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
BoxArray & coarsen(int refinement_ratio)
Coarsen each Box in the BoxArray by refinement_ratio.
Definition AMReX_BoxArray.cpp:685
BoxArray & convert(IndexType typ)
Set the IndexType of the BoxArray.
Definition AMReX_BoxArray.cpp:829
__host__ __device__ BoxND & makeSlab(int direction, int slab_index) noexcept
Collapse the box to a single slab at coordinate slab_index along direction.
Definition AMReX_Box.H:860
__host__ __device__ const IntVectND< dim > & smallEnd() const &noexcept
Return the inclusive lower bound of the box.
Definition AMReX_Box.H:124
Calculates the distribution of FABs to MPI processes.
Definition AMReX_DistributionMapping.H:51
static DistributionMapping makeSFC(const MultiFab &weight, bool sort=true)
Build an SFC map weighted by the sum of component 0 over each valid box of weight; sort enables load-...
Definition AMReX_DistributionMapping.cpp:1770
Definition AMReX_EBFabFactory.H:32
Abstract factory interface for creating, aliasing, and destroying FAB objects.
Definition AMReX_FabFactory.H:73
Solve using GMRES with multigrid as preconditioner.
Definition AMReX_GMRES_MLMG.H:28
Rectangular problem domain geometry.
Definition AMReX_Geometry.H:85
Interface
HYPRE interface modes supported.
Definition AMReX_Hypre.H:37
__host__ static __device__ constexpr std::size_t size() noexcept
Definition AMReX_IntVect.H:824
__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
Definition AMReX_MLABecLaplacian.H:22
CG-family solvers (BiCGStab or CG) for use as the bottom solver in MLMG.
Definition AMReX_MLCGSolver.H:21
Abstract base class for multilevel linear operators used by MLMG and the bottom solvers.
Definition AMReX_MLLinOp.H:137
virtual void copyNSolveSolution(MF &dst, MF const &src) const
Copy an NSolve solution from src to dst.
Definition AMReX_MLLinOp.H:797
const MF * m_coarse_data_for_bc
Definition AMReX_MLLinOp.H:937
virtual void postSolve(Vector< MF * > const &sol) const
Optional hook invoked after the main solve completes.
Definition AMReX_MLLinOp.H:806
Vector< Vector< std::unique_ptr< FabFactory< FAB > > > > m_factory
Definition AMReX_MLLinOp.H:908
virtual bool scaleRHS(int amrlev, MF *rhs) const
Optionally scale the RHS to fix solvability.
Definition AMReX_MLLinOp.H:605
virtual void avgDownResMG(int clev, MF &cres, MF const &fres) const
Average residuals from fine to coarse MG levels (FMG helper).
Definition AMReX_MLLinOp.H:1935
int NAMRLevels() const noexcept
Return the number of AMR levels.
Definition AMReX_MLLinOp.H:860
bool m_do_consolidation
Definition AMReX_MLLinOp.H:899
bool isCellCentered() const noexcept
Definition AMReX_MLLinOp.H:991
IntVect m_ixtype
Definition AMReX_MLLinOp.H:896
void setVerbose(int v) noexcept
Set verbosity.
Definition AMReX_MLLinOp.H:303
bool isMFIterSafe(int amrlev, int mglev1, int mglev2) const
Check whether mixing MFIter loops for different MG levels is safe.
Definition AMReX_MLLinOp.H:1960
RealVect m_coarse_bc_loc
Definition AMReX_MLLinOp.H:936
virtual bool needsUpdate() const
Does it need update if it's reused?
Definition AMReX_MLLinOp.H:353
virtual void interpolation(int amrlev, int fmglev, MF &fine, const MF &crse) const =0
Add interpolated coarse MG level data to fine MG level data.
virtual void setLevelBC(int, const MF *, const MF *=nullptr, const MF *=nullptr, const MF *=nullptr)=0
Set boundary conditions for given level. For cell-centered solves only.
virtual MF make(int amrlev, int mglev, IntVect const &ng) const
Definition AMReX_MLLinOp.H:1832
virtual void applyOverset(int amrlev, MF &rhs) const
Overset-only hook for zeroing regions covered by masks.
Definition AMReX_MLLinOp.H:594
FabFactory< FAB > const * Factory(int amr_lev, int mglev=0) const noexcept
Definition AMReX_MLLinOp.H:955
void setDomainBC(const Vector< Array< BCType, 3 > > &lobc, const Vector< Array< BCType, 3 > > &hibc)
Boundary of the whole domain.
Definition AMReX_MLLinOp.H:1534
Array< Real, 3 > m_domain_bloc_hi
Definition AMReX_MLLinOp.H:931
T get_d0(T const &dx, T const &dy, T const &) const noexcept
Definition AMReX_MLLinOp.H:1046
MPI_Comm BottomCommunicator() const noexcept
Definition AMReX_MLLinOp.H:982
void setEnforceSingularSolvable(bool o) noexcept
Control whether the solver should try to make singular problems solvable.
Definition AMReX_MLLinOp.H:319
MPI_Comm Communicator() const noexcept
Definition AMReX_MLLinOp.H:983
int mg_domain_min_width
Definition AMReX_MLLinOp.H:880
void setMaxOrder(int o) noexcept
Set order of interpolation at coarse/fine boundary.
Definition AMReX_MLLinOp.H:310
virtual void compGrad(int amrlev, const Array< MF *, 3 > &grad, MF &sol, Location loc) const
Compute gradients of the solution.
Definition AMReX_MLLinOp.H:540
virtual void interpAssign(int amrlev, int fmglev, MF &fine, MF &crse) const
Overwrite fine MG level data with interpolated coarse data.
Definition AMReX_MLLinOp.H:386
virtual std::string name() const
Definition AMReX_MLLinOp.H:181
GpuArray< BCType, 3 > LoBC(int icomp=0) const noexcept
Definition AMReX_MLLinOp.H:959
virtual void getEBFluxes(const Vector< MF * > &a_flux, const Vector< MF * > &a_sol) const
Extract embedded-boundary fluxes.
Definition AMReX_MLLinOp.H:742
bool doAgglomeration() const noexcept
Definition AMReX_MLLinOp.H:987
virtual bool supportCustomBottomSolver() const
Does this operator provide its own bottom solver (BottomSolver::custom)?
Definition AMReX_MLLinOp.H:327
virtual MF makeCoarseAmr(int famrlev, IntVect const &ng, MFInfo const &mf_info) const
As above, with caller-selected allocation metadata for temporary storage.
Definition AMReX_MLLinOp.H:1900
MF m_coarse_data_for_bc_raii
Definition AMReX_MLLinOp.H:938
MLLinOpT< MF > & operator=(const MLLinOpT< MF > &)=delete
std::unique_ptr< CommContainer > m_raii_comm
Definition AMReX_MLLinOp.H:928
bool m_do_semicoarsening
Definition AMReX_MLLinOp.H:901
bool hasRobinBC() const noexcept
Definition AMReX_MLLinOp.H:1608
virtual std::unique_ptr< MLLinOpT< MF > > makeNLinOp(int grid_size) const
Create the NSolve counterpart of this operator with the requested grid size.
Definition AMReX_MLLinOp.H:703
Vector< Array< BCType, 3 > > m_hibc
Definition AMReX_MLLinOp.H:870
virtual void resizeMultiGrid(int new_size)
Definition AMReX_MLLinOp.H:1917
Vector< Vector< BoxArray > > m_grids
Definition AMReX_MLLinOp.H:906
virtual MF makeCoarseAmr(int famrlev, IntVect const &ng) const
Allocate an MF on the next coarser AMR level (famrlev-1) with grow cells ng.
Definition AMReX_MLLinOp.H:1893
bool m_do_agglomeration
Definition AMReX_MLLinOp.H:898
virtual MF makeAlias(MF const &mf) const
Definition AMReX_MLLinOp.H:1854
Array4< T > compactify(Array4< T > const &a) const noexcept
Definition AMReX_MLLinOp.H:1032
static constexpr int mg_coarsen_ratio
Definition AMReX_MLLinOp.H:878
virtual void solutionResidual(int amrlev, MF &resid, MF &x, const MF &b, const MF *crse_bcdata=nullptr)=0
Compute residual for solution.
int getMaxOrder() const noexcept
Get order of interpolation at coarse/fine boundary.
Definition AMReX_MLLinOp.H:312
virtual int getNComp() const
Return number of components.
Definition AMReX_MLLinOp.H:348
void setCoarseFineBCLocation(const RealVect &cloc) noexcept
Definition AMReX_MLLinOp.H:985
Vector< int > m_amr_ref_ratio
Definition AMReX_MLLinOp.H:891
MPI_Comm m_default_comm
Definition AMReX_MLLinOp.H:911
virtual void unapplyMetricTerm(int amrlev, int mglev, MF &rhs) const
Remove metric scaling previously applied via applyMetricTerm().
Definition AMReX_MLLinOp.H:564
virtual void setDirichletNodesToZero(int amrlev, int mglev, MF &mf) const
Optional hook for masking out Dirichlet nodes or cells prior to GMRES solves; the default is a no-op ...
Definition AMReX_MLLinOp.H:656
bool isBottomActive() const noexcept
Definition AMReX_MLLinOp.H:980
virtual void applyInhomogNeumannTerm(int amrlev, MF &rhs) const
Add extra terms introduced when treating inhomogeneous Neumann BC as homogeneous.
Definition AMReX_MLLinOp.H:584
virtual BottomSolver getDefaultBottomSolver() const
Definition AMReX_MLLinOp.H:324
virtual void prepareForFluxes(int amrlev, const MF *crse_bcdata=nullptr)
Ensure BC caches are populated before flux extraction.
Definition AMReX_MLLinOp.H:479
typename FabDataType< MF >::fab_type FAB
Definition AMReX_MLLinOp.H:147
virtual RT normInf(int amrlev, MF const &mf, bool local) const =0
Infinity norm helper used by residual reductions.
Vector< int > m_num_mg_levels
Definition AMReX_MLLinOp.H:893
bool hasBC(BCType bct) const noexcept
Definition AMReX_MLLinOp.H:1586
Vector< Vector< DistributionMapping > > m_dmap
Definition AMReX_MLLinOp.H:907
int verbose
Definition AMReX_MLLinOp.H:884
virtual MF make(int amrlev, int mglev, IntVect const &ng, MFInfo const &mf_info) const
As above, with caller-selected allocation metadata for temporary storage.
Definition AMReX_MLLinOp.H:1839
IntVect m_coarse_data_crse_ratio
Definition AMReX_MLLinOp.H:935
virtual void correctionResidual(int amrlev, int mglev, MF &resid, MF &x, const MF &b, BCMode bc_mode, const MF *crse_bcdata=nullptr)=0
Compute residual for the residual-correction form, resid = b - L(x)
virtual void customBottomSolve(MLMGT< MF > *mlmg, MF &x, const MF &b, RT eps_rel, RT eps_abs, int maxiter)
Bottom solve provided by the operator itself.
Definition AMReX_MLLinOp.H:340
const Vector< int > & AMRRefRatio() const noexcept
Return AMR refinement ratios.
Definition AMReX_MLLinOp.H:943
MLLinOpT(MLLinOpT< MF > &&)=delete
Vector< Array< BCType, 3 > > m_hibc_orig
Definition AMReX_MLLinOp.H:874
virtual void unimposeNeumannBC(int amrlev, MF &rhs) const
Undo Neumann contributions stored on the RHS.
Definition AMReX_MLLinOp.H:574
void setCoarseFineBC(const MF *crse, int crse_ratio, LinOpBCType bc_type=LinOpBCType::Dirichlet) noexcept
Set coarse/fine boundary conditions. For cell-centered solves only.
Definition AMReX_MLLinOp.H:1770
virtual void apply(int amrlev, int mglev, MF &out, MF &in, BCMode bc_mode, StateMode s_mode, const MLMGBndryT< MF > *bndry=nullptr) const =0
Apply the linear operator, out = L(in)
bool needsCoarseDataForBC() const noexcept
Needs coarse data for bc?
Definition AMReX_MLLinOp.H:228
typename FabDataType< MF >::value_type RT
Definition AMReX_MLLinOp.H:148
virtual void update()
Update for reuse.
Definition AMReX_MLLinOp.H:355
Vector< Array< BCType, 3 > > m_lobc_orig
Definition AMReX_MLLinOp.H:873
bool m_precond_mode
Definition AMReX_MLLinOp.H:940
virtual std::unique_ptr< FabFactory< FAB > > makeFactory(int, int) const
Definition AMReX_MLLinOp.H:1021
bool hasHiddenDimension() const noexcept
Definition AMReX_MLLinOp.H:1027
virtual bool isBottomSingular() const =0
Is the bottom of the multigrid hierarchy singular?
virtual void reflux(int crse_amrlev, MF &res, const MF &crse_sol, const MF &crse_rhs, MF &fine_res, MF &fine_sol, const MF &fine_rhs) const
Reflux at AMR coarse/fine boundary.
Definition AMReX_MLLinOp.H:508
virtual IntVect getNGrowVectRestriction() const
Definition AMReX_MLLinOp.H:993
virtual void compFlux(int amrlev, const Array< MF *, 3 > &fluxes, MF &sol, Location loc) const
Compute fluxes.
Definition AMReX_MLLinOp.H:525
static constexpr int mg_box_min_width
Definition AMReX_MLLinOp.H:879
virtual RT dotProductPrecond(Vector< MF const * > const &x, Vector< MF const * > const &y) const
Dot product over the composite AMR hierarchy, excluding cells covered by finer levels....
Definition AMReX_MLLinOp.H:2021
virtual MF makeCoarseMG(int amrlev, int mglev, IntVect const &ng) const
Allocate an MF on the next coarser MG level (mglev+1) with grow cells ng.
Definition AMReX_MLLinOp.H:1867
const Geometry & Geom(int amr_lev, int mglev=0) const noexcept
Geometry accessor for (amr_lev,mglev).
Definition AMReX_MLLinOp.H:866
virtual void endPrecondBC()
Called when the operator stops being used as a preconditioner.
Definition AMReX_MLLinOp.H:848
int hiddenDirection() const noexcept
Definition AMReX_MLLinOp.H:1028
void setDomainBCLoc(const Array< Real, 3 > &lo_bcloc, const Array< Real, 3 > &hi_bcloc) noexcept
Set location offsets for the physical domain boundaries.
Definition AMReX_MLLinOp.H:1761
Vector< Array< BCType, 3 > > m_lobc
Definition AMReX_MLLinOp.H:869
Vector< int > m_domain_covered
Definition AMReX_MLLinOp.H:909
const MLLinOpT< MF > * m_parent
Definition AMReX_MLLinOp.H:894
bool doSemicoarsening() const noexcept
Definition AMReX_MLLinOp.H:989
virtual bool supportNSolve() const
Whether this operator supports NSolve.
Definition AMReX_MLLinOp.H:789
virtual bool supportRobinBC() const noexcept
Definition AMReX_MLLinOp.H:974
virtual void normalize(int amrlev, int mglev, MF &mf) const
Divide mf by the diagonal component of the operator. Used by the bottom solvers.
Definition AMReX_MLLinOp.H:457
virtual void avgDownResAmr(int clev, MF &cres, MF const &fres) const
Definition AMReX_MLLinOp.H:826
Vector< Vector< Geometry > > m_geom
first Vector is for amr level and second is mg level
Definition AMReX_MLLinOp.H:905
virtual RT norm2Precond(Vector< MF const * > const &x) const
L2 norm over the composite AMR hierarchy, excluding cells covered by finer levels....
Definition AMReX_MLLinOp.H:2029
MLLinOpT(const MLLinOpT< MF > &)=delete
virtual void averageDownAndSync(Vector< MF > &sol) const =0
Average the solution hierarchy down (fine to coarse) and synchronize interfaces.
void setCoarseFineBC(const MF *crse, IntVect const &crse_ratio, LinOpBCType bc_type=LinOpBCType::Dirichlet) noexcept
Definition AMReX_MLLinOp.H:1778
MLLinOpT()=default
virtual void getFluxes(const Vector< MF * > &a_flux, const Vector< MF * > &a_sol) const
Extract fluxes when the operator stores them in single MultiFabs per level.
Definition AMReX_MLLinOp.H:729
Box compactify(Box const &b) const noexcept
Definition AMReX_MLLinOp.H:1615
bool m_needs_coarse_data_for_bc
Definition AMReX_MLLinOp.H:933
virtual void fixSolvabilityByOffset(int amrlev, int mglev, MF &rhs, Vector< RT > const &offset) const
Subtract previously computed offsets from the RHS.
Definition AMReX_MLLinOp.H:632
GpuArray< BCType, 3 > HiBC(int icomp=0) const noexcept
Definition AMReX_MLLinOp.H:964
int maxorder
Definition AMReX_MLLinOp.H:886
void define(const Vector< Geometry > &a_geom, const Vector< BoxArray > &a_grids, const Vector< DistributionMapping > &a_dmap, const LPInfo &a_info, const Vector< FabFactory< FAB > const * > &a_factory, bool eb_limit_coarsening=true)
Initialize the operator hierarchy on a set of AMR levels.
Definition AMReX_MLLinOp.H:1089
Vector< IntVect > mg_coarsen_ratio_vec
Definition AMReX_MLLinOp.H:902
virtual MF makeCoarseMG(int amrlev, int mglev, IntVect const &ng, MFInfo const &mf_info) const
As above, with caller-selected allocation metadata for temporary storage.
Definition AMReX_MLLinOp.H:1874
MF MFType
Definition AMReX_MLLinOp.H:146
virtual void preparePrecond()
Prepare auxiliary data used when the operator acts as a preconditioner.
Definition AMReX_MLLinOp.H:646
virtual ~MLLinOpT()=default
virtual void averageDownSolutionRHS(int camrlev, MF &crse_sol, MF &crse_rhs, const MF &fine_sol, const MF &fine_rhs)
Average-down data from fine AMR level to coarse AMR level.
Definition AMReX_MLLinOp.H:416
LPInfo info
Definition AMReX_MLLinOp.H:882
int NMGLevels(int amrlev) const noexcept
Return the number of MG levels at given AMR level.
Definition AMReX_MLLinOp.H:863
virtual Vector< RT > getSolvabilityOffset(int amrlev, int mglev, MF const &rhs) const
Compute offsets used to enforce solvability (per component).
Definition AMReX_MLLinOp.H:618
T get_d1(T const &, T const &dy, T const &dz) const noexcept
Definition AMReX_MLLinOp.H:1056
bool enforceSingularSolvable
Definition AMReX_MLLinOp.H:888
virtual RT xdoty(int amrlev, int mglev, const MF &x, const MF &y, bool local) const =0
Dot-product helper used by bottom solvers.
virtual void interpolationAmr(int famrlev, MF &fine, const MF &crse, IntVect const &nghost) const
Interpolation between AMR levels.
Definition AMReX_MLLinOp.H:400
bool doConsolidation() const noexcept
Definition AMReX_MLLinOp.H:988
virtual bool supportInhomogNeumannBC() const noexcept
Definition AMReX_MLLinOp.H:975
LinOpBCType m_coarse_fine_bc_type
Definition AMReX_MLLinOp.H:934
Array< Real, 3 > m_domain_bloc_lo
Definition AMReX_MLLinOp.H:930
virtual bool isSingular(int amrlev) const =0
Is it singular on AMR level amrlev?
int AMRRefRatio(int amr_lev) const noexcept
Return AMR refinement ratio at given AMR level.
Definition AMReX_MLLinOp.H:946
MPI_Comm m_bottom_comm
Definition AMReX_MLLinOp.H:912
virtual void restriction(int amrlev, int cmglev, MF &crse, MF &fine) const =0
Restriction onto coarse MG level.
virtual void getFluxes(const Vector< Array< MF *, 3 > > &a_flux, const Vector< MF * > &a_sol, Location a_loc) const
Extract per-direction fluxes for each AMR level.
Definition AMReX_MLLinOp.H:717
virtual void beginPrecondBC()
Called when the operator starts being used as a preconditioner.
Definition AMReX_MLLinOp.H:844
virtual void applyMetricTerm(int amrlev, int mglev, MF &rhs) const
Apply metric scaling to the RHS on (amrlev,mglev).
Definition AMReX_MLLinOp.H:554
bool getEnforceSingularSolvable() const noexcept
Definition AMReX_MLLinOp.H:322
virtual void prepareForSolve()=0
Finalize coefficients, masks, and BC data before iterative solves.
IntVect AMRRefRatioVect(int amr_lev) const noexcept
Return AMR refinement ratio as IntVect (1 in hidden direction)
Definition AMReX_MLLinOp.H:949
virtual void smooth(int amrlev, int mglev, MF &sol, const MF &rhs, bool skip_fillboundary, int niter) const =0
Smooth.
virtual void make(Vector< Vector< MF > > &mf, IntVect const &ng) const
Definition AMReX_MLLinOp.H:1818
bool hasInhomogNeumannBC() const noexcept
Definition AMReX_MLLinOp.H:1601
void setDomainBC(const Array< BCType, 3 > &lobc, const Array< BCType, 3 > &hibc) noexcept
Boundary of the whole domain.
Definition AMReX_MLLinOp.H:1524
virtual int getNGrow(int=0, int=0) const
Definition AMReX_MLLinOp.H:350
int m_num_amr_levels
Definition AMReX_MLLinOp.H:890
Boundary helper for MLMG that manages coarse/fine and physical BC metadata.
Definition AMReX_MLMGBndry.H:20
Definition AMReX_MLMG.H:26
Cell-centered Laplacian operator \nabla^2 \phi.
Definition AMReX_MLPoisson.H:32
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
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
__host__ __device__ BoxND< dim > coarsen(const BoxND< dim > &b, int ref_ratio) noexcept
Return a copy of b coarsened by the isotropic ratio ref_ratio.
Definition AMReX_Box.H:1469
__host__ __device__ BoxND< dim > enclosedCells(const BoxND< dim > &b, int dir) noexcept
Return a BoxND with CELL based coordinates in direction dir that is enclosed by b.
Definition AMReX_Box.H:1664
std::array< T, N > Array
Definition AMReX_Array.H:31
bool notInLaunchRegion() noexcept
Definition AMReX_GpuControl.H:89
MPI_Comm CommunicatorSub() noexcept
sub-communicator for current frame
Definition AMReX_ParallelContext.H:70
int local_to_global_rank(int rank) noexcept
translate between local rank and global rank
Definition AMReX_ParallelContext.H:98
int global_to_local_rank(int rank) noexcept
Definition AMReX_ParallelContext.H:101
int NProcsSub() noexcept
number of ranks in current frame
Definition AMReX_ParallelContext.H:74
MPI_Comm Communicator() noexcept
Definition AMReX_ParallelDescriptor.H:223
int MPI_Comm
Definition AMReX_ccse-mpi.H:51
int MPI_Group
Definition AMReX_ccse-mpi.H:52
static constexpr int MPI_COMM_NULL
Definition AMReX_ccse-mpi.H:59
Definition AMReX_Amr.cpp:50
@ make_alias
Definition AMReX_MakeType.H:7
__host__ __device__ void ignore_unused(const Ts &...)
No-op helper that marks variables as intentionally unused.
Definition AMReX.H:259
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
BoxND< 3 > Box
Box is an alias for amrex::BoxND instantiated with AMREX_SPACEDIM.
Definition AMReX_BaseFwd.H:35
LinOpBCType
Definition AMReX_LO_BCTYPES.H:27
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
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
std::unique_ptr< Hypre > makeHypre(const BoxArray &grids, const DistributionMapping &dmap, const Geometry &geom, MPI_Comm comm_, Hypre::Interface interface, const iMultiFab *overset_mask)
Factory that instantiates the requested HYPRE interface.
Definition AMReX_Hypre.cpp:12
void Warning(const std::string &msg)
Print a warning message to the diagnostic stream and keep running.
Definition AMReX.cpp:248
void Abort(const std::string &msg)
Print a fatal-error message to stderr and abort execution.
Definition AMReX.cpp:242
__host__ __device__ constexpr int get(IntVectND< dim > const &iv) noexcept
Get I'th element of IntVectND<dim>
Definition AMReX_IntVect.H:1338
A multidimensional array accessor.
Definition AMReX_Array4.H:289
Type trait specialized for MultiFab-like types and their containers.
Definition AMReX_FabDataType.H:16
Fixed-size array that can be used on GPU.
Definition AMReX_Array.H:52
Definition AMReX_TypeTraits.H:27
Configuration knobs for multilevel linear operators (grid agglomeration, metrics, etc....
Definition AMReX_MLLinOp.H:51
LPInfo & setConsolidationRatio(int x) noexcept
Set the refinement ratio x between consolidated levels.
Definition AMReX_MLLinOp.H:77
int con_strategy
Definition AMReX_MLLinOp.H:58
bool do_semicoarsening
Definition AMReX_MLLinOp.H:54
bool has_metric_term
Definition AMReX_MLLinOp.H:59
LPInfo & setConsolidationGridSize(int x) noexcept
Override the consolidation grid cutoff x (cells per MPI task) used to trigger grouping.
Definition AMReX_MLLinOp.H:75
int max_semicoarsening_level
Definition AMReX_MLLinOp.H:61
bool hasHiddenDimension() const noexcept
True if a hidden dimension was configured via setHiddenDirection().
Definition AMReX_MLLinOp.H:94
int con_ratio
Definition AMReX_MLLinOp.H:57
bool do_consolidation
Definition AMReX_MLLinOp.H:53
int con_grid_size
Definition AMReX_MLLinOp.H:56
LPInfo & setSemicoarsening(bool x) noexcept
Toggle plane-wise semicoarsening instead of full coarsening (x = true selects semicoarsening).
Definition AMReX_MLLinOp.H:71
LPInfo & setHiddenDirection(int n) noexcept
Specify a dimension n that should be treated as “hidden” (e.g., for thin domains).
Definition AMReX_MLLinOp.H:89
LPInfo & setConsolidation(bool x) noexcept
Enable or disable consolidation (MPI rank grouping) on coarse levels (x toggles the feature).
Definition AMReX_MLLinOp.H:69
LPInfo & setSemicoarseningDirection(int n) noexcept
Lock the direction n used for semicoarsening (-1 restores the default heuristic).
Definition AMReX_MLLinOp.H:87
LPInfo & setMaxSemicoarseningLevel(int n) noexcept
Cap the number of semicoarsening steps (when enabled) via n.
Definition AMReX_MLLinOp.H:85
bool do_agglomeration
Definition AMReX_MLLinOp.H:52
LPInfo & setMetricTerm(bool x) noexcept
Indicate whether metric terms are present so downstream code can skip metric work when absent.
Definition AMReX_MLLinOp.H:81
bool deterministic
Enable deterministic mode for GPU operations.
Definition AMReX_MLLinOp.H:64
static constexpr int getDefaultConsolidationGridSize()
Definition AMReX_MLLinOp.H:106
LPInfo & setConsolidationStrategy(int x) noexcept
Select the heuristic x used when forming consolidated grids.
Definition AMReX_MLLinOp.H:79
int max_coarsening_level
Definition AMReX_MLLinOp.H:60
int agg_grid_size
Definition AMReX_MLLinOp.H:55
static constexpr int getDefaultAgglomerationGridSize()
Definition AMReX_MLLinOp.H:98
int hidden_direction
Definition AMReX_MLLinOp.H:63
LPInfo & setAgglomerationGridSize(int x) noexcept
Override the target grid size x used when agglomerating patches.
Definition AMReX_MLLinOp.H:73
LPInfo & setMaxCoarseningLevel(int n) noexcept
Cap how many coarsening steps (standard or semi-) MLMG may perform by setting n.
Definition AMReX_MLLinOp.H:83
LPInfo & setDeterministic(bool x) noexcept
Enable deterministic reductions even on GPUs (slower but reproducible) by toggling x.
Definition AMReX_MLLinOp.H:91
LPInfo & setAgglomeration(bool x) noexcept
Enable or disable grid agglomeration on the coarsest MLMG levels (x = true enables it).
Definition AMReX_MLLinOp.H:67
int semicoarsening_direction
Definition AMReX_MLLinOp.H:62
Definition AMReX_MLLinOp.H:116
StateMode
Definition AMReX_MLLinOp.H:118
BCMode
Definition AMReX_MLLinOp.H:117
Location
Definition AMReX_MLLinOp.H:119
FabArray memory allocation information.
Definition AMReX_FabArray.H:73