ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
ERF_TI_utils.H
Go to the documentation of this file.
1 /**
2  * Define the primitive variables by dividing the conserved variables by density
3  */
4  auto cons_to_prim = [&](const MultiFab& cons_state, int ng)
5  {
6  BL_PROFILE("cons_to_prim()");
7 
8  int ncomp_prim = S_prim.nComp();
9 
10 #ifdef _OPENMP
11 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
12 #endif
13  for (MFIter mfi(cons_state,TilingIfNotGPU()); mfi.isValid(); ++mfi)
14  {
15  const Box& gbx = mfi.growntilebox(ng);
16  const Array4<const Real>& cons_arr = cons_state.array(mfi);
17  const Array4< Real>& prim_arr = S_prim.array(mfi);
18 
19  //
20  // We may need > one ghost cells of prim in order to compute higher order advective terms
21  //
22  amrex::ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
23  {
24  Real rho = cons_arr(i,j,k,Rho_comp);
25  Real rho_theta = cons_arr(i,j,k,RhoTheta_comp);
26  prim_arr(i,j,k,PrimTheta_comp) = rho_theta / rho;
27  for (int n = 1; n < ncomp_prim; ++n) {
28  prim_arr(i,j,k,PrimTheta_comp + n) = cons_arr(i,j,k,RhoTheta_comp + n) / rho;
29  }
30  });
31 
32  //
33  // We only use one ghost cell of pi_stage so we only fill one here
34  //
35  const Box& gbx1 = mfi.growntilebox(1);
36 
37  const Array4< Real>& pi_stage_arr = pi_stage.array(mfi);
38  const Real rdOcp = solverChoice.rdOcp;
39 
40  amrex::ParallelFor(gbx1, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
41  {
42  pi_stage_arr(i,j,k) = getExnergivenRTh(cons_arr(i,j,k,RhoTheta_comp), rdOcp);
43  });
44  } // mfi
45  };
46 
47 /**
48  * Define the total water content in each cell
49  */
50  auto make_qt = [&](const MultiFab& cons_state, MultiFab& qt)
51  {
52  BL_PROFILE("make_qt()");
53 
54  int n_qstate_moist = micro->Get_Qstate_Moist_Size();
55 
56  // All moisture models are guaranteed to have RhoQ1_comp
57  MultiFab::Copy(qt, cons_state, RhoQ1_comp, 0, 1, 1);
58 
59  // Sum up each component
60  for (int n(1); n<n_qstate_moist; ++n) {
61  MultiFab::Add(qt, cons_state, RhoQ1_comp+n, 0, 1, 1);
62  }
63 
64  // Divide out the dry density
65  MultiFab::Divide(qt, cons_state, Rho_comp, 0, 1, 1);
66  };
67 
68 /**
69  * This routine is called after the scalars are updated for each RK stage
70  */
71  auto apply_bcs = [&](Vector<MultiFab>& S_data,
72  const Real time_for_fp, int ng_cons, int ng_vel,
73  bool fast_only, bool vel_and_mom_synced)
74  {
75  BL_PROFILE("apply_bcs()");
76 
77  int scomp_cons;
78  int ncomp_cons;
79  bool cons_only;
80 
81  int ng_cons_to_use;
82 
83  // **********************************************************************************
84  // Because momentum is updated in the time-stepping routine, but boundary conditions
85  // are imposed on velocities, we must first update the velocity from the momentum
86  // before applying bcs.
87  // **********************************************************************************
88  if (!vel_and_mom_synced) {
89 
90  // **********************************************************************************
91  // Call FillPatch routines for the density only because we need it to convert between
92  // momentum and velocity
93  // This fills ghost cells/faces from
94  // 1) coarser level if lev > 0
95  // 2) physical boundaries
96  // 3) other grids at the same level
97  // **********************************************************************************
98 
99  // We must have at least one ghost cell of density to convert from momentum to velocity
100  // on the valid region
101  AMREX_ALWAYS_ASSERT (ng_cons >= 1);
102 
103  // We must have at least one extra ghost cell of density to convert from velocity to momentum
104  // on the valid region
105  ng_cons_to_use = std::max(ng_cons, ng_vel+1);
106 
107  scomp_cons = 0;
108  ncomp_cons = 1;
109  cons_only = true;
110  // **********************************************************************************
111  // Fill ghost cells of density only
112  // **********************************************************************************
113  FillIntermediatePatch(level, time_for_fp,
114  {&S_data[IntVars::cons], &xvel_new, &yvel_new, &zvel_new},
115  {&S_data[IntVars::cons], &S_data[IntVars::xmom],
116  &S_data[IntVars::ymom], &S_data[IntVars::zmom]},
117  ng_cons_to_use, 0, cons_only, scomp_cons, ncomp_cons);
118  }
119 
120  // ***************************************************************************************
121  // Call FillPatch routines for all data except rho which was filled above
122  // This fills ghost cells/faces from
123  // 1) coarser level if lev > 0
124  // 2) physical boundaries
125  // 3) other grids at the same level
126  // ***************************************************************************************
127  if (vel_and_mom_synced) {
128  if (fast_only) {
129  scomp_cons = 0;
130  ncomp_cons = 2; // rho and (rho theta) only
131  } else {
132  scomp_cons = 0;
133  ncomp_cons = S_data[IntVars::cons].nComp();
134  }
135  // We must have at least one extra ghost cell of density to convert from velocity to momentum
136  // on the valid region
137  ng_cons_to_use = std::max(ng_cons, ng_vel+1);
138 
139  } else {
140  if (fast_only) {
141  scomp_cons = 1;
142  ncomp_cons = 1; // (rho theta) only since we filled rho above
143  } else {
144  scomp_cons = 1;
145  ncomp_cons = S_data[IntVars::cons].nComp()-1; // since we filled rho above
146  }
147  ng_cons_to_use = ng_cons;
148  }
149 
150  // **********************************************************************************
151  // NOTE: FillIntermediatePatch takes momenta at the new time, and returns
152  // BOTH updated velocities and momenta
153  // **********************************************************************************
154  cons_only = false;
155  FillIntermediatePatch(level, time_for_fp,
156  {&S_data[IntVars::cons], &xvel_new, &yvel_new, &zvel_new},
157  {&S_data[IntVars::cons], &S_data[IntVars::xmom],
158  &S_data[IntVars::ymom], &S_data[IntVars::zmom]},
159  ng_cons_to_use, ng_vel, cons_only, scomp_cons, ncomp_cons);
160  };
161 
162  auto update_terrain_stage = [&](int lev, Real old_step_time, Real old_stage_time, Real new_stage_time, Real slow_dt)
163  {
164  // Note that the "old" and "new" metric terms correspond to
165  // t^n and the RK stage (either t^*, t^** or t^{n+1} that this source
166  // will be used to advance to
167 
168  // The "src" metric terms correspond to the time at which we are evaluating the source here,
169  // aka old_stage_time
170 
171  if (verbose) Print() << "Re-making old geometry at old time : " << old_step_time << std::endl;
172 
173  //
174  // Make a temporary MF to fill the terrain
175  //
176  Box terrain_bx(surroundingNodes(Geom(0).Domain())); terrain_bx.grow(z_phys_nd[lev]->nGrow());
177  FArrayBox terrain_fab_old(makeSlab(terrain_bx,2,0),1);
178  FArrayBox terrain_fab_src(makeSlab(terrain_bx,2,0),1);
179  FArrayBox terrain_fab_new(makeSlab(terrain_bx,2,0),1);
180 
181  prob->init_terrain_surface(fine_geom,terrain_fab_old,old_step_time);
182  prob->init_terrain_surface(fine_geom,terrain_fab_src,old_stage_time);
183  prob->init_terrain_surface(fine_geom,terrain_fab_new,new_stage_time);
184 
185  // Copy on intersection
186  for (MFIter mfi(*z_phys_nd[lev],false); mfi.isValid(); ++mfi)
187  {
188  Box isect = terrain_fab_old.box() & (*z_phys_nd[lev])[mfi].box();
189  ( *z_phys_nd[lev])[mfi].template copy<RunOn::Device>(terrain_fab_old,isect,0,isect,0,1);
190  (*z_phys_nd_src[lev])[mfi].template copy<RunOn::Device>(terrain_fab_src,isect,0,isect,0,1);
191  (*z_phys_nd_new[lev])[mfi].template copy<RunOn::Device>(terrain_fab_new,isect,0,isect,0,1);
192  }
193 
194  make_terrain_fitted_coords(lev,fine_geom,*z_phys_nd[lev], zlevels_stag[lev], phys_bc_type);
195  make_J (fine_geom, *z_phys_nd[lev], *detJ_cc[lev]);
196  make_areas (fine_geom, *z_phys_nd[lev], *ax[lev], *ay[lev], *az[lev]);
197 
198  if (verbose) Print() << "Making src geometry at old_stage_time: "
199  << std::setprecision(timeprecision) << old_stage_time << std::endl;
200  make_terrain_fitted_coords(lev,fine_geom,*z_phys_nd_src[lev], zlevels_stag[lev], phys_bc_type);
201  make_J (fine_geom, *z_phys_nd_src[lev], *detJ_cc_src[lev]);
202  make_areas (fine_geom, *z_phys_nd_src[lev], *ax_src[lev], *ay_src[lev], *az_src[lev]);
203  make_zcc (fine_geom, *z_phys_nd_src[lev], *z_phys_cc_src[lev]);
204 
205  if (verbose) Print() << "Making new geometry at new_stage_time: "
206  << std::setprecision(timeprecision) << new_stage_time << std::endl;
207  make_terrain_fitted_coords(lev,fine_geom,*z_phys_nd_new[lev], zlevels_stag[lev], phys_bc_type);
208  make_J (fine_geom, *z_phys_nd_new[lev], *detJ_cc_new[lev]);
209 
210  Real inv_dt = 1./slow_dt;
211 
212 #ifdef _OPENMP
213 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
214 #endif
215  for (MFIter mfi(*z_t_rk[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
216  {
217  Box gbx = mfi.growntilebox(IntVect(1,1,0));
218 
219  const Array4<Real >& z_t_arr = z_t_rk[lev]->array(mfi);
220  const Array4<Real const>& z_nd_new_arr = z_phys_nd_new[lev]->const_array(mfi);
221  const Array4<Real const>& z_nd_old_arr = z_phys_nd[lev]->const_array(mfi);
222 
223  // Loop over horizontal plane
224  amrex::ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
225  {
226  // Evaluate between RK stages assuming the geometry is linear between old and new time
227  z_t_arr(i,j,k) = 0.25 * inv_dt * (z_nd_new_arr(i+1,j+1,k) - z_nd_old_arr(i+1,j+1,k)
228  +z_nd_new_arr(i ,j+1,k) - z_nd_old_arr( i,j+1,k)
229  +z_nd_new_arr(i+1,j ,k) - z_nd_old_arr(i+1,j ,k)
230  +z_nd_new_arr(i ,j ,k) - z_nd_old_arr(i ,j ,k));
231  });
232 
233  } // mfi
234  };
235 
236  auto update_terrain_substep = [&](int lev, Real old_substep_time, Real new_substep_time, Real dtau,
237  Vector<MultiFab>& S_data, std::unique_ptr<MultiFab>& z_t_pert)
238  {
239  // Make "old" fast geom -- store in z_phys_nd for convenience
240  Box terrain_bx(surroundingNodes(Geom(0).Domain())); terrain_bx.grow(z_phys_nd[lev]->nGrow());
241 
242  if (verbose) Print() << "Making geometry at start of substep time: "
243  << std::setprecision(timeprecision) << old_substep_time << std::endl;
244  FArrayBox terrain_fab_old(makeSlab(terrain_bx,2,0),1);
245  prob->init_terrain_surface(fine_geom,terrain_fab_old,old_substep_time);
246 
247  // Make "new" fast geom
248  if (verbose) Print() << "Making geometry for end of substep time :"
249  << std::setprecision(timeprecision) << new_substep_time << std::endl;
250  FArrayBox terrain_fab_new(makeSlab(terrain_bx,2,0),1);
251  prob->init_terrain_surface(fine_geom,terrain_fab_new,new_substep_time);
252 
253  // Copy on intersection
254  for (MFIter mfi(*z_phys_nd[lev],false); mfi.isValid(); ++mfi)
255  {
256  Box isect = terrain_fab_old.box() & (*z_phys_nd[lev])[mfi].box();
257  ( *z_phys_nd[lev])[mfi].template copy<RunOn::Device>(terrain_fab_old,isect,0,isect,0,1);
258  (*z_phys_nd_new[lev])[mfi].template copy<RunOn::Device>(terrain_fab_new,isect,0,isect,0,1);
259  }
260 
261  make_terrain_fitted_coords(lev,fine_geom,*z_phys_nd[lev], zlevels_stag[lev], phys_bc_type);
262  make_J (fine_geom,*z_phys_nd[lev], *detJ_cc[lev]);
263 
264  make_terrain_fitted_coords(lev,fine_geom,*z_phys_nd_new[lev], zlevels_stag[lev], phys_bc_type);
265  make_J (fine_geom,*z_phys_nd_new[lev], *detJ_cc_new[lev]);
266 
267  Real inv_dt = 1./dtau;
268 
269  z_t_pert = std::make_unique<MultiFab>(S_data[IntVars::zmom].boxArray(), S_data[IntVars::zmom].DistributionMap(), 1, 1);
270 
271  for (MFIter mfi(*z_t_rk[lev],TilingIfNotGPU()); mfi.isValid(); ++mfi)
272  {
273  Box gbx = mfi.growntilebox(IntVect(1,1,0));
274 
275  const Array4<Real >& z_t_arr = z_t_rk[lev]->array(mfi);
276  const Array4<Real >& zp_t_arr = z_t_pert->array(mfi);
277 
278  const Array4<Real const>& z_nd_new_arr = z_phys_nd_new[lev]->const_array(mfi);
279  const Array4<Real const>& z_nd_old_arr = z_phys_nd[lev]->const_array(mfi);
280 
281  // Loop over horizontal plane
282  amrex::ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
283  {
284  // Evaluate between RK stages assuming the geometry is linear between old and new time
285  zp_t_arr(i,j,k) = 0.25 * inv_dt * (z_nd_new_arr(i+1,j+1,k) - z_nd_old_arr(i+1,j+1,k)
286  +z_nd_new_arr(i ,j+1,k) - z_nd_old_arr( i,j+1,k)
287  +z_nd_new_arr(i+1,j ,k) - z_nd_old_arr(i+1,j ,k)
288  +z_nd_new_arr(i ,j ,k) - z_nd_old_arr(i ,j ,k));
289  // Convert to perturbation: z"_t(t) = z_t(t) - z_t^{RK}
290  zp_t_arr(i,j,k) -= z_t_arr(i,j,k);
291  });
292  } // mfi
293  };
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getExnergivenRTh(const amrex::Real rhotheta, const amrex::Real rdOcp, const amrex::Real qv=0.0)
Definition: ERF_EOS.H:159
#define Rho_comp
Definition: ERF_IndexDefines.H:36
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:37
#define PrimTheta_comp
Definition: ERF_IndexDefines.H:50
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:42
auto apply_bcs
Definition: ERF_TI_utils.H:71
auto cons_to_prim
Definition: ERF_TI_utils.H:4
auto update_terrain_substep
Definition: ERF_TI_utils.H:236
auto make_qt
Definition: ERF_TI_utils.H:50
auto update_terrain_stage
Definition: ERF_TI_utils.H:162
void make_areas(const Geometry &geom, MultiFab &z_phys_nd, MultiFab &ax, MultiFab &ay, MultiFab &az)
Definition: ERF_TerrainMetrics.cpp:558
void make_terrain_fitted_coords(int lev, const Geometry &geom, MultiFab &z_phys_nd, Vector< Real > const &z_levels_h, GpuArray< ERF_BC, AMREX_SPACEDIM *2 > &phys_bc_type)
Definition: ERF_TerrainMetrics.cpp:46
void make_J(const Geometry &geom, MultiFab &z_phys_nd, MultiFab &detJ_cc)
Definition: ERF_TerrainMetrics.cpp:520
void make_zcc(const Geometry &geom, MultiFab &z_phys_nd, MultiFab &z_phys_cc)
Definition: ERF_TerrainMetrics.cpp:623
@ ymom
Definition: ERF_IndexDefines.H:160
@ cons
Definition: ERF_IndexDefines.H:158
@ zmom
Definition: ERF_IndexDefines.H:161
@ xmom
Definition: ERF_IndexDefines.H:159
@ rho
Definition: ERF_Kessler.H:22
@ qt
Definition: ERF_Kessler.H:27
@ ng
Definition: ERF_Morrison.H:48