ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_SolveWithEBMLMG.cpp File Reference
#include "ERF.H"
#include "ERF_EB.H"
#include "ERF_Utils.H"
#include "ERF_SolverUtils.H"
#include <AMReX_MLMG.H>
#include <AMReX_MLEBABecLap.H>
Include dependency graph for ERF_SolveWithEBMLMG.cpp:

Functions

template<typename T >
void FillZeroAreaFaceFluxes (MultiFab &phi, Array< MultiFab, AMREX_SPACEDIM > &fluxes, const Geometry &geom, EBFArrayBoxFactory const &ebfact, T const &ebfact_u, T const &ebfact_v, T const &ebfact_w)
 
template<typename T >
void solve_with_EB_mlmg (int lev, Vector< MultiFab > &rhs, Vector< MultiFab > &phi, Vector< Array< MultiFab, AMREX_SPACEDIM >> &fluxes, EBFArrayBoxFactory const &ebfact, T const &ebfact_u, T const &ebfact_v, T const &ebfact_w, const Geometry &geom, const Vector< amrex::IntVect > &ref_ratio, Array< std::string, 2 *AMREX_SPACEDIM > domain_bc_type, int mg_verbose, Real reltol, Real abstol)
 
template void solve_with_EB_mlmg (int, Vector< MultiFab > &, Vector< MultiFab > &, Vector< Array< MultiFab, AMREX_SPACEDIM >> &, EBFArrayBoxFactory const &, EBFArrayBoxFactory const &, EBFArrayBoxFactory const &, EBFArrayBoxFactory const &, const Geometry &, const Vector< amrex::IntVect > &, Array< std::string, 2 *AMREX_SPACEDIM >, int, Real, Real)
 
template void solve_with_EB_mlmg (int, Vector< MultiFab > &, Vector< MultiFab > &, Vector< Array< MultiFab, AMREX_SPACEDIM >> &, EBFArrayBoxFactory const &, eb_aux_ const &, eb_aux_ const &, eb_aux_ const &, const Geometry &, const Vector< amrex::IntVect > &, Array< std::string, 2 *AMREX_SPACEDIM >, int, Real, Real)
 

Function Documentation

◆ FillZeroAreaFaceFluxes()

template<typename T >
void FillZeroAreaFaceFluxes ( MultiFab &  phi,
Array< MultiFab, AMREX_SPACEDIM > &  fluxes,
const Geometry &  geom,
EBFArrayBoxFactory const &  ebfact,
T const &  ebfact_u,
T const &  ebfact_v,
T const &  ebfact_w 
)

Compute phi gradients where the area of the face is zero.

Template Parameters
TEB factory or auxiliary EB data type for face-centered grids
Parameters
[in]phiCell-centered solution used to compute gradients
[out]fluxesFace-centered gradient fluxes to fill
[in]geomGeometry used for inverse cell spacing
[in]ebfactCell-centered embedded-boundary factory
[in]ebfact_uEmbedded-boundary data on x-faces
[in]ebfact_vEmbedded-boundary data on y-faces
[in]ebfact_wEmbedded-boundary data on z-faces

Compute phi gradients where the area of the face is zero

At such a face one of the two adjacent cells is covered, so the gradient is extrapolated from the two nearest face gradients on the uncovered side, which reaches three cells away. Give phi three filled ghost cells to get that stencil everywhere; with fewer, faces near the edge of a box fall back to a first-order one-sided gradient, and the result then depends on the grid decomposition.

