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