ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
TerrainMetrics.cpp File Reference
#include <TerrainMetrics.H>
#include <AMReX_ParmParse.H>
#include <ERF_Constants.H>
#include <cmath>
Include dependency graph for TerrainMetrics.cpp:

Functions

void init_zlevels (Vector< Real > &zlevels_stag, const Geometry &geom, const Real grid_stretching_ratio, const Real zsurf, const Real dz0)
 
void init_terrain_grid (int lev, const Geometry &geom, MultiFab &z_phys_nd, Vector< Real > const &z_levels_h)
 
void make_J (const Geometry &geom, MultiFab &z_phys_nd, MultiFab &detJ_cc)
 
void make_areas (const Geometry &geom, MultiFab &z_phys_nd, MultiFab &ax, MultiFab &ay, MultiFab &az)
 
void make_zcc (const Geometry &geom, MultiFab &z_phys_nd, MultiFab &z_phys_cc)
 

Function Documentation

◆ init_terrain_grid()

void init_terrain_grid ( int  lev,
const Geometry &  geom,
MultiFab &  z_phys_nd,
Vector< Real > const &  z_levels_h 
)

Computation of the terrain grid from BTF, STF, or Sullivan TF model

NOTE: Multilevel is not yet working for either of these terrain-following coordinates, but (we think) the issue is deep in ERF and this code will work once the deeper problem is fixed. For now, make sure to run on a single level. -mmsanders

