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