ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_TerrainMetrics.H
Go to the documentation of this file.
1 #ifndef ERF_TERRAIN_METRIC_H_
2 #define ERF_TERRAIN_METRIC_H_
3 
4 #include <AMReX.H>
5 #include <AMReX_Geometry.H>
6 #include <AMReX_MultiFab.H>
7 #include <ERF_IndexDefines.H>
8 #include <ERF_Constants.H>
9 
10 /**
11  * Routine to define default z_phys_nd and z_phys_cc
12  */
13 void
14 init_default_zphys (int lev, const amrex::Geometry& geom,
15  amrex::MultiFab& z_phys_nd, amrex::MultiFab& z_phys_cc,
16  amrex::Real z_offset = zero);
17 
18 /**
19  * Utility routines for constructing terrain metric terms
20  */
21 
22 // Declare functions for ERF.cpp
23 /**
24  * Initialize staggered z-levels and stretched vertical spacing.
25  *
26  * @param[out] zlevels_stag Staggered z-levels.
27  * @param[out] stretched_dz_h Host stretched vertical spacing.
28  * @param[out] stretched_dz_d Device stretched vertical spacing.
29  * @param[in] geom Geometry information.
30  * @param[in] ref_ratio Refinement ratios.
31  * @param[in] grid_stretching_ratio Ratio for grid stretching.
32  * @param[in] zsurf Surface height.
33  * @param[in] dz0 Initial vertical spacing.
34  */
35 void
36 init_zlevels (amrex::Vector<amrex::Vector<amrex::Real>>& zlevels_stag,
37  amrex::Vector<amrex::Vector<amrex::Real>>& stretched_dz_h,
38  amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real>>& stretched_dz_d,
39  amrex::Vector<amrex::Geometry> const& geom,
40  amrex::Vector<amrex::IntVect> const& ref_ratio,
41  const amrex::Real grid_stretching_ratio,
42  const amrex::Real zsurf,
43  const amrex::Real dz0);
44 
45 /**
46  * How the terrain-fitted mesh on a fine level is built when erf.terrain_smoothing != 0.
47  *
48  * The STF and Sullivan TF transformations are not defined on a level that does not
49  * reach the top of the domain, so on a fine level we instead start from the mesh
50  * interpolated from the coarse level. A caller that has done that interpolation --
51  * and, for Transform, has also written the fine-resolution terrain into the k=0 slab
52  * and saved the pre-overwrite field -- says so by passing anything other than None.
53  * Every other caller keeps the historical behavior of aborting, because it has not
54  * prepared z_phys_nd in a way this path can use.
55  */
56 enum struct FineTerrain {
57  None, //!< caller has not prepared a fine mesh from the coarse level
58  Interpolate, //!< use the coordinates interpolated from the coarse level as-is
59  Transform //!< add the fine terrain as a correction that decays with height
60 };
61 
62 /**
63  * Read erf.amr_terrain_refinement and translate it into a FineTerrain.
64  *
65  * Aborts on any value other than "interpolate" or "transform" so that a typo cannot
66  * silently select a path that leaves the fine mesh untransformed.
67  */
70 
71 /**
72  * Construct terrain-fitted coordinates.
73  *
74  * @param[in] lev Level index.
75  * @param[in] geom Geometry information.
76  * @param[out] z_phys_nd Nodal physical height field.
77  * @param[in] z_levels_h Host z-levels.
78  * @param[in] phys_bc_type Physical boundary condition types.
79  * @param[in] fine_terrain How a fine level with terrain_smoothing != 0 is to be built.
80  * @param[in] z_phys_interp Mesh interpolated from the coarse level before the fine
81  * terrain was written into the k=0 slab; required by, and
82  * only used for, FineTerrain::Transform.
83  */
84 void
85 make_terrain_fitted_coords (int lev, const amrex::Geometry& geom,
86  amrex::MultiFab& z_phys_nd,
87  amrex::Vector<amrex::Real> const& z_levels_h,
88  amrex::GpuArray<ERF_BC, AMREX_SPACEDIM*2>& phys_bc_type,
89  FineTerrain fine_terrain = FineTerrain::None,
90  amrex::MultiFab const* z_phys_interp = nullptr);
91 
92 /**
93  * Initialize the terrain grid mapping.
94  *
95  * @param[in] lev Level index.
96  * @param[in] geom Geometry information.
97  * @param[out] z_phys_nd Nodal physical height field.
98  * @param[in] z_levels_h Host z-levels.
99  * @param[in] fine_terrain How a fine level with terrain_smoothing != 0 is to be built.
100  * @param[in] z_phys_interp Mesh interpolated from the coarse level; see above.
101  */
102 void
103 init_which_terrain_grid (int lev, const amrex::Geometry& geom,
104  amrex::MultiFab& z_phys_nd,
105  amrex::Vector<amrex::Real> const& z_levels_h,
106  FineTerrain fine_terrain = FineTerrain::None,
107  amrex::MultiFab const* z_phys_interp = nullptr);
108 
109 /**
110  * Finish the terrain-fitted mesh on a fine level whose interior was set by
111  * interpolation from the coarse level (erf.terrain_smoothing != 0).
112  *
113  * For FineTerrain::Transform this adds the fine-resolution terrain as a correction
114  * that decays linearly to zero at the top of the fine grids. For both modes it then
115  * fills the nodes outside the lateral domain boundary and the layer below the
116  * surface, which init_which_terrain_grid()'s BTF branch would otherwise have filled.
117  *
118  * @param[in] lev Level index.
119  * @param[in] geom Geometry information.
120  * @param[in,out] z_phys_nd Nodal physical height field.
121  * @param[in] z_levels_h Host z-levels.
122  * @param[in] fine_terrain Interpolate or Transform.
123  * @param[in] z_phys_interp Mesh interpolated from the coarse level; see above.
124  */
125 void
126 init_fine_terrain_grid (int lev, const amrex::Geometry& geom,
127  amrex::MultiFab& z_phys_nd,
128  amrex::Vector<amrex::Real> const& z_levels_h,
129  FineTerrain fine_terrain,
130  amrex::MultiFab const* z_phys_interp);
131 
132 // Compute the min dz at cell center with terrain
133 /**
134  * Compute the minimum vertical spacing with terrain.
135  *
136  * @param[in] z_phys_nd Nodal physical height field.
137  * @return Minimum vertical grid spacing.
138  */
140 get_dzmin_terrain (amrex::MultiFab& z_phys_nd);
141 
142 /**
143  * Re-derive the stretched vertical spacing at one level from the nominal z-levels.
144  *
145  * init_zlevels() fills both zlevels_stag and stretched_dz_* together, but
146  * zlevels_stag can be replaced after construction (init_from_wrfinput() does this
147  * when erf.avg_grid_faces_to_nodes = false, and restart reads it back from the
148  * checkpoint). Call this whenever zlevels_stag[lev] changes so that the cell
149  * heights never drift out of sync with the levels they are supposed to describe.
150  *
151  * @param[in] lev Level index.
152  * @param[in] zlevels_stag Staggered z-levels (the source of truth).
153  * @param[out] stretched_dz_h Host stretched vertical spacing.
154  * @param[out] stretched_dz_d Device stretched vertical spacing.
155  */
156 void
158  amrex::Vector<amrex::Vector<amrex::Real>> const& zlevels_stag,
159  amrex::Vector<amrex::Vector<amrex::Real>>& stretched_dz_h,
160  amrex::Vector<amrex::Gpu::DeviceVector<amrex::Real>>& stretched_dz_d);
161 
162 //*****************************************************************************************
163 // Compute terrain metric terms at cell-center
164 //*****************************************************************************************
165 // Metric is at cell center
166 /**
167  * Compute the zeta metric term at the cell center.
168  *
169  * @param[in] i x-index
170  * @param[in] j y-index
171  * @param[in] k z-index
172  * @param[in] cellSizeInv Inverse cell spacing.
173  * @param[in] z_nd Nodal physical height field.
174  * @return Zeta metric term.
175  */
176 AMREX_GPU_HOST_DEVICE
177 AMREX_FORCE_INLINE
179 Compute_h_zeta_AtCellCenter (const int &i, const int &j, const int &k,
180  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
181  const amrex::Array4<const amrex::Real>& z_nd)
182 {
183  amrex::Real dzInv = cellSizeInv[2];
184  amrex::Real met_h_zeta = fourth * dzInv *
185  ( z_nd(i+1,j,k+1) + z_nd(i+1,j+1,k+1) + z_nd(i,j,k+1) + z_nd(i,j+1,k+1)
186  -z_nd(i+1,j,k ) - z_nd(i+1,j+1,k ) - z_nd(i,j,k ) - z_nd(i,j+1,k ) );
187  return met_h_zeta;
188 }
189 
190 // Metric is at cell center
191 /**
192  * Compute the xi metric term at the cell center.
193  *
194  * @param[in] i x-index
195  * @param[in] j y-index
196  * @param[in] k z-index
197  * @param[in] cellSizeInv Inverse cell spacing.
198  * @param[in] z_nd Nodal physical height field.
199  * @return Xi metric term.
200  */
201 AMREX_GPU_HOST_DEVICE
202 AMREX_FORCE_INLINE
204 Compute_h_xi_AtCellCenter (const int &i, const int &j, const int &k,
205  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
206  const amrex::Array4<const amrex::Real>& z_nd)
207 {
208  amrex::Real dxInv = cellSizeInv[0];
209  amrex::Real met_h_xi = fourth * dxInv *
210  ( z_nd(i+1,j,k) + z_nd(i+1,j+1,k) + z_nd(i+1,j,k+1) + z_nd(i+1,j+1,k+1)
211  -z_nd(i ,j,k) - z_nd(i ,j+1,k) - z_nd(i ,j,k+1) - z_nd(i ,j+1,k+1) );
212  return met_h_xi;
213 }
214 
215 // Metric is at cell center
216 /**
217  * Compute the eta metric term at the cell center.
218  *
219  * @param[in] i x-index
220  * @param[in] j y-index
221  * @param[in] k z-index
222  * @param[in] cellSizeInv Inverse cell spacing.
223  * @param[in] z_nd Nodal physical height field.
224  * @return Eta metric term.
225  */
226 AMREX_GPU_HOST_DEVICE
227 AMREX_FORCE_INLINE
229 Compute_h_eta_AtCellCenter (const int &i, const int &j, const int &k,
230  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
231  const amrex::Array4<const amrex::Real>& z_nd)
232 {
233  amrex::Real dyInv = cellSizeInv[1];
234  amrex::Real met_h_eta = fourth * dyInv *
235  ( z_nd(i,j+1,k) + z_nd(i+1,j+1,k) + z_nd(i,j+1,k+1) + z_nd(i+1,j+1,k+1)
236  -z_nd(i,j ,k) - z_nd(i+1,j ,k) - z_nd(i,j ,k+1) - z_nd(i+1,j ,k+1) );
237  return met_h_eta;
238 }
239 
240 
241 //*****************************************************************************************
242 // Compute terrain metric terms at face-centers
243 //*****************************************************************************************
244 // Metric coincides with U location
245 /**
246  * Compute the zeta metric term at the i-face.
247  *
248  * @param[in] i x-index
249  * @param[in] j y-index
250  * @param[in] k z-index
251  * @param[in] cellSizeInv Inverse cell spacing.
252  * @param[in] z_nd Nodal physical height field.
253  * @return Zeta metric term.
254  */
255 AMREX_GPU_DEVICE
256 AMREX_FORCE_INLINE
258 Compute_h_zeta_AtIface (const int &i, const int &j, const int &k,
259  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
260  const amrex::Array4<const amrex::Real>& z_nd)
261 {
262  amrex::Real met_h_zeta = myhalf * cellSizeInv[2] * ( (z_nd(i,j ,k+1) - z_nd(i,j ,k))
263  + (z_nd(i,j+1,k+1) - z_nd(i,j+1,k)) );
264  return met_h_zeta;
265 }
266 
267 // Metric coincides with U location
268 /**
269  * Compute the xi metric term at the i-face.
270  *
271  * @param[in] i x-index
272  * @param[in] j y-index
273  * @param[in] k z-index
274  * @param[in] cellSizeInv Inverse cell spacing.
275  * @param[in] z_nd Nodal physical height field.
276  * @return Xi metric term.
277  */
278 AMREX_GPU_DEVICE
279 AMREX_FORCE_INLINE
281 Compute_h_xi_AtIface (const int &i, const int &j, const int &k,
282  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
283  const amrex::Array4<const amrex::Real>& z_nd)
284 {
285  amrex::Real met_h_xi = amrex::Real(0.125) * cellSizeInv[0] * (
286  (z_nd(i+1,j ,k ) - z_nd(i-1,j ,k )) + (z_nd(i+1,j ,k+1) - z_nd(i-1,j ,k+1)) +
287  (z_nd(i+1,j+1,k ) - z_nd(i-1,j+1,k )) + (z_nd(i+1,j+1,k+1) - z_nd(i-1,j+1,k+1)) );
288  return met_h_xi;
289 }
290 
291 // Metric coincides with U location
292 /**
293  * Compute the eta metric term at the i-face.
294  *
295  * @param[in] i x-index
296  * @param[in] j y-index
297  * @param[in] k z-index
298  * @param[in] cellSizeInv Inverse cell spacing.
299  * @param[in] z_nd Nodal physical height field.
300  * @return Eta metric term.
301  */
302 AMREX_GPU_DEVICE
303 AMREX_FORCE_INLINE
305 Compute_h_eta_AtIface (const int &i, const int &j, const int &k,
306  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
307  const amrex::Array4<const amrex::Real>& z_nd)
308 {
309  amrex::Real met_h_eta = myhalf * cellSizeInv[1] * ( (z_nd(i,j+1,k ) - z_nd(i,j,k ))
310  + (z_nd(i,j+1,k+1) - z_nd(i,j,k+1)) );
311  return met_h_eta;
312 }
313 
314 // Metric coincides with V location
315 /**
316  * Compute the zeta metric term at the j-face.
317  *
318  * @param[in] i x-index
319  * @param[in] j y-index
320  * @param[in] k z-index
321  * @param[in] cellSizeInv Inverse cell spacing.
322  * @param[in] z_nd Nodal physical height field.
323  * @return Zeta metric term.
324  */
325 AMREX_GPU_DEVICE
326 AMREX_FORCE_INLINE
328 Compute_h_zeta_AtJface (const int &i, const int &j, const int &k,
329  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
330  const amrex::Array4<const amrex::Real>& z_nd)
331 {
332  amrex::Real met_h_zeta = myhalf * cellSizeInv[2] * ( (z_nd(i ,j,k+1) - z_nd(i ,j,k ))
333  + (z_nd(i+1,j,k+1) - z_nd(i+1,j,k )) );
334  return met_h_zeta;
335 }
336 
337 // Metric coincides with V location
338 /**
339  * Compute the xi metric term at the j-face.
340  *
341  * @param[in] i x-index
342  * @param[in] j y-index
343  * @param[in] k z-index
344  * @param[in] cellSizeInv Inverse cell spacing.
345  * @param[in] z_nd Nodal physical height field.
346  * @return Xi metric term.
347  */
348 AMREX_GPU_DEVICE
349 AMREX_FORCE_INLINE
351 Compute_h_xi_AtJface (const int &i, const int &j, const int &k,
352  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
353  const amrex::Array4<const amrex::Real>& z_nd)
354 {
355  amrex::Real met_h_xi = myhalf * cellSizeInv[0] * ( (z_nd(i+1,j,k ) - z_nd(i,j,k ))
356  + (z_nd(i+1,j,k+1) - z_nd(i,j,k+1)) );
357  return met_h_xi;
358 }
359 
360 // Metric coincides with V location
361 /**
362  * Compute the eta metric term at the j-face.
363  *
364  * @param[in] i x-index
365  * @param[in] j y-index
366  * @param[in] k z-index
367  * @param[in] cellSizeInv Inverse cell spacing.
368  * @param[in] z_nd Nodal physical height field.
369  * @return Eta metric term.
370  */
371 AMREX_GPU_DEVICE
372 AMREX_FORCE_INLINE
374 Compute_h_eta_AtJface (const int &i, const int &j, const int &k,
375  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
376  const amrex::Array4<const amrex::Real>& z_nd)
377 {
378  amrex::Real met_h_eta = amrex::Real(0.125) * cellSizeInv[1] *
379  ( (z_nd(i ,j+1,k ) - z_nd(i ,j-1,k )) + (z_nd(i ,j+1,k+1) - z_nd(i ,j-1,k+1)) +
380  (z_nd(i+1,j+1,k ) - z_nd(i+1,j-1,k )) + (z_nd(i+1,j+1,k+1) - z_nd(i+1,j-1,k+1)) );
381  return met_h_eta;
382 }
383 
384 // Metric coincides with K location
385 /**
386  * Compute the zeta metric term at the k-face.
387  *
388  * @param[in] i x-index
389  * @param[in] j y-index
390  * @param[in] k z-index
391  * @param[in] cellSizeInv Inverse cell spacing.
392  * @param[in] z_nd Nodal physical height field.
393  * @return Zeta metric term.
394  */
395 AMREX_GPU_DEVICE
396 AMREX_FORCE_INLINE
398 Compute_h_zeta_AtKface (const int &i, const int &j, const int &k,
399  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
400  const amrex::Array4<const amrex::Real>& z_nd)
401 {
402  amrex::Real met_h_zeta = amrex::Real(0.125) * cellSizeInv[2] *
403  ( (z_nd(i ,j ,k+1) - z_nd(i ,j ,k-1)) + (z_nd(i+1,j ,k+1) - z_nd(i+1,j ,k-1)) +
404  (z_nd(i ,j+1,k+1) - z_nd( i,j+1,k-1)) + (z_nd(i+1,j+1,k+1) - z_nd(i+1,j+1,k-1)) );
405  return met_h_zeta;
406 }
407 
408 // Metric coincides with K location
409 /**
410  * Compute the xi metric term at the k-face.
411  *
412  * @param[in] i x-index
413  * @param[in] j y-index
414  * @param[in] k z-index
415  * @param[in] cellSizeInv Inverse cell spacing.
416  * @param[in] z_nd Nodal physical height field.
417  * @return Xi metric term.
418  */
419 AMREX_GPU_DEVICE
420 AMREX_FORCE_INLINE
422 Compute_h_xi_AtKface (const int &i, const int &j, const int &k,
423  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
424  const amrex::Array4<const amrex::Real>& z_nd)
425 {
426  amrex::Real met_h_xi = myhalf * cellSizeInv[0] * ( (z_nd(i+1,j ,k) - z_nd(i,j ,k))
427  + (z_nd(i+1,j+1,k) - z_nd(i,j+1,k)) );
428  return met_h_xi;
429 }
430 
431 // Metric coincides with K location
432 /**
433  * Compute the eta metric term at the k-face.
434  *
435  * @param[in] i x-index
436  * @param[in] j y-index
437  * @param[in] k z-index
438  * @param[in] cellSizeInv Inverse cell spacing.
439  * @param[in] z_nd Nodal physical height field.
440  * @return Eta metric term.
441  */
442 AMREX_GPU_DEVICE
443 AMREX_FORCE_INLINE
445 Compute_h_eta_AtKface (const int &i, const int &j, const int &k,
446  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
447  const amrex::Array4<const amrex::Real>& z_nd)
448 {
449  amrex::Real met_h_eta = myhalf * cellSizeInv[1] * ( (z_nd(i ,j+1,k) - z_nd(i ,j,k))
450  + (z_nd(i+1,j+1,k) - z_nd(i+1,j,k)) );
451  return met_h_eta;
452 }
453 
454 
455 //*****************************************************************************************
456 // Compute terrain metric terms at edge-centers
457 //*****************************************************************************************
458 // -- EdgeCenterK --
459 
460 // Metric is at edge and center Z (red pentagon)
461 /**
462  * Compute the zeta metric term at the k-edge center.
463  *
464  * @param[in] i x-index
465  * @param[in] j y-index
466  * @param[in] k z-index
467  * @param[in] cellSizeInv Inverse cell spacing.
468  * @param[in] z_nd Nodal physical height field.
469  * @return Zeta metric term.
470  */
471 AMREX_GPU_DEVICE
472 AMREX_FORCE_INLINE
474 Compute_h_zeta_AtEdgeCenterK (const int &i, const int &j, const int &k,
475  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
476  const amrex::Array4<const amrex::Real>& z_nd)
477 {
478  amrex::Real dzInv = cellSizeInv[2];
479  amrex::Real met_h_zeta = dzInv * (z_nd(i,j,k+1) - z_nd(i,j,k));
480  return met_h_zeta;
481 }
482 
483 // Metric is at edge and center Z (red pentagon)
484 /**
485  * Compute the xi metric term at the k-edge center.
486  *
487  * @param[in] i x-index
488  * @param[in] j y-index
489  * @param[in] k z-index
490  * @param[in] cellSizeInv Inverse cell spacing.
491  * @param[in] z_nd Nodal physical height field.
492  * @return Xi metric term.
493  */
494 AMREX_GPU_DEVICE
495 AMREX_FORCE_INLINE
497 Compute_h_xi_AtEdgeCenterK (const int &i, const int &j, const int &k,
498  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
499  const amrex::Array4<const amrex::Real>& z_nd)
500 {
501  amrex::Real dxInv = cellSizeInv[0];
502  amrex::Real met_h_xi = fourth * dxInv *
503  ( z_nd(i+1,j,k) + z_nd(i+1,j,k+1)
504  -z_nd(i-1,j,k) - z_nd(i-1,j,k+1) );
505  return met_h_xi;
506 }
507 
508 // Metric is at edge and center Z (red pentagon)
509 /**
510  * Compute the eta metric term at the k-edge center.
511  *
512  * @param[in] i x-index
513  * @param[in] j y-index
514  * @param[in] k z-index
515  * @param[in] cellSizeInv Inverse cell spacing.
516  * @param[in] z_nd Nodal physical height field.
517  * @return Eta metric term.
518  */
519 AMREX_GPU_DEVICE
520 AMREX_FORCE_INLINE
522 Compute_h_eta_AtEdgeCenterK (const int &i, const int &j, const int &k,
523  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
524  const amrex::Array4<const amrex::Real>& z_nd)
525 {
526  amrex::Real dyInv = cellSizeInv[1];
527  amrex::Real met_h_eta = fourth * dyInv *
528  ( z_nd(i,j+1,k) + z_nd(i,j+1,k+1)
529  -z_nd(i,j-1,k) - z_nd(i,j-1,k+1) );
530  return met_h_eta;
531 }
532 
533 // -- EdgeCenterJ --
534 
535 // Metric is at edge and center Y (magenta cross)
536 /**
537  * Compute the zeta metric term at the j-edge center.
538  *
539  * @param[in] i x-index
540  * @param[in] j y-index
541  * @param[in] k z-index
542  * @param[in] cellSizeInv Inverse cell spacing.
543  * @param[in] z_nd Nodal physical height field.
544  * @return Zeta metric term.
545  */
546 AMREX_GPU_DEVICE
547 AMREX_FORCE_INLINE
549 Compute_h_zeta_AtEdgeCenterJ (const int &i, const int &j, const int &k,
550  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
551  const amrex::Array4<const amrex::Real>& z_nd)
552 {
553  amrex::Real dzInv = cellSizeInv[2];
554  amrex::Real met_h_zeta = fourth * dzInv * ( z_nd(i,j,k+1) + z_nd(i,j+1,k+1)
555  -z_nd(i,j,k-1) - z_nd(i,j+1,k-1) );
556  return met_h_zeta;
557 }
558 
559 // Metric is at edge and center Y (magenta cross)
560 /**
561  * Compute the xi metric term at the j-edge center.
562  *
563  * @param[in] i x-index
564  * @param[in] j y-index
565  * @param[in] k z-index
566  * @param[in] cellSizeInv Inverse cell spacing.
567  * @param[in] z_nd Nodal physical height field.
568  * @return Xi metric term.
569  */
570 AMREX_GPU_DEVICE
571 AMREX_FORCE_INLINE
573 Compute_h_xi_AtEdgeCenterJ (const int &i, const int &j, const int &k,
574  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
575  const amrex::Array4<const amrex::Real>& z_nd)
576 {
577  amrex::Real dxInv = cellSizeInv[0];
578  amrex::Real met_h_xi = fourth * dxInv *
579  ( z_nd(i+1,j+1,k) + z_nd(i+1,j ,k)
580  -z_nd(i-1,j+1,k) - z_nd(i-1,j ,k) );
581  return met_h_xi;
582 }
583 
584 // Metric is at edge and center Y (magenta cross)
585 /**
586  * Compute the eta metric term at the j-edge center.
587  *
588  * @param[in] i x-index
589  * @param[in] j y-index
590  * @param[in] k z-index
591  * @param[in] cellSizeInv Inverse cell spacing.
592  * @param[in] z_nd Nodal physical height field.
593  * @return Eta metric term.
594  */
595 AMREX_GPU_DEVICE
596 AMREX_FORCE_INLINE
598 Compute_h_eta_AtEdgeCenterJ (const int &i, const int &j, const int &k,
599  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
600  const amrex::Array4<const amrex::Real>& z_nd)
601 {
602  amrex::Real dyInv = cellSizeInv[1];
603  amrex::Real met_h_eta = dyInv * ( z_nd(i,j+1,k) - z_nd(i,j,k) );
604  return met_h_eta;
605 }
606 
607 // -- EdgeCenterI --
608 
609 // Metric is at edge and center X (purple hexagon)
610 /**
611  * Compute the zeta metric term at the i-edge center.
612  *
613  * @param[in] i x-index
614  * @param[in] j y-index
615  * @param[in] k z-index
616  * @param[in] cellSizeInv Inverse cell spacing.
617  * @param[in] z_nd Nodal physical height field.
618  * @return Zeta metric term.
619  */
620 AMREX_GPU_DEVICE
621 AMREX_FORCE_INLINE
623 Compute_h_zeta_AtEdgeCenterI (const int &i, const int &j, const int &k,
624  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
625  const amrex::Array4<const amrex::Real>& z_nd)
626 {
627  amrex::Real dzInv = cellSizeInv[2];
628  amrex::Real met_h_zeta = fourth * dzInv * ( z_nd(i,j,k+1) + z_nd(i+1,j,k+1)
629  -z_nd(i,j,k-1) - z_nd(i+1,j,k-1) );
630  return met_h_zeta;
631 }
632 
633 // Metric is at edge and center X (purple hexagon)
634 /**
635  * Compute the xi metric term at the i-edge center.
636  *
637  * @param[in] i x-index
638  * @param[in] j y-index
639  * @param[in] k z-index
640  * @param[in] cellSizeInv Inverse cell spacing.
641  * @param[in] z_nd Nodal physical height field.
642  * @return Xi metric term.
643  */
644 AMREX_GPU_DEVICE
645 AMREX_FORCE_INLINE
647 Compute_h_xi_AtEdgeCenterI (const int &i, const int &j, const int &k,
648  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
649  const amrex::Array4<const amrex::Real>& z_nd)
650 {
651  amrex::Real dxInv = cellSizeInv[0];
652  amrex::Real met_h_xi = dxInv * ( z_nd(i+1,j,k) - z_nd(i,j,k) );
653  return met_h_xi;
654 }
655 
656 // Metric is at edge and center X (purple hexagon)
657 /**
658  * Compute the eta metric term at the i-edge center.
659  *
660  * @param[in] i x-index
661  * @param[in] j y-index
662  * @param[in] k z-index
663  * @param[in] cellSizeInv Inverse cell spacing.
664  * @param[in] z_nd Nodal physical height field.
665  * @return Eta metric term.
666  */
667 AMREX_GPU_DEVICE
668 AMREX_FORCE_INLINE
670 Compute_h_eta_AtEdgeCenterI (const int &i, const int &j, const int &k,
671  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
672  const amrex::Array4<const amrex::Real>& z_nd)
673 {
674  amrex::Real dyInv = cellSizeInv[1];
675  amrex::Real met_h_eta = fourth * dyInv *
676  ( z_nd(i+1,j+1,k) + z_nd(i,j+1,k)
677  -z_nd(i+1,j-1,k) - z_nd(i,j-1,k) );
678  return met_h_eta;
679 }
680 
681 // Relative height above terrain surface at cell center from z_nd (nodal absolute height)
682 /**
683  * Compute the physical height at the cell center.
684  *
685  * @param[in] i x-index
686  * @param[in] j y-index
687  * @param[in] k z-index
688  * @param[in] z_nd Nodal physical height field.
689  * @return Physical height at cell center.
690  */
691 AMREX_GPU_DEVICE
692 AMREX_FORCE_INLINE
694 Compute_Z_AtCellCenter (const int &i, const int &j, const int &k,
695  const amrex::Array4<const amrex::Real>& z_nd)
696 {
697  const amrex::Real z_cc = amrex::Real(0.125)*( z_nd(i ,j ,k) + z_nd(i ,j ,k+1)
698  + z_nd(i+1,j ,k) + z_nd(i+1,j ,k+1)
699  + z_nd(i ,j+1,k) + z_nd(i ,j+1,k+1)
700  + z_nd(i+1,j+1,k) + z_nd(i+1,j+1,k+1));
701 
702  return z_cc;
703 }
704 
705 // Relative height above terrain surface at cell center from z_nd (nodal absolute height)
706 /**
707  * Compute the physical height at the W-face.
708  *
709  * @param[in] i x-index
710  * @param[in] j y-index
711  * @param[in] k z-index
712  * @param[in] z_nd Nodal physical height field.
713  * @return Physical height at W-face.
714  */
715 AMREX_GPU_DEVICE
716 AMREX_FORCE_INLINE
718 Compute_Z_AtWFace (const int &i, const int &j, const int &k,
719  const amrex::Array4<const amrex::Real>& z_nd)
720 {
721  const amrex::Real z_wf = fourth*( z_nd(i ,j ,k) + z_nd(i+1,j ,k)
722  + z_nd(i ,j+1,k) + z_nd(i+1,j+1,k) );
723 
724  return z_wf;
725 }
726 
727 // Relative height above terrain surface at cell center from z_nd (nodal absolute height)
728 /**
729  * Compute the relative height above the terrain surface at the cell center.
730  *
731  * @param[in] i x-index
732  * @param[in] j y-index
733  * @param[in] k z-index
734  * @param[in] z_nd Nodal physical height field.
735  * @return Relative height at cell center.
736  */
737 AMREX_GPU_DEVICE
738 AMREX_FORCE_INLINE
740 Compute_Zrel_AtCellCenter (const int &i, const int &j, const int &k,
741  const amrex::Array4<const amrex::Real>& z_nd)
742 {
743  const amrex::Real z_cc = amrex::Real(0.125)*( z_nd(i ,j ,k) + z_nd(i ,j ,k+1) +
744  + z_nd(i+1,j ,k) + z_nd(i+1,j ,k+1)
745  + z_nd(i ,j+1,k) + z_nd(i ,j+1,k+1)
746  + z_nd(i+1,j+1,k) + z_nd(i+1,j+1,k+1));
747 
748  // Note: we assume the z_nd array spans from the bottom to top of the domain
749  // i.e. no domain decomposition across processors in vertical direction
750  const amrex::Real z0_cc = fourth*( z_nd(i ,j ,0) + z_nd(i ,j+1,0)
751  +z_nd(i+1,j ,0) + z_nd(i+1,j+1,0));
752 
753  return (z_cc - z0_cc);
754 }
755 
756 
757 //*****************************************************************************************
758 // Contravariant velocity computation
759 //*****************************************************************************************
760 
761 // Define omega given u,v and w (4th order)
762 /**
763  * Compute the contravariant vertical velocity omega from w.
764  *
765  * @param[in] i x-index
766  * @param[in] j y-index
767  * @param[in] k z-index
768  * @param[in] w Vertical velocity.
769  * @param[in] u_arr X-velocity array.
770  * @param[in] v_arr Y-velocity array.
771  * @param[in] mf_u X-velocity map factor.
772  * @param[in] mf_v Y-velocity map factor.
773  * @param[in] z_nd Nodal physical height field.
774  * @param[in] dxInv Inverse cell spacing.
775  * @return Contravariant vertical velocity.
776  */
777 AMREX_GPU_DEVICE
778 AMREX_FORCE_INLINE
780 OmegaFromW (int& i, int& j, int& k, amrex::Real w,
781  const amrex::Array4<const amrex::Real>& u_arr,
782  const amrex::Array4<const amrex::Real>& v_arr,
783  const amrex::Array4<const amrex::Real>& mf_u,
784  const amrex::Array4<const amrex::Real>& mf_v,
785  const amrex::Array4<const amrex::Real>& z_nd,
786  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& dxInv)
787 {
788  // This is dh/dxi at hi and lo edges
789  amrex::Real z_x_p2 = Compute_Z_AtWFace(i+2,j,k,z_nd);
790  amrex::Real z_x_p1 = Compute_Z_AtWFace(i+1,j,k,z_nd);
791  amrex::Real z_x_m1 = Compute_Z_AtWFace(i-1,j,k,z_nd);
792  amrex::Real z_x_m2 = Compute_Z_AtWFace(i-2,j,k,z_nd);
793  amrex::Real met_xi = (amrex::Real(1.0/amrex::Real(12.0))*(z_x_m2 - z_x_p2)
794  + amrex::Real(8.0/amrex::Real(12.0))*(z_x_p1 - z_x_m1)) * dxInv[0];
795 
796  // This is dh/deta at hi and lo edges
797  amrex::Real z_y_p2 = Compute_Z_AtWFace(i,j+2,k,z_nd);
798  amrex::Real z_y_p1 = Compute_Z_AtWFace(i,j+1,k,z_nd);
799  amrex::Real z_y_m1 = Compute_Z_AtWFace(i,j-1,k,z_nd);
800  amrex::Real z_y_m2 = Compute_Z_AtWFace(i,j-2,k,z_nd);
801  amrex::Real met_eta = (amrex::Real(1.0/amrex::Real(12.0))*(z_y_m2 - z_y_p2)
802  + amrex::Real(8.0/amrex::Real(12.0))*(z_y_p1 - z_y_m1)) * dxInv[1];
803 
804  // Use extrapolation instead of interpolation if at the bottom boundary
805  amrex::Real u_hi = (k == 0) ? amrex::Real(1.5) * u_arr(i+1,j ,k ) - myhalf * u_arr(i+1,j ,k+1) :
806  myhalf * ( u_arr(i+1,j ,k-1) + u_arr(i+1,j ,k ) );
807  amrex::Real u_lo = (k == 0) ? amrex::Real(1.5) * u_arr(i ,j ,k ) - myhalf * u_arr(i ,j ,k+1) :
808  myhalf * ( u_arr(i ,j ,k-1) + u_arr(i ,j ,k ) );
809  amrex::Real mf_u_hi = mf_u(i+1,j,0);
810  amrex::Real mf_u_lo = mf_u(i ,j,0);
811 
812  amrex::Real v_hi = (k == 0) ? amrex::Real(1.5) * v_arr(i ,j+1,k ) - myhalf * v_arr(i ,j+1,k+1) :
813  myhalf * ( v_arr(i ,j+1,k-1) + v_arr(i ,j+1,k ) );
814  amrex::Real v_lo = (k == 0) ? amrex::Real(1.5) * v_arr(i ,j ,k ) - myhalf * v_arr(i ,j ,k+1) :
815  myhalf * ( v_arr(i ,j ,k-1) + v_arr(i ,j ,k ) );
816  amrex::Real mf_v_hi = mf_v(i,j+1,0);
817  amrex::Real mf_v_lo = mf_v(i,j ,0);
818 
819  amrex::Real u_met = met_xi * myhalf * ( u_hi*mf_u_hi + u_lo*mf_u_lo );
820  amrex::Real v_met = met_eta * myhalf * ( v_hi*mf_v_hi + v_lo*mf_v_lo );
821 
822  amrex::Real omega = w - u_met - v_met;
823  return omega;
824 }
825 
826 // Define w given u and v arrays and scalar omega (4th order)
827 /**
828  * Compute the vertical velocity w from the contravariant omega.
829  *
830  * @param[in] i x-index
831  * @param[in] j y-index
832  * @param[in] k z-index
833  * @param[in] omega Contravariant vertical velocity.
834  * @param[in] u_arr X-velocity array.
835  * @param[in] v_arr Y-velocity array.
836  * @param[in] mf_u X-velocity map factor.
837  * @param[in] mf_v Y-velocity map factor.
838  * @param[in] z_nd Nodal physical height field.
839  * @param[in] dxInv Inverse cell spacing.
840  * @return Vertical velocity w.
841  */
842 AMREX_GPU_DEVICE
843 AMREX_FORCE_INLINE
845 WFromOmega (int& i, int& j, int& k, amrex::Real omega,
846  const amrex::Array4<const amrex::Real>& u_arr,
847  const amrex::Array4<const amrex::Real>& v_arr,
848  const amrex::Array4<const amrex::Real>& mf_u,
849  const amrex::Array4<const amrex::Real>& mf_v,
850  const amrex::Array4<const amrex::Real>& z_nd,
851  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& dxInv)
852 {
853  // This is dh/dxi at hi and lo edges
854  amrex::Real z_x_p2 = Compute_Z_AtWFace(i+2,j,k,z_nd);
855  amrex::Real z_x_p1 = Compute_Z_AtWFace(i+1,j,k,z_nd);
856  amrex::Real z_x_m1 = Compute_Z_AtWFace(i-1,j,k,z_nd);
857  amrex::Real z_x_m2 = Compute_Z_AtWFace(i-2,j,k,z_nd);
858  amrex::Real met_xi = (amrex::Real(1.0/amrex::Real(12.0))*(z_x_m2 - z_x_p2)
859  + amrex::Real(8.0/amrex::Real(12.0))*(z_x_p1 - z_x_m1)) * dxInv[0];
860 
861  // This is dh/deta at hi and lo edges
862  amrex::Real z_y_p2 = Compute_Z_AtWFace(i,j+2,k,z_nd);
863  amrex::Real z_y_p1 = Compute_Z_AtWFace(i,j+1,k,z_nd);
864  amrex::Real z_y_m1 = Compute_Z_AtWFace(i,j-1,k,z_nd);
865  amrex::Real z_y_m2 = Compute_Z_AtWFace(i,j-2,k,z_nd);
866  amrex::Real met_eta = (amrex::Real(1.0/amrex::Real(12.0))*(z_y_m2 - z_y_p2)
867  + amrex::Real(8.0/amrex::Real(12.0))*(z_y_p1 - z_y_m1)) * dxInv[1];
868 
869  // Use extrapolation instead of interpolation if at the bottom boundary
870  amrex::Real u_hi = (k == 0) ? amrex::Real(1.5) * u_arr(i+1,j ,k ) - myhalf * u_arr(i+1,j ,k+1) :
871  myhalf * ( u_arr(i+1,j ,k-1) + u_arr(i+1,j ,k ) );
872  amrex::Real u_lo = (k == 0) ? amrex::Real(1.5) * u_arr(i ,j ,k ) - myhalf * u_arr(i ,j ,k+1) :
873  myhalf * ( u_arr(i ,j ,k-1) + u_arr(i ,j ,k ) );
874  amrex::Real mf_u_hi = mf_u(i+1,j,0);
875  amrex::Real mf_u_lo = mf_u(i ,j,0);
876 
877  amrex::Real v_hi = (k == 0) ? amrex::Real(1.5) * v_arr(i ,j+1,k ) - myhalf * v_arr(i ,j+1,k+1) :
878  myhalf * ( v_arr(i ,j+1,k-1) + v_arr(i ,j+1,k ) );
879  amrex::Real v_lo = (k == 0) ? amrex::Real(1.5) * v_arr(i ,j ,k ) - myhalf * v_arr(i ,j ,k+1) :
880  myhalf * ( v_arr(i ,j ,k-1) + v_arr(i ,j ,k ) );
881  amrex::Real mf_v_hi = mf_v(i,j+1,0);
882  amrex::Real mf_v_lo = mf_v(i,j ,0);
883 
884  amrex::Real u_met = met_xi * myhalf * ( u_hi*mf_u_hi + u_lo*mf_u_lo );
885  amrex::Real v_met = met_eta * myhalf * ( v_hi*mf_v_hi + v_lo*mf_v_lo );
886 
887  amrex::Real w = omega + u_met + v_met;
888  return w;
889 }
890 
891 //
892 // NOTE: 2nd order kept here for debugging purposes
893 //
894 
895 # if 0
896 // Define omega given u,v and w (2nd order)
897 /**
898  * Compute the contravariant vertical velocity omega from w (2nd order).
899  *
900  * @param[in] i x-index
901  * @param[in] j y-index
902  * @param[in] k z-index
903  * @param[in] w Vertical velocity.
904  * @param[in] u_arr X-velocity array.
905  * @param[in] v_arr Y-velocity array.
906  * @param[in] mf_u X-velocity map factor.
907  * @param[in] mf_v Y-velocity map factor.
908  * @param[in] z_nd Nodal physical height field.
909  * @param[in] dxInv Inverse cell spacing.
910  * @return Contravariant vertical velocity.
911  */
912 AMREX_GPU_DEVICE
913 AMREX_FORCE_INLINE
915 OmegaFromW (int& i, int& j, int& k, amrex::Real w,
916  const amrex::Array4<const amrex::Real>& u_arr,
917  const amrex::Array4<const amrex::Real>& v_arr,
918  const amrex::Array4<const amrex::Real>& mf_u,
919  const amrex::Array4<const amrex::Real>& mf_v,
920  const amrex::Array4<const amrex::Real>& z_nd,
921  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& dxInv)
922 {
923  // This is dh/dxi at hi and lo edges
924  amrex::Real z_x_h = Compute_Z_AtWFace(i+1,j,k,z_nd);
925  amrex::Real z_x_l = Compute_Z_AtWFace(i-1,j,k,z_nd);
926  amrex::Real met_xi = myhalf * (z_x_h - z_x_l) * dxInv[0];
927 
928  // This is dh/deta at hi and lo edges
929  amrex::Real z_y_h = Compute_Z_AtWFace(i,j+1,k,z_nd);
930  amrex::Real z_y_l = Compute_Z_AtWFace(i,j-1,k,z_nd);
931  amrex::Real met_eta = myhalf * (z_y_h - z_y_l) * dxInv[1];
932 
933  // Use extrapolation instead of interpolation if at the bottom boundary
934  amrex::Real u_hi = (k == 0) ? amrex::Real(1.5) * u_arr(i+1,j ,k ) - myhalf * u_arr(i+1,j ,k+1) :
935  myhalf * ( u_arr(i+1,j ,k-1) + u_arr(i+1,j ,k ) );
936  amrex::Real u_lo = (k == 0) ? amrex::Real(1.5) * u_arr(i ,j ,k ) - myhalf * u_arr(i ,j ,k+1) :
937  myhalf * ( u_arr(i ,j ,k-1) + u_arr(i ,j ,k ) );
938  amrex::Real mf_u_hi = mf_u(i+1,j,0);
939  amrex::Real mf_u_lo = mf_u(i ,j,0);
940 
941  amrex::Real v_hi = (k == 0) ? amrex::Real(1.5) * v_arr(i ,j+1,k ) - myhalf * v_arr(i ,j+1,k+1) :
942  myhalf * ( v_arr(i ,j+1,k-1) + v_arr(i ,j+1,k ) );
943  amrex::Real v_lo = (k == 0) ? amrex::Real(1.5) * v_arr(i ,j ,k ) - myhalf * v_arr(i ,j ,k+1) :
944  myhalf * ( v_arr(i ,j ,k-1) + v_arr(i ,j ,k ) );
945  amrex::Real mf_v_hi = mf_v(i,j+1,0);
946  amrex::Real mf_v_lo = mf_v(i,j ,0);
947 
948  amrex::Real u_met = met_xi * myhalf * ( u_hi*mf_u_hi + u_lo*mf_u_lo );
949  amrex::Real v_met = met_eta * myhalf * ( v_hi*mf_v_hi + v_lo*mf_v_lo );
950 
951  amrex::Real omega = w - u_met - v_met;
952  return omega;
953 }
954 
955 // Define w given u and v arrays and scalar omega (2nd order)
956 /**
957  * Compute the vertical velocity w from the contravariant omega (2nd order).
958  *
959  * @param[in] i x-index
960  * @param[in] j y-index
961  * @param[in] k z-index
962  * @param[in] omega Contravariant vertical velocity.
963  * @param[in] u_arr X-velocity array.
964  * @param[in] v_arr Y-velocity array.
965  * @param[in] mf_u X-velocity map factor.
966  * @param[in] mf_v Y-velocity map factor.
967  * @param[in] z_nd Nodal physical height field.
968  * @param[in] dxInv Inverse cell spacing.
969  * @return Vertical velocity w.
970  */
971 AMREX_GPU_DEVICE
972 AMREX_FORCE_INLINE
974 WFromOmega (int& i, int& j, int& k, amrex::Real omega,
975  const amrex::Array4<const amrex::Real>& u_arr,
976  const amrex::Array4<const amrex::Real>& v_arr,
977  const amrex::Array4<const amrex::Real>& mf_u,
978  const amrex::Array4<const amrex::Real>& mf_v,
979  const amrex::Array4<const amrex::Real>& z_nd,
980  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& dxInv)
981 {
982  // This is dh/dxi at hi and lo edges
983  amrex::Real z_x_h = Compute_Z_AtWFace(i+1,j,k,z_nd);
984  amrex::Real z_x_l = Compute_Z_AtWFace(i-1,j,k,z_nd);
985  amrex::Real met_xi = myhalf * (z_x_h - z_x_l) * dxInv[0];
986 
987  // This is dh/deta at hi and lo edges
988  amrex::Real z_y_h = Compute_Z_AtWFace(i,j+1,k,z_nd);
989  amrex::Real z_y_l = Compute_Z_AtWFace(i,j-1,k,z_nd);
990  amrex::Real met_eta = myhalf * (z_y_h - z_y_l) * dxInv[1];
991 
992  // Use extrapolation instead of interpolation if at the bottom boundary
993  amrex::Real u_hi = (k == 0) ? amrex::Real(1.5) * u_arr(i+1,j ,k ) - myhalf * u_arr(i+1,j ,k+1) :
994  myhalf * ( u_arr(i+1,j ,k-1) + u_arr(i+1,j ,k ) );
995  amrex::Real u_lo = (k == 0) ? amrex::Real(1.5) * u_arr(i ,j ,k ) - myhalf * u_arr(i ,j ,k+1) :
996  myhalf * ( u_arr(i ,j ,k-1) + u_arr(i ,j ,k ) );
997  amrex::Real mf_u_hi = mf_u(i+1,j,0);
998  amrex::Real mf_u_lo = mf_u(i ,j,0);
999 
1000  amrex::Real v_hi = (k == 0) ? amrex::Real(1.5) * v_arr(i ,j+1,k ) - myhalf * v_arr(i ,j+1,k+1) :
1001  myhalf * ( v_arr(i ,j+1,k-1) + v_arr(i ,j+1,k ) );
1002  amrex::Real v_lo = (k == 0) ? amrex::Real(1.5) * v_arr(i ,j ,k ) - myhalf * v_arr(i ,j ,k+1) :
1003  myhalf * ( v_arr(i ,j ,k-1) + v_arr(i ,j ,k ) );
1004  amrex::Real mf_v_hi = mf_v(i,j+1,0);
1005  amrex::Real mf_v_lo = mf_v(i,j ,0);
1006 
1007  amrex::Real u_met = met_xi * myhalf * ( u_hi*mf_u_hi + u_lo*mf_u_lo );
1008  amrex::Real v_met = met_eta * myhalf * ( v_hi*mf_v_hi + v_lo*mf_v_lo );
1009 
1010  amrex::Real w = omega + u_met + v_met;
1011  return w;
1012 }
1013 #endif
1014 
1015 
1016 //*****************************************************************************************
1017 // Rotate scalar flux vector and stress tensor for MOST
1018 //*****************************************************************************************
1019 /**
1020  * Rotate a scalar flux vector into terrain-following coordinates.
1021  *
1022  * @param[in] i x-index
1023  * @param[in] j y-index
1024  * @param[in] klo z-index
1025  * @param[in] flux Scalar flux value.
1026  * @param[in] dxInv Inverse cell spacing.
1027  * @param[in] zphys_arr Physical height field.
1028  * @param[out] phi1_arr Rotated flux component 1.
1029  * @param[out] phi2_arr Rotated flux component 2.
1030  * @param[out] phi3_arr Rotated flux component 3.
1031  */
1032 AMREX_GPU_DEVICE
1033 AMREX_FORCE_INLINE
1034 void
1035 rotate_scalar_flux (const int& i,
1036  const int& j,
1037  const int& klo,
1038  const amrex::Real& flux,
1039  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& dxInv,
1040  const amrex::Array4<const amrex::Real>& zphys_arr,
1041  const amrex::Array4<amrex::Real>& phi1_arr,
1042  const amrex::Array4<amrex::Real>& phi2_arr,
1043  const amrex::Array4<amrex::Real>& phi3_arr)
1044 {
1045  amrex::Real h_xi = Compute_h_xi_AtCellCenter(i, j, klo, dxInv, zphys_arr);
1046  amrex::Real h_eta = Compute_h_eta_AtCellCenter(i, j, klo, dxInv, zphys_arr);
1047  amrex::Real InvNorm = one / std::sqrt(one + h_xi*h_xi + h_eta*h_eta);
1048  phi1_arr(i,j,klo) = -h_xi * flux * InvNorm;
1049  phi2_arr(i,j,klo) = -h_eta * flux * InvNorm;
1050  phi3_arr(i,j,klo) = flux * InvNorm;
1051 }
1052 
1053 /**
1054  * Rotate the stress tensor for the MOST model.
1055  *
1056  * @param[in] i x-index
1057  * @param[in] j y-index
1058  * @param[in] klo z-index
1059  * @param[in] flux Flux value used for rotation.
1060  * @param[in] dxInv Inverse cell spacing.
1061  * @param[in] zphys_arr Physical height field.
1062  * @param[in] u_arr X-velocity array.
1063  * @param[in] v_arr Y-velocity array.
1064  * @param[in] w_arr Z-velocity array.
1065  * @param[out] tau11_arr Stress component tau11.
1066  * @param[out] tau22_arr Stress component tau22.
1067  * @param[out] tau33_arr Stress component tau33.
1068  * @param[out] tau12_arr Stress component tau12.
1069  * @param[out] tau21_arr Stress component tau21.
1070  * @param[out] tau13_arr Stress component tau13.
1071  * @param[out] tau31_arr Stress component tau31.
1072  * @param[out] tau23_arr Stress component tau23.
1073  * @param[out] tau32_arr Stress component tau32.
1074  */
1075 AMREX_GPU_DEVICE
1076 AMREX_FORCE_INLINE
1077 void
1078 rotate_stress_tensor (const int& i,
1079  const int& j,
1080  const int& klo,
1081  const amrex::Real& flux,
1082  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& dxInv,
1083  const amrex::Array4<const amrex::Real>& zphys_arr,
1084  const amrex::Array4<const amrex::Real>& u_arr,
1085  const amrex::Array4<const amrex::Real>& v_arr,
1086  const amrex::Array4<const amrex::Real>& w_arr,
1087  const amrex::Array4<amrex::Real>& tau11_arr,
1088  const amrex::Array4<amrex::Real>& tau22_arr,
1089  const amrex::Array4<amrex::Real>& tau33_arr,
1090  const amrex::Array4<amrex::Real>& tau12_arr,
1091  const amrex::Array4<amrex::Real>& tau21_arr,
1092  const amrex::Array4<amrex::Real>& tau13_arr,
1093  const amrex::Array4<amrex::Real>& tau31_arr,
1094  const amrex::Array4<amrex::Real>& tau23_arr,
1095  const amrex::Array4<amrex::Real>& tau32_arr)
1096 {
1097  // Unit-normal vector
1098  amrex::Array1D<amrex::Real,0,2> n_hat;
1099 
1100  // Unit-tangent vectors
1101  amrex::Array1D<amrex::Real,0,2> t_hat_1;
1102  amrex::Array1D<amrex::Real,0,2> t_hat_2;
1103  amrex::Array1D<amrex::Real,0,2> u_t_hat;
1104 
1105  // Final basis vector for right-hand coord sys
1106  amrex::Array1D<amrex::Real,0,2> a_hat;
1107 
1108  // Rotation matrix
1109  amrex::Array2D<amrex::Real,0,2,0,2> R_mat;
1110 
1111  // Metric data
1112  amrex::Real h_xi = Compute_h_xi_AtIface(i, j, klo, dxInv, zphys_arr);
1113  amrex::Real h_eta = Compute_h_eta_AtIface(i, j, klo, dxInv, zphys_arr);
1114  amrex::Real h_zeta = Compute_h_zeta_AtIface(i, j, klo, dxInv, zphys_arr);
1115 
1116  // Populate the normal vector
1117  amrex::Real Inormn = one/std::sqrt(one + h_xi*h_xi + h_eta*h_eta);
1118  n_hat(0) = Inormn*h_xi; n_hat(1) = Inormn*h_eta; n_hat(2) = -Inormn;
1119 
1120  // Populate the tangent vectors: t_1 ~ (1,0,h_xi) and t_2 ~ (0,1,h_eta).
1121  // NOTE: the out-of-plane component of each is identically zero and must be
1122  // set explicitly since Array1D is an aggregate with no default init.
1123  amrex::Real Inorm1 = one/std::sqrt(one + h_xi*h_xi);
1124  amrex::Real Inorm2 = one/std::sqrt(one + h_eta*h_eta);
1125  t_hat_1(0) = -Inorm1; t_hat_2(0) = zero;
1126  t_hat_1(1) = zero; t_hat_2(1) = -Inorm2;
1127  t_hat_1(2) = -Inorm1*h_xi; t_hat_2(2) = -Inorm2*h_eta;
1128 
1129  // Populate the u_t vector
1130  amrex::Real Norm_u_t = zero;
1131  amrex::Real mag1 = (u_arr(i,j,klo) + h_xi *w_arr(i,j,klo))*Inorm1;
1132  amrex::Real mag2 = (v_arr(i,j,klo) + h_eta*w_arr(i,j,klo))*Inorm2;
1133  for (int icol(0); icol<3; ++icol) {
1134  u_t_hat(icol) = mag1*t_hat_1(icol) + mag2*t_hat_2(icol);
1135  Norm_u_t += u_t_hat(icol)*u_t_hat(icol);
1136  }
1137  // NOTE: the direction of the tangential velocity is undefined when that
1138  // velocity vanishes (a quiescent surface cell, or flow normal to the
1139  // terrain), so we cannot normalize. Leaving u_t_hat as the zero vector
1140  // is the only rotation-invariant choice, and it is the right limit for
1141  // a quiescent cell since the flux vanishes with the surface wind too.
1142  amrex::Real Inorm_u_t = (Norm_u_t > zero) ? one/std::sqrt(Norm_u_t) : zero;
1143  for (int icol(0); icol<3; ++icol) {
1144  u_t_hat(icol) *= Inorm_u_t;
1145  }
1146 
1147  // Populate the a_hat vector
1148  a_hat(0) = n_hat(1)*u_t_hat(2) - n_hat(2)*u_t_hat(1);
1149  a_hat(1) = -(n_hat(0)*u_t_hat(2) - n_hat(2)*u_t_hat(0));
1150  a_hat(2) = n_hat(0)*u_t_hat(1) - n_hat(1)*u_t_hat(0);
1151 
1152  // Copy column vectors into R_mat
1153  int jrow;
1154  jrow = 0;
1155  for (int icol(0); icol<3; ++icol) {
1156  R_mat(icol,jrow) = u_t_hat(icol);
1157  }
1158  jrow = 1;
1159  for (int icol(0); icol<3; ++icol) {
1160  R_mat(icol,jrow) = a_hat(icol);
1161  }
1162  jrow = 2;
1163  for (int icol(0); icol<3; ++icol) {
1164  R_mat(icol,jrow) = n_hat(icol);
1165  }
1166 
1167  // Body-fixed tau
1168  amrex::Real T11 = two*R_mat(0,0)*R_mat(2,0)*flux;
1169  amrex::Real T22 = two*R_mat(0,1)*R_mat(2,1)*flux;
1170  amrex::Real T33 = two*R_mat(0,2)*R_mat(2,2)*flux;
1171  amrex::Real T12 = (R_mat(0,0)*R_mat(2,1) + R_mat(2,0)*R_mat(0,1))*flux;
1172  amrex::Real T13 = (R_mat(0,0)*R_mat(2,2) + R_mat(0,2)*R_mat(2,0))*flux;
1173  amrex::Real T23 = (R_mat(0,1)*R_mat(2,2) + R_mat(0,2)*R_mat(2,1))*flux;
1174 
1175  // Rotated stress fluxes
1176  tau11_arr(i,j,klo) = h_zeta*T11;
1177  tau22_arr(i,j,klo) = h_zeta*T22;
1178  tau33_arr(i,j,klo) = -h_xi*T13 - h_eta*T23 + T33;
1179 
1180  tau12_arr(i,j,klo) = h_zeta*T12;
1181  tau21_arr(i,j,klo) = h_zeta*T12; // the body-fixed tensor is symmetric: T21 == T12
1182 
1183  tau13_arr(i,j,klo) = -h_xi*T11 - h_eta*T12 + T13;
1184  tau31_arr(i,j,klo) = h_zeta*T13;
1185 
1186  tau23_arr(i,j,klo) = -h_xi*T12 - h_eta*T22 + T23;
1187  tau32_arr(i,j,klo) = h_zeta*T23;
1188 }
1189 #endif
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real one
Definition: ERF_Constants.H:9
constexpr amrex::Real fourth
Definition: ERF_Constants.H:14
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real OmegaFromW(int &i, int &j, int &k, amrex::Real w, const amrex::Array4< const amrex::Real > &u_arr, const amrex::Array4< const amrex::Real > &v_arr, const amrex::Array4< const amrex::Real > &mf_u, const amrex::Array4< const amrex::Real > &mf_v, const amrex::Array4< const amrex::Real > &z_nd, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxInv)
Definition: ERF_TerrainMetrics.H:780
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtEdgeCenterJ(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:598
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtEdgeCenterK(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:497
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtEdgeCenterJ(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:573
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtIface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:281
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtEdgeCenterJ(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:549
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtEdgeCenterK(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:474
void init_zlevels(amrex::Vector< amrex::Vector< amrex::Real >> &zlevels_stag, amrex::Vector< amrex::Vector< amrex::Real >> &stretched_dz_h, amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real >> &stretched_dz_d, amrex::Vector< amrex::Geometry > const &geom, amrex::Vector< amrex::IntVect > const &ref_ratio, const amrex::Real grid_stretching_ratio, const amrex::Real zsurf, const amrex::Real dz0)
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtEdgeCenterI(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:623
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_Zrel_AtCellCenter(const int &i, const int &j, const int &k, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:740
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtKface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:398
FineTerrain
Definition: ERF_TerrainMetrics.H:56
@ Transform
add the fine terrain as a correction that decays with height
@ None
caller has not prepared a fine mesh from the coarse level
@ Interpolate
use the coordinates interpolated from the coarse level as-is
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_Z_AtCellCenter(const int &i, const int &j, const int &k, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:694
FineTerrain which_fine_terrain()
Definition: ERF_TerrainMetrics.cpp:172
void init_which_terrain_grid(int lev, const amrex::Geometry &geom, amrex::MultiFab &z_phys_nd, amrex::Vector< amrex::Real > const &z_levels_h, FineTerrain fine_terrain=FineTerrain::None, amrex::MultiFab const *z_phys_interp=nullptr)
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void rotate_scalar_flux(const int &i, const int &j, const int &klo, const amrex::Real &flux, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxInv, const amrex::Array4< const amrex::Real > &zphys_arr, const amrex::Array4< amrex::Real > &phi1_arr, const amrex::Array4< amrex::Real > &phi2_arr, const amrex::Array4< amrex::Real > &phi3_arr)
Definition: ERF_TerrainMetrics.H:1035
void init_fine_terrain_grid(int lev, const amrex::Geometry &geom, amrex::MultiFab &z_phys_nd, amrex::Vector< amrex::Real > const &z_levels_h, FineTerrain fine_terrain, amrex::MultiFab const *z_phys_interp)
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtCellCenter(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:204
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtIface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:258
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void rotate_stress_tensor(const int &i, const int &j, const int &klo, const amrex::Real &flux, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxInv, const amrex::Array4< const amrex::Real > &zphys_arr, const amrex::Array4< const amrex::Real > &u_arr, const amrex::Array4< const amrex::Real > &v_arr, const amrex::Array4< const amrex::Real > &w_arr, const amrex::Array4< amrex::Real > &tau11_arr, const amrex::Array4< amrex::Real > &tau22_arr, const amrex::Array4< amrex::Real > &tau33_arr, const amrex::Array4< amrex::Real > &tau12_arr, const amrex::Array4< amrex::Real > &tau21_arr, const amrex::Array4< amrex::Real > &tau13_arr, const amrex::Array4< amrex::Real > &tau31_arr, const amrex::Array4< amrex::Real > &tau23_arr, const amrex::Array4< amrex::Real > &tau32_arr)
Definition: ERF_TerrainMetrics.H:1078
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtJface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:351
void update_stretched_dz(int lev, amrex::Vector< amrex::Vector< amrex::Real >> const &zlevels_stag, amrex::Vector< amrex::Vector< amrex::Real >> &stretched_dz_h, amrex::Vector< amrex::Gpu::DeviceVector< amrex::Real >> &stretched_dz_d)
void make_terrain_fitted_coords(int lev, const amrex::Geometry &geom, amrex::MultiFab &z_phys_nd, amrex::Vector< amrex::Real > const &z_levels_h, amrex::GpuArray< ERF_BC, AMREX_SPACEDIM *2 > &phys_bc_type, FineTerrain fine_terrain=FineTerrain::None, amrex::MultiFab const *z_phys_interp=nullptr)
amrex::Real get_dzmin_terrain(amrex::MultiFab &z_phys_nd)
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_Z_AtWFace(const int &i, const int &j, const int &k, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:718
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtKface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:422
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtCellCenter(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:229
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtEdgeCenterK(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:522
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtCellCenter(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:179
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtIface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:305
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtEdgeCenterI(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:647
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_zeta_AtJface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:328
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtJface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:374
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtEdgeCenterI(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:670
void init_default_zphys(int lev, const amrex::Geometry &geom, amrex::MultiFab &z_phys_nd, amrex::MultiFab &z_phys_cc, amrex::Real z_offset=zero)
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtKface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:445
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real WFromOmega(int &i, int &j, int &k, amrex::Real omega, const amrex::Array4< const amrex::Real > &u_arr, const amrex::Array4< const amrex::Real > &v_arr, const amrex::Array4< const amrex::Real > &mf_u, const amrex::Array4< const amrex::Real > &mf_v, const amrex::Array4< const amrex::Real > &z_nd, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxInv)
Definition: ERF_TerrainMetrics.H:845
@ omega
Definition: ERF_Morrison.H:54