77 {
78  // z_nd is nodal in all directions
79  const Box& domain = geom.Domain();
80  int domlo_x = domain.smallEnd(0); int domhi_x = domain.bigEnd(0) + 1;
81  int domlo_y = domain.smallEnd(1); int domhi_y = domain.bigEnd(1) + 1;
82  int domlo_z = domain.smallEnd(2); int domhi_z = domain.bigEnd(2) + 1;
83  int nz = domain.length(2)+1; // staggered
84 
85  Real z_top = z_levels_h[nz-1];
86 
87  // User-selected method from inputs file (BTF default)
88  ParmParse pp("erf");
89  int terrain_smoothing = 0;
90  pp.query("terrain_smoothing", terrain_smoothing);
91 
92  if (lev > 0 && terrain_smoothing != 0) {
93  Abort("Must use terrain_smoothing = 0 when doing multilevel");
94  }
95 
96  Gpu::DeviceVector<Real> z_levels_d;
97  z_levels_d.resize(nz);
98  Gpu::copy(Gpu::hostToDevice, z_levels_h.begin(), z_levels_h.end(), z_levels_d.begin());
99 
100  // Number of ghost cells
101  int ngrow = z_phys_nd.nGrow();
102  IntVect ngrowVect = z_phys_nd.nGrowVect();
103 
104  int imin = domlo_x; if (geom.isPeriodic(0)) imin -= z_phys_nd.nGrowVect()[0];
105  int jmin = domlo_y; if (geom.isPeriodic(1)) jmin -= z_phys_nd.nGrowVect()[1];
106 
107  int imax = domhi_x; if (geom.isPeriodic(0)) imax += z_phys_nd.nGrowVect()[0];
108  int jmax = domhi_y; if (geom.isPeriodic(1)) jmax += z_phys_nd.nGrowVect()[1];
109 
110  switch(terrain_smoothing) {
111  case 0: // BTF Method
112  {
113  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
114  {
115  // Note that this box is nodal because it is based on z_phys_nd
116  const Box& bx = mfi.validbox();
117 
118  int k0 = bx.smallEnd()[2];
119 
120  // Grown box with corrected ghost cells at top
121  Box gbx = mfi.growntilebox(ngrowVect);
122 
123  if (bx.smallEnd(2) > domlo_z) {
124  gbx.growLo(2,-1);
125  }
126  if (bx.bigEnd(2) < domhi_z) {
127  gbx.growHi(2,-1);
128  }
129 
130  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
131  auto const& z_lev = z_levels_d.data();
132 
133  //
134  // Vertical grid stretching using BTF model from p2163 of Klemp2011
135  //
136  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
137  {
138  int ii = amrex::max(amrex::min(i,imax),imin);
139  int jj = amrex::max(amrex::min(j,jmax),jmin);
140 
141  //
142  // Start with flat z_lev set either with uniform cell size or specified z_levels
143  // If k0 = 0 then z_arr at k0 has already been filled from the terrain data
144  // If k0 > 0 then z_arr at k0 has already been filled from interpolation
145  //
146  Real z = z_lev[k];
147  Real z_sfc = z_arr(ii,jj,k0);
148  Real z_lev_sfc = z_lev[k0];
149 
150  z_arr(i,j,k) = ( (z_sfc - z_lev_sfc) * z_top +
151  (z_top - z_sfc ) * z ) / (z_top - z_lev_sfc);
152  });
153 
154  if (k0 == 0) {
155  // Fill lateral boundaries below the bottom surface
156  ParallelFor(makeSlab(gbx,2,0), [=] AMREX_GPU_DEVICE (int i, int j, int)
157  {
158  z_arr(i,j,-1) = 2.0*z_arr(i,j,0) - z_arr(i,j,1);
159  });
160  }
161  } // mfi
162 
163  break;
164  }
165 
166  case 1: // STF Method
167  {
168  // Get Multifab spanning domain with 1 level of ghost cells
169  MultiFab h_mf( z_phys_nd.boxArray(), z_phys_nd.DistributionMap(), 1, ngrow+1);
170  MultiFab h_mf_old(z_phys_nd.boxArray(), z_phys_nd.DistributionMap(), 1, ngrow+1);
171 
172  // Save max height for smoothing
173  Real max_h;
174 
175  // Crete 2D MF without allocation
176  MultiFab mf2d;
177  {
178  BoxList bl2d = h_mf.boxArray().boxList();
179  for (auto& b : bl2d) {
180  b.setRange(2,0);
181  }
182  BoxArray ba2d(std::move(bl2d));
183  mf2d = MultiFab(ba2d, h_mf.DistributionMap(), 1, ngrow, MFInfo().SetAlloc(false));
184  }
185 
186  // Get MultiArray4s from the multifabs
187  MultiArray4<Real> const& ma_h_s = h_mf.arrays();
188  MultiArray4<Real> const& ma_h_s_old = h_mf_old.arrays();
189  MultiArray4<Real> const& ma_z_phys = z_phys_nd.arrays();
190 
191  // Bottom boundary
192  int k0 = domlo_z;
193 
194  // Get max value
195  max_h = ParReduce(TypeList<ReduceOpMax>{}, TypeList<Real>{}, mf2d, IntVect(ngrow,ngrow,0),
196  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int) noexcept
197  -> GpuTuple<Real>
198  {
199  // Get Array4s
200  const auto & h = ma_h_s[box_no];
201  const auto & z_arr = ma_z_phys[box_no];
202 
203  int ii = amrex::max(amrex::min(i,imax),imin);
204  int jj = amrex::max(amrex::min(j,jmax),jmin);
205 
206  // Fill the lateral boundaries
207  z_arr(i,j,k0) = z_arr(ii,jj,k0);
208 
209  // Populate h with terrain
210  h(i,j,k0) = z_arr(i,j,k0);
211 
212  // Return height for max
213  return { z_arr(i,j,k0) };
214  });
215 
216  // Fill ghost cells (neglects domain boundary if not periodic)
217  h_mf.FillBoundary(geom.periodicity());
218 
219  // Make h_mf copy for old values
220  MultiFab::Copy(h_mf_old, h_mf,0,0,1,h_mf_old.nGrow());
221 
222  // Populate h_mf at k>0 with h_s, solving in ordered 2D slices
223  for (int k = domlo_z+1; k <= domhi_z; k++) // skip terrain level
224  {
225  auto const& z_lev_h = z_levels_h.data();
226 
227  Real zz = z_lev_h[k];
228  Real zz_minus = z_lev_h[k-1];
229 
230  Real gamma_m = 0.5; // min allowed fractional grid spacing
231  Real z_H = 2.44/(1-gamma_m);
232 
233  Real A;
234  Real foo = cos((PI/2)*(zz/z_H));
235  if(zz < z_H) { A = foo*foo*foo*foo*foo*foo; } // A controls rate of return to atm
236  else { A = 0; }
237  Real foo_minus = cos((PI/2)*(zz_minus/z_H));
238  Real A_minus;
239  if(zz_minus < z_H) { A_minus = foo_minus*foo_minus*foo_minus*foo_minus*foo_minus*foo_minus; } // A controls rate of return to atm
240  else { A_minus = 0; }
241 
242  unsigned maxIter = 50; // M_k in paper
243  unsigned iter = 0;
244  Real threshold = gamma_m;
245  Real diff = 1.e20;
246  while (iter < maxIter && diff > threshold)
247  {
248 
249  diff = ParReduce(TypeList<ReduceOpMin>{}, TypeList<Real>{}, mf2d, IntVect(ngrow,ngrow,0),
250  [=] AMREX_GPU_DEVICE (int box_no, int i, int j, int) noexcept
251  -> GpuTuple<Real>
252  {
253  const auto & h_s = ma_h_s[box_no];
254  const auto & h_s_old = ma_h_s_old[box_no];
255 
256  Real h_m = max_h; //high point of hill
257  Real beta_k = 0.2*std::min(zz/(2*h_m),1.0); //smoothing coefficient
258 
259  // Clip indices for ghost-cells
260  int ii = amrex::min(amrex::max(i,domlo_x),domhi_x);
261  int jj = amrex::min(amrex::max(j,domlo_y),domhi_y);
262 
263  if (iter == 0) {
264  h_s(i,j,k) = h_s_old(i,j,k-1) + beta_k*(h_s_old(ii+1,jj ,k-1)
265  + h_s_old(ii-1,jj ,k-1)
266  + h_s_old(ii ,jj+1,k-1)
267  + h_s_old(ii ,jj-1,k-1) - 4*h_s_old(ii,jj,k-1));
268  }
269  else {
270  h_s(i,j,k) = h_s_old(i,j,k ) + beta_k*(h_s_old(ii+1,jj ,k )
271  + h_s_old(ii-1,jj ,k )
272  + h_s_old(ii ,jj+1,k )
273  + h_s_old(ii ,jj-1,k ) - 4*h_s_old(ii,jj,k ));
274  }
275 
276  return { (zz + A * h_s(i,j,k) - (zz_minus + A_minus * h_s(i,j,k-1))) / (zz - zz_minus) };
277 
278  }); //ParReduce
279 
280  MultiFab::Copy(h_mf_old, h_mf,0,0,1,h_mf_old.nGrow());
281 
282  ParallelDescriptor::ReduceRealMin(diff);
283 
284  iter++;
285 
286  //fill ghost points
287  h_mf_old.FillBoundary(geom.periodicity());
288 
289  } //while
290 
291  auto const& z_lev_d = z_levels_d.data();
292 
293  //Populate z_phys_nd by solving z_arr(i,j,k) = z + A*h_s(i,j,k)
294  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
295  {
296  // Grown box with no z range
297  Box xybx = mfi.growntilebox(ngrow);
298  xybx.setRange(2,0);
299 
300  Array4<Real> const& h_s = h_mf_old.array(mfi);
301  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
302 
303  ParallelFor(xybx, [=] AMREX_GPU_DEVICE (int i, int j, int) {
304 
305  // Location of nodes
306  Real z = z_lev_d[k];
307 
308  // STF model from p2164 of Klemp2011
309  z_arr(i,j,k) = z + A*h_s(i,j,k);
310 
311  // Fill below the bottom surface
312  if (k == 1)
313  z_arr(i,j,k0-1) = 2.0*z_arr(i,j,k0) - z_arr(i,j,k);
314  });
315  }//mfi
316  } //k
317 
318  Gpu::streamSynchronize();
319 
320  break;
321  }
322 
323  case 2: // Sullivan TF Method
324  {
325  int k0 = 0;
326 
327  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
328  {
329  // Grown box with corrected ghost cells at top
330  Box gbx = mfi.growntilebox(ngrow);
331  gbx.setRange(2,domlo_z,domhi_z+1);
332 
333  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
334  auto const& z_lev = z_levels_d.data();
335 
336  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
337  {
338  // Vertical grid stretching
339  Real z = z_lev[k];
340 
341  int ii = amrex::max(amrex::min(i,imax),imin);
342  int jj = amrex::max(amrex::min(j,jmax),jmin);
343 
344  // Fill levels using model from Sullivan et. al. 2014
345  int omega = 3; //Used to adjust how rapidly grid lines level out. omega=1 is BTF!
346  z_arr(i,j,k) = z + (std::pow((1. - (z/z_top)),omega) * z_arr(ii,jj,k0));
347 
348  // Fill lateral boundaries and below the bottom surface
349  if (k == k0)
350  z_arr(i,j,k0 ) = z_arr(ii,jj,k0);
351  if (k == 1)
352  z_arr(i,j,k0-1) = 2.0*z_arr(ii,jj,k0) - z_arr(i,j,k);
353  });
354  }
355 
356  break;
357  }
358  case 3: // Debugging Test Method -- applies Sullivan TF starting at k = 1 so that domain does not change size
359  {
360  int k0 = 0;
361 
362  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
363  {
364  // Grown box with corrected ghost cells at top
365  Box gbx = mfi.growntilebox(ngrow);
366  gbx.setRange(2,domlo_z,domhi_z+1);
367 
368  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
369  auto const& z_lev = z_levels_d.data();
370 
371  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
372  {
373  // Vertical grid stretching
374  Real z = z_lev[k];
375 
376  int ii = amrex::max(amrex::min(i,imax),imin);
377  int jj = amrex::max(amrex::min(j,jmax),jmin);
378 
379  // Fill values outside the lateral boundaries and below the bottom surface (necessary if init_type = "real")
380  if (k == k0+1) {
381  z_arr(i,j,k) = z + z_arr(ii,jj,k0);
382  } else {
383  // Fill levels using model from Sullivan et. al. 2014
384  int omega = 3; //Used to adjust how rapidly grid lines level out. omega=1 is BTF!
385  z_arr(i,j,k) = z + (std::pow((1. - (z/z_top)),omega) * z_arr(ii,jj,k0));
386  }
387  });
388  gbx.setBig(2,0);
389  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k)
390  {
391  z_arr(i,j,k ) = 0.0;
392  z_arr(i,j,k-1) = -z_arr(i,j,k+1);
393  });
394  }
395 
396  break;
397  }
398  } //switch
399 
400  // Fill ghost layers and corners (including periodic)
401  z_phys_nd.FillBoundary(geom.periodicity());
402 
403 
404  //********************************************************************************
405  // Populate domain boundary cells in z-direction
406  //********************************************************************************
407  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
408  {
409  // Only set values above top of domain if this box reaches that far
410  Box nd_bx = mfi.tilebox();
411 
412  // Note that domhi_z is already nodal in the z-direction
413  if (nd_bx.bigEnd(2) >= domhi_z) {
414  // Grown box with no z range
415  Box xybx = mfi.growntilebox(ngrow);
416  xybx.setRange(2,0);
417  Array4<Real> const& z_arr = z_phys_nd.array(mfi);
418 
419  // Extrapolate top layer
420  ParallelFor(xybx, [=] AMREX_GPU_DEVICE (int i, int j, int ) {
421  z_arr(i,j,domhi_z+1) = 2.0*z_arr(i,j,domhi_z) - z_arr(i,j,domhi_z-1);
422  });
423  }
424  }
425 
426  /*
427  // Debug
428  for ( MFIter mfi(z_phys_nd, TilingIfNotGPU()); mfi.isValid(); ++mfi )
429  {
430  Box gbx = mfi.growntilebox(ngrow);
431  gbx.setRange(2,domlo_z,domhi_z+1);
432 
433  Array4<Real> z_arr = z_phys_nd.array(mfi);
434 
435  int rank = 0;
436  Print(rank) << "Debugging init_terrain_grid" << "\n";
437  Print(rank) << gbx << "\n";
438 
439  ParallelFor(gbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) {
440  Print(rank) << IntVect(i,j,k) << "\n";
441  Print(rank) << z_arr(i,j,k) << "\n";
442  Print(rank) << "\n";
443  });
444  Print(rank) << "Cleared..." << "\n";
445  }
446  */
447 }
constexpr amrex::Real PI
Definition: ERF_Constants.H:6
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real pp(amrex::Real y)
Definition: Microphysics_Utils.H:183
@ omega
Definition: SAM.H:49

Referenced by ERF::update_terrain_arrays().

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

◆ init_zlevels()

void init_zlevels ( Vector< Real > &  zlevels_stag,
const Geometry &  geom,
const Real  grid_stretching_ratio,
const Real  zsurf,
const Real  dz0 
)
14 {
15  auto dx = geom.CellSizeArray();
16  const Box& domain = geom.Domain();
17  int nz = domain.length(2)+1; // staggered
18  zlevels_stag.resize(nz);
19 
20  if (grid_stretching_ratio == 0) {
21  // This is the default for z_levels
22  for (int k = 0; k < nz; k++)
23  {
24  zlevels_stag[k] = k * dx[2];
25  }
26  } else {
27  // Create stretched grid based on initial dz and stretching ratio
28  zlevels_stag[0] = zsurf;
29  Real dz = dz0;
30  Print() << "Stretched grid levels: " << zsurf;
31  for (int k = 1; k < nz; k++)
32  {
33  zlevels_stag[k] = zlevels_stag[k-1] + dz;
34  Print() << " " << zlevels_stag[k];
35  dz *= grid_stretching_ratio;
36  }
37  Print() << std::endl;
38  }
39 
40  // Try reading in terrain_z_levels, which allows arbitrarily spaced grid
41  // levels to be specified and will take precedence over the
42  // grid_stretching_ratio parameter
43  ParmParse pp("erf");
44  int n_zlevels = pp.countval("terrain_z_levels");
45  if (n_zlevels > 0)
46  {
47  if (n_zlevels != nz) {
48  Print() << "You supplied " << n_zlevels << " staggered terrain_z_levels " << std::endl;
49  Print() << "but n_cell+1 in the z-direction is " << nz << std::endl;
50  Abort("You must specify a z_level for every value of k");
51  }
52 
53  if (grid_stretching_ratio > 0) {
54  Print() << "Note: Found terrain_z_levels, ignoring grid_stretching_ratio" << std::endl;
55  }
56 
57  pp.queryarr("terrain_z_levels", zlevels_stag, 0, nz);
58 
59  // These levels should range from 0 at the surface to the height of the
60  // top of model domain (see the coordinate surface height, zeta, in
61  // Klemp 2011)
62  AMREX_ALWAYS_ASSERT(zlevels_stag[0] == 0);
63  }
64 }