Template Parameters
TEB factory or auxiliary EB data type for face-centered grids
Parameters
phiCell-centered solution used to compute gradients, with its ghost cells filled
fluxesFace-centered gradient fluxes to fill
geomGeometry used for inverse cell spacing
ebfactCell-centered embedded-boundary factory
ebfact_uEmbedded-boundary data on x-faces
ebfact_vEmbedded-boundary data on y-faces
ebfact_wEmbedded-boundary data on z-faces
32 {
33  BL_PROFILE("ERF::FillZeroAreaFaceFluxes()");
34 
35  const GpuArray<Real, AMREX_SPACEDIM> dxInv = geom.InvCellSizeArray();
36 
37  for (MFIter mfi(phi,TileNoZ()); mfi.isValid(); ++mfi)
38  {
39  const Box& tbx = mfi.tilebox();
40  const Box& xbx = mfi.nodaltilebox(0);
41  const Box& ybx = mfi.nodaltilebox(1);
42  const Box& zbx = mfi.nodaltilebox(2);
43 
44  // EBCellFlagFab const& cflag_fab = (ebfact.get_const_factory())->getMultiEBCellFlagFab()[mfi];
45  EBCellFlagFab const& cflag_fab = (ebfact.getMultiEBCellFlagFab())[mfi];
46  Array4<const EBCellFlag> cflag = cflag_fab.const_array();
47 
48  if (cflag_fab.getType(tbx) == FabType::singlevalued)
49  {
50  Array4<const Real> apx = ebfact.getAreaFrac()[0]->const_array(mfi);
51  Array4<const Real> apy = ebfact.getAreaFrac()[1]->const_array(mfi);
52  Array4<const Real> apz = ebfact.getAreaFrac()[2]->const_array(mfi);
53 
54  Array4<const EBCellFlag> u_cflag = ebfact_u.getMultiEBCellFlagFab()[mfi].const_array();
55  Array4<const EBCellFlag> v_cflag = ebfact_v.getMultiEBCellFlagFab()[mfi].const_array();
56  Array4<const EBCellFlag> w_cflag = ebfact_w.getMultiEBCellFlagFab()[mfi].const_array();
57 
58  Array4<Real const> const& p_arr = phi.const_array(mfi);
59  Array4<Real> const& fx = fluxes[0].array(mfi);
60  Array4<Real> const& fy = fluxes[1].array(mfi);
61  Array4<Real> const& fz = fluxes[2].array(mfi);
62 
63  // The region of phi that we may read: this FAB's own cells, less any that lie
64  // outside the domain in a non-periodic direction, since the FillBoundary done
65  // before this call leaves those unfilled. The three-cell stencils below reach
66  // three cells past the tile at a face on the box edge, so they must be checked
67  // against this rather than assumed to fit (issue #3699)
68  const Box rbx = phi[mfi].box() & geom.growPeriodicDomain(phi.nGrowVect());
69  const auto rlo = lbound(rbx);
70  const auto rhi = ubound(rbx);
71 
72  ParallelFor(xbx, ybx, zbx,
73  // x-face
74  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
75  {
76  if (apx(i,j,k) == zero) {
77  if (!u_cflag(i,j,k).isCovered()) {
78  if (cflag(i,j,k).isCovered() && !cflag(i-1,j,k).isCovered()) {
79  // Extrapolate from the uncovered side, at i-1 and below
80  if (i-3 >= rlo.x) {
81  fx(i,j,k) = dxInv[0] * (p_arr(i-3,j,k) - three*p_arr(i-2,j,k) + two*p_arr(i-1,j,k));
82  } else if (i-2 >= rlo.x) {
83  fx(i,j,k) = dxInv[0] * (p_arr(i-1,j,k) - p_arr(i-2,j,k));
84  }
85  } else if (cflag(i-1,j,k).isCovered() && !cflag(i,j,k).isCovered()) {
86  // Extrapolate from the uncovered side, at i and above
87  if (i+2 <= rhi.x) {
88  fx(i,j,k) = dxInv[0] * (three*p_arr(i+1,j,k) - p_arr(i+2,j,k) - two*p_arr(i,j,k));
89  } else if (i+1 <= rhi.x) {
90  fx(i,j,k) = dxInv[0] * (p_arr(i+1,j,k) - p_arr(i,j,k));
91  }
92  }
93  }
94  }
95  },
96  // y-face
97  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
98  {
99  if (apy(i,j,k) == zero) {
100  if (!v_cflag(i,j,k).isCovered()) {
101  if (cflag(i,j,k).isCovered() && !cflag(i,j-1,k).isCovered()) {
102  // Extrapolate from the uncovered side, at j-1 and below
103  if (j-3 >= rlo.y) {
104  fy(i,j,k) = dxInv[1] * (p_arr(i,j-3,k) - three*p_arr(i,j-2,k) + two*p_arr(i,j-1,k));
105  } else if (j-2 >= rlo.y) {
106  fy(i,j,k) = dxInv[1] * (p_arr(i,j-1,k) - p_arr(i,j-2,k));
107  }
108  } else if (cflag(i,j-1,k).isCovered() && !cflag(i,j,k).isCovered()) {
109  // Extrapolate from the uncovered side, at j and above
110  if (j+2 <= rhi.y) {
111  fy(i,j,k) = dxInv[1] * (three*p_arr(i,j+1,k) - p_arr(i,j+2,k) - two*p_arr(i,j,k));
112  } else if (j+1 <= rhi.y) {
113  fy(i,j,k) = dxInv[1] * (p_arr(i,j+1,k) - p_arr(i,j,k));
114  }
115  }
116  }
117  }
118  },
119  // z-face
120  [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
121  {
122  if (apz(i,j,k) == zero) {
123  if (!w_cflag(i,j,k).isCovered()) {
124  if (cflag(i,j,k).isCovered() && !cflag(i,j,k-1).isCovered()) {
125  // Extrapolate from the uncovered side, at k-1 and below
126  if (k-3 >= rlo.z) {
127  fz(i,j,k) = dxInv[2] * (p_arr(i,j,k-3) - three*p_arr(i,j,k-2) + two*p_arr(i,j,k-1));
128  } else if (k-2 >= rlo.z) {
129  fz(i,j,k) = dxInv[2] * (p_arr(i,j,k-1) - p_arr(i,j,k-2));
130  }
131  } else if (cflag(i,j,k-1).isCovered() && !cflag(i,j,k).isCovered()) {
132  // Extrapolate from the uncovered side, at k and above
133  if (k+2 <= rhi.z) {
134  fz(i,j,k) = dxInv[2] * (three*p_arr(i,j,k+1) - p_arr(i,j,k+2) - two*p_arr(i,j,k));
135  } else if (k+1 <= rhi.z) {
136  fz(i,j,k) = dxInv[2] * (p_arr(i,j,k+1) - p_arr(i,j,k));
137  }
138  }
139  }
140  }
141  });
142  } // single-valued
143  } // mfi
144 }
if(l_use_mynn &&start_comp<=RhoKE_comp &&end_comp >=RhoKE_comp)
Definition: ERF_AddQKESources.H:2
constexpr amrex::Real three
Definition: ERF_Constants.H:11
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
AMREX_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
@ rhi
Definition: ERF_WSM6.H:250

Referenced by solve_with_EB_mlmg().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ solve_with_EB_mlmg() [1/3]

template<typename T >
void solve_with_EB_mlmg ( int  lev,
Vector< MultiFab > &  rhs,
Vector< MultiFab > &  phi,
Vector< Array< MultiFab, AMREX_SPACEDIM >> &  fluxes,
EBFArrayBoxFactory const &  ebfact,
T const &  ebfact_u,
T const &  ebfact_v,
T const &  ebfact_w,
const Geometry &  geom,
const Vector< amrex::IntVect > &  ref_ratio,
Array< std::string, 2 *AMREX_SPACEDIM >  domain_bc_type,
int  mg_verbose,
Real  reltol,
Real  abstol 
)

Solve the Poisson equation using EB_enabled MLMG Note that the level may or may not be level zero

Important: we solve on the whole level even if there are disjoint regions

Template Parameters
TEB factory or auxiliary EB data type for face-centered grids
Parameters
levLevel index for the solve
rhsRight-hand side MultiFab vector
phiSolution MultiFab vector to fill
fluxesFace-centered gradient fluxes to fill
ebfactCell-centered embedded-boundary factory
ebfact_uEmbedded-boundary data on x-faces
ebfact_vEmbedded-boundary data on y-faces
ebfact_wEmbedded-boundary data on z-faces
geomGeometry for the solve level
ref_ratioCoarse-fine refinement ratios
domain_bc_typeDomain boundary-condition names
mg_verboseMLMG verbosity level
reltolRelative solver tolerance
abstolAbsolute solver tolerance
70 {
71  BL_PROFILE("ERF::solve_with_EB_mlmg()");
72 
73  auto const dom_lo = lbound(geom.Domain());
74  auto const dom_hi = ubound(geom.Domain());
75 
76  LPInfo info;
77  // Allow a hidden direction if the domain is one cell wide in any lateral direction
78  if (dom_lo.x == dom_hi.x) {
79  info.setHiddenDirection(0);
80  } else if (dom_lo.y == dom_hi.y) {
81  info.setHiddenDirection(1);
82  }
83 
84  // Make sure the solver only sees the levels over which we are solving
85  Vector<BoxArray> ba_tmp; ba_tmp.push_back(rhs[0].boxArray());
86  Vector<DistributionMapping> dm_tmp; dm_tmp.push_back(rhs[0].DistributionMap());
87  Vector<Geometry> geom_tmp; geom_tmp.push_back(geom);
88 
89  auto bclo = get_lo_projection_bc(geom,domain_bc_type);
90  auto bchi = get_hi_projection_bc(geom,domain_bc_type);
91 
92  amrex::Print() << "BCLO " << bclo[0] << " " << bclo[1] << " " << bclo[2] << std::endl;
93  amrex::Print() << "BCHI " << bchi[0] << " " << bchi[1] << " " << bchi[2] << std::endl;
94 
95  // ****************************************************************************
96  // Multigrid solve
97  // ****************************************************************************
98 
99  MLEBABecLap mleb (geom_tmp, ba_tmp, dm_tmp, info, {&ebfact});
100 
101  mleb.setMaxOrder(2);
102  mleb.setDomainBC(bclo, bchi);
103  if (lev > 0) {
104  mleb.setCoarseFineBC(nullptr, ref_ratio[lev-1], LinOpBCType::Neumann);
105  }
106  mleb.setLevelBC(0, nullptr);
107 
108  //
109  // This sets A = 0, B = 1 so that
110  // the operator A alpha - b del dot beta grad to b
111  // becomes - del dot beta grad
112  //
113  mleb.setScalars(zero, one);
114 
115  Array<MultiFab,AMREX_SPACEDIM> bcoef;
116  for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
117  bcoef[idim].define(convert(ba_tmp[0],IntVect::TheDimensionVector(idim)),
118  dm_tmp[0], 1, 0, MFInfo(), ebfact);
119  bcoef[idim].setVal(-1.0);
120  }
121  mleb.setBCoeffs(0, amrex::GetArrOfConstPtrs(bcoef));
122 
123  MLMG mlmg(mleb);
124 
125  int max_iter = 100;
126  mlmg.setMaxIter(max_iter);
127  mlmg.setVerbose(mg_verbose);
128  mlmg.setBottomVerbose(0);
129 
130  mlmg.solve(GetVecOfPtrs(phi), GetVecOfConstPtrs(rhs), reltol, abstol);
131 
132  mlmg.getFluxes(GetVecOfArrOfPtrs(fluxes));
133 
134  // Add the flux values (gradient phi) to the face-centered cell with zero area fraction
135  // (mlmg.getFluxes does not fill gradient phi at faces with zero area fraction)
136  //
137  // That routine extrapolates the gradient onto a zero-area face from three cells on the
138  // uncovered side, and at a face on the edge of a box the stencil reaches three cells
139  // past the box -- well beyond the single ghost cell phi carries, and beyond whatever
140  // the solve happened to leave in it (issue #3699). Do the extrapolation out of a
141  // three-ghost-cell scratch copy instead of widening phi itself, which would cost MLMG
142  // its alias fast path for the solution. Ghost cells outside the domain in a
143  // non-periodic direction are still unfilled; FillZeroAreaFaceFluxes detects that and
144  // drops to a lower order one-sided form there.
145  MultiFab phi_ext(phi[0].boxArray(), phi[0].DistributionMap(), 1, 3);
146  MultiFab::Copy(phi_ext, phi[0], 0, 0, 1, 0);
147  phi_ext.FillBoundary(geom.periodicity());
148 
149  FillZeroAreaFaceFluxes(phi_ext, fluxes[0], geom, ebfact, ebfact_u, ebfact_v, ebfact_w);
150 
151  // ImposeBCsOnPhi(lev,phi[0], geom[lev].Domain());
152 
153  //
154  // This arises because we solve MINUS del dot beta grad phi = div (rho u)
155  //
156  fluxes[0][0].mult(-one);
157  fluxes[0][1].mult(-one);
158  fluxes[0][2].mult(-one);
159 }
constexpr amrex::Real one
Definition: ERF_Constants.H:9
void FillZeroAreaFaceFluxes(MultiFab &phi, Array< MultiFab, AMREX_SPACEDIM > &fluxes, const Geometry &geom, EBFArrayBoxFactory const &ebfact, T const &ebfact_u, T const &ebfact_v, T const &ebfact_w)
Definition: ERF_FillZeroAreaFaceFluxes.cpp:29
Array< LinOpBCType, AMREX_SPACEDIM > get_lo_projection_bc(Geometry const &lev_geom, Array< std::string, 2 *AMREX_SPACEDIM > l_domain_bc_type)
Definition: ERF_SolverUtils.H:20
Array< LinOpBCType, AMREX_SPACEDIM > get_hi_projection_bc(Geometry const &lev_geom, Array< std::string, 2 *AMREX_SPACEDIM > l_domain_bc_type)
Definition: ERF_SolverUtils.H:48
Here is the call graph for this function:

◆ solve_with_EB_mlmg() [2/3]

template void solve_with_EB_mlmg ( int  ,
Vector< MultiFab > &  ,
Vector< MultiFab > &  ,
Vector< Array< MultiFab, AMREX_SPACEDIM >> &  ,
EBFArrayBoxFactory const &  ,
eb_aux_ const &  ,
eb_aux_ const &  ,
eb_aux_ const &  ,
const Geometry &  ,
const Vector< amrex::IntVect > &  ,
Array< std::string, 2 *AMREX_SPACEDIM >  ,
int  ,
Real  ,
Real   
)

◆ solve_with_EB_mlmg() [3/3]

template void solve_with_EB_mlmg ( int  ,
Vector< MultiFab > &  ,
Vector< MultiFab > &  ,
Vector< Array< MultiFab, AMREX_SPACEDIM >> &  ,
EBFArrayBoxFactory const &  ,
EBFArrayBoxFactory const &  ,
EBFArrayBoxFactory const &  ,
EBFArrayBoxFactory const &  ,
const Geometry &  ,
const Vector< amrex::IntVect > &  ,
Array< std::string, 2 *AMREX_SPACEDIM >  ,
int  ,
Real  ,
Real   
)