Referenced by ERF::ERF().

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

◆ make_areas()

void make_areas ( const Geometry &  geom,
MultiFab &  z_phys_nd,
MultiFab &  ax,
MultiFab &  ay,
MultiFab &  az 
)

Computation of area fractions on faces

494 {
495  const auto* dx = geom.CellSize();
496  Real dzInv = 1.0/dx[2];
497 
498  // Domain valid box (z_nd is nodal)
499  const Box& domain = geom.Domain();
500  int domlo_z = domain.smallEnd(2);
501 
502  // The z-faces are always full when using terrain-fitted coordinates
503  az.setVal(1.0);
504 
505  //
506  // x-areas
507  //
508 #ifdef _OPENMP
509 #pragma omp parallel if (Gpu::notInLaunchRegion())
510 #endif
511  for ( MFIter mfi(ax, TilingIfNotGPU()); mfi.isValid(); ++mfi )
512  {
513  Box gbx = mfi.growntilebox(ax.nGrow());
514  if (gbx.smallEnd(2) < domlo_z) {
515  gbx.setSmall(2,domlo_z);
516  }
517 
518  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
519  Array4<Real > ax_arr = ax.array(mfi);
520  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
521  ax_arr(i, j, k) = .5 * dzInv * (
522  z_nd(i,j,k+1) + z_nd(i,j+1,k+1) - z_nd(i,j,k) - z_nd(i,j+1,k));
523  });
524  }
525 
526  //
527  // y-areas
528  //
529 #ifdef _OPENMP
530 #pragma omp parallel if (Gpu::notInLaunchRegion())
531 #endif
532  for ( MFIter mfi(ay, TilingIfNotGPU()); mfi.isValid(); ++mfi )
533  {
534  Box gbx = mfi.growntilebox(ay.nGrow());
535  if (gbx.smallEnd(2) < domlo_z) {
536  gbx.setSmall(2,domlo_z);
537  }
538 
539  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
540  Array4<Real > ay_arr = ay.array(mfi);
541  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
542  ay_arr(i, j, k) = .5 * dzInv * (
543  z_nd(i,j,k+1) + z_nd(i+1,j,k+1) - z_nd(i,j,k) - z_nd(i+1,j,k));
544  });
545  }
546 
547  ax.FillBoundary(geom.periodicity());
548  ay.FillBoundary(geom.periodicity());
549  az.FillBoundary(geom.periodicity());
550 }

Referenced by ERF::update_terrain_arrays().

Here is the caller graph for this function:

◆ make_J()

void make_J ( const Geometry &  geom,
MultiFab &  z_phys_nd,
MultiFab &  detJ_cc 
)

Computation of detJ at cell-center

456 {
457  const auto *dx = geom.CellSize();
458  Real dzInv = 1.0/dx[2];
459 
460  // Domain valid box (z_nd is nodal)
461  const Box& domain = geom.Domain();
462  int domlo_z = domain.smallEnd(2);
463 
464  // Number of ghost cells
465  int ngrow= detJ_cc.nGrow();
466 
467 #ifdef _OPENMP
468 #pragma omp parallel if (Gpu::notInLaunchRegion())
469 #endif
470  for ( MFIter mfi(detJ_cc, TilingIfNotGPU()); mfi.isValid(); ++mfi )
471  {
472  Box gbx = mfi.growntilebox(ngrow);
473  if (gbx.smallEnd(2) < domlo_z) {
474  gbx.setSmall(2,domlo_z);
475  }
476  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
477  Array4<Real > detJ = detJ_cc.array(mfi);
478  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
479  detJ(i, j, k) = .25 * dzInv * (
480  z_nd(i,j,k+1) + z_nd(i+1,j,k+1) + z_nd(i,j+1,k+1) + z_nd(i+1,j+1,k+1)
481  -z_nd(i,j,k ) - z_nd(i+1,j,k ) - z_nd(i,j+1,k ) - z_nd(i+1,j+1,k ) );
482  });
483  }
484  detJ_cc.FillBoundary(geom.periodicity());
485 }

Referenced by ERF::update_terrain_arrays().

Here is the caller graph for this function:

◆ make_zcc()

void make_zcc ( const Geometry &  geom,
MultiFab &  z_phys_nd,
MultiFab &  z_phys_cc 
)

Computation of z_phys at cell-center

559 {
560  // Domain valid box (z_nd is nodal)
561  const Box& domain = geom.Domain();
562  int domlo_z = domain.smallEnd(2);
563 
564  // Number of ghost cells
565  int ngrow= z_phys_cc.nGrow();
566 
567 #ifdef _OPENMP
568 #pragma omp parallel if (Gpu::notInLaunchRegion())
569 #endif
570  for ( MFIter mfi(z_phys_cc, TilingIfNotGPU()); mfi.isValid(); ++mfi )
571  {
572  Box gbx = mfi.growntilebox(ngrow);
573  if (gbx.smallEnd(2) < domlo_z) {
574  gbx.setSmall(2,domlo_z);
575  }
576  Array4<Real const> z_nd = z_phys_nd.const_array(mfi);
577  Array4<Real > z_cc = z_phys_cc.array(mfi);
578  ParallelFor(gbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
579  z_cc(i, j, k) = .125 * ( z_nd(i,j,k ) + z_nd(i+1,j,k ) + z_nd(i,j+1,k ) + z_nd(i+1,j+1,k )
580  +z_nd(i,j,k+1) + z_nd(i+1,j,k+1) + z_nd(i,j+1,k+1) + z_nd(i+1,j+1,k+1) );
581  });
582  }
583  z_phys_cc.FillBoundary(geom.periodicity());
584 }

Referenced by ERF::post_timestep(), and ERF::update_terrain_arrays().

Here is the caller graph for this function: