ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_ScalarDiffusion.H
Go to the documentation of this file.
1 #ifndef ERF_SCALAR_DIFFUSION_H_
2 #define ERF_SCALAR_DIFFUSION_H_
3 
4 #include <type_traits>
5 
6 #include "AMReX_Box.H"
7 #include "AMReX_FArrayBox.H"
8 #include "AMReX_Gpu.H"
9 #include "ERF_DiffStruct.H"
10 #include "ERF_NumericalConstants.H"
11 #include "ERF_TerrainMetrics.H"
13 
14 /**
15  * Coefficients and state-layout mapping at the native ERF adapter boundary.
16  *
17  * The numerical routines below receive the scalar, density, flux, and RHS
18  * components independently. `molecular_coeff` is kinematic alpha for
19  * ConstantAlpha and density-weighted rhoAlpha for Constant. The density
20  * multiplication is selected by the host-side template specialization.
21  */
23 {
25  int eddy_h_comp = 0;
26  int eddy_v_comp = 0;
27 };
28 
29 static_assert(std::is_trivially_copyable_v<ScalarDiffusionCoefficients>);
30 
31 /** Scalar, density, RHS and flux views with independent component indices. */
33 {
34  amrex::Array4<const amrex::Real> scalar;
35  int scalar_comp = 0;
36  amrex::Array4<const amrex::Real> density;
37  int rho_comp = 0;
38  amrex::Array4<const amrex::Real> mu_turb;
39  amrex::Array4<amrex::Real> xflux;
40  amrex::Array4<amrex::Real> yflux;
41  amrex::Array4<amrex::Real> zflux;
42  int flux_comp = 0;
43  amrex::Array4<amrex::Real> rhs;
44  int rhs_comp = 0;
45 };
46 
47 /** Host-side coefficient form; dispatches to compile-time face kernels. */
49 {
50  bool molecular_is_kinematic = false;
51  bool add_turbulence = false;
52 };
53 
54 /** Explicit face fluxes supplied by an application-specific boundary model. */
56 {
57  bool use_x_on_side = false;
58  bool use_y_on_side = false;
59  bool use_z_on_side = false;
60  bool use_x_on_bottom_z = false;
61  bool use_y_on_bottom_z = false;
62  amrex::Array4<const amrex::Real> x;
63  amrex::Array4<const amrex::Real> y;
64  amrex::Array4<const amrex::Real> z;
65  int x_comp = 0;
66  int y_comp = 0;
67  int z_comp = 0;
68 };
69 
70 /** Optional raw vertical-flux diagnostic, written by the z-face kernel. */
72 {
73  bool enabled = false;
74  bool write_on_surface = false;
75  amrex::Array4<amrex::Real> z;
76  int z_comp = 0;
77 };
78 
79 /** Surface-layer faces that may select prescribed fluxes or zero flux. */
81 {
82  bool xlo = false;
83  bool xhi = false;
84  bool ylo = false;
85  bool yhi = false;
86  bool zlo = false;
87  bool zhi = false;
88 };
89 
90 /** Configuration shared by component-explicit face builders. */
92 {
98 };
99 
100 static_assert(std::is_trivially_copyable_v<ScalarDiffusionFieldViews>);
101 static_assert(std::is_trivially_copyable_v<ScalarDiffusionFluxPolicy>);
102 
103 /**
104  * Density-weighted face transport coefficient. The booleans are compile-time
105  * choices for ConstantAlpha/Constant/None and turbulent/no-turbulence modes;
106  * turbulent coefficients are already density weighted and are never
107  * multiplied by rho here.
108  *
109  * `rho_comp` and the eddy component are independent of the scalar component.
110  */
111 template <bool MultiplyMolecularByDensity, bool AddTurbulence>
112 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
114  const amrex::Array4<const amrex::Real>& rho,
115  const int rho_comp,
116  const amrex::Array4<const amrex::Real>& mu_turb,
117  const ScalarDiffusionCoefficients& coefficients,
118  const int i,
119  const int j,
120  const int k,
121  const int di,
122  const int dj,
123  const int dk,
124  const int eddy_comp) noexcept
125 {
126  amrex::Real coeff = coefficients.molecular_coeff;
127  if constexpr (MultiplyMolecularByDensity) {
128  coeff *= amrex::Real(0.5) *
129  (rho(i, j, k, rho_comp) + rho(i - di, j - dj, k - dk, rho_comp));
130  }
131  if constexpr (AddTurbulence) {
132  coeff += amrex::Real(0.5) * (mu_turb(i, j, k, eddy_comp) +
133  mu_turb(i - di, j - dj, k - dk, eddy_comp));
134  }
135  return coeff;
136 }
137 
138 /** Face-value one-sided derivative weights for an S-grid vertical boundary. */
140 {
142 };
143 
144 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE StretchedDiffusionBoundaryWeights
146  const amrex::Real dz_near, const amrex::Real dz_next) noexcept
147 {
148  const amrex::Real f = dz_next / dz_near + amrex::Real(2.0);
149  const amrex::Real f2 = f * f;
150  const amrex::Real c3 = amrex::Real(2.0) / (f - f2);
151  return {-(amrex::Real(1.0) - f2) * c3, -f2 * c3, c3};
152 }
153 
154 /**
155  * Compute the existing N-grid signed face flux, F^D=-K grad(chi). `scalar_comp`
156  * selects the intensive scalar and `flux_comp` is applied by the caller to its
157  * independent face-flux view. Horizontal map-factor numerator and denominator
158  * remain separate to preserve the native arithmetic association. Physical
159  * Dirichlet flags select the legacy one-sided stencil; ext_dir_upwind selection
160  * stays in the adapter.
161  */
162 template <int Direction>
163 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
165  const amrex::Array4<const amrex::Real>& scalar,
166  const int scalar_comp,
167  const int i,
168  const int j,
169  const int k,
170  const amrex::Real coeff,
171  const amrex::Real inv_spacing,
172  const amrex::Real map_numerator,
173  const amrex::Real map_denominator,
174  const bool ext_dir_lo,
175  const bool ext_dir_hi) noexcept
176 {
177  static_assert(Direction >= 0 && Direction < 3);
178  amrex::Real grad;
179  if constexpr (Direction == 0) {
180  if (ext_dir_lo) {
181  grad = -(amrex::Real(8.0) / amrex::Real(3.0)) *
182  scalar(i - 1, j, k, scalar_comp) +
183  amrex::Real(3.0) * scalar(i, j, k, scalar_comp) -
184  (amrex::Real(1.0) / amrex::Real(3.0)) *
185  scalar(i + 1, j, k, scalar_comp);
186  } else if (ext_dir_hi) {
187  grad =
188  (amrex::Real(8.0) / amrex::Real(3.0)) * scalar(i, j, k, scalar_comp) -
189  amrex::Real(3.0) * scalar(i - 1, j, k, scalar_comp) +
190  (amrex::Real(1.0) / amrex::Real(3.0)) *
191  scalar(i - 2, j, k, scalar_comp);
192  } else {
193  grad = scalar(i, j, k, scalar_comp) - scalar(i - 1, j, k, scalar_comp);
194  }
195  } else if constexpr (Direction == 1) {
196  if (ext_dir_lo) {
197  grad = -(amrex::Real(8.0) / amrex::Real(3.0)) *
198  scalar(i, j - 1, k, scalar_comp) +
199  amrex::Real(3.0) * scalar(i, j, k, scalar_comp) -
200  (amrex::Real(1.0) / amrex::Real(3.0)) *
201  scalar(i, j + 1, k, scalar_comp);
202  } else if (ext_dir_hi) {
203  grad =
204  (amrex::Real(8.0) / amrex::Real(3.0)) * scalar(i, j, k, scalar_comp) -
205  amrex::Real(3.0) * scalar(i, j - 1, k, scalar_comp) +
206  (amrex::Real(1.0) / amrex::Real(3.0)) *
207  scalar(i, j - 2, k, scalar_comp);
208  } else {
209  grad = scalar(i, j, k, scalar_comp) - scalar(i, j - 1, k, scalar_comp);
210  }
211  } else {
212  if (ext_dir_lo) {
213  grad = -(amrex::Real(8.0) / amrex::Real(3.0)) *
214  scalar(i, j, k - 1, scalar_comp) +
215  amrex::Real(3.0) * scalar(i, j, k, scalar_comp) -
216  (amrex::Real(1.0) / amrex::Real(3.0)) *
217  scalar(i, j, k + 1, scalar_comp);
218  } else if (ext_dir_hi) {
219  grad =
220  (amrex::Real(8.0) / amrex::Real(3.0)) * scalar(i, j, k, scalar_comp) -
221  amrex::Real(3.0) * scalar(i, j, k - 1, scalar_comp) +
222  (amrex::Real(1.0) / amrex::Real(3.0)) *
223  scalar(i, j, k - 2, scalar_comp);
224  } else {
225  grad = scalar(i, j, k, scalar_comp) - scalar(i, j, k - 1, scalar_comp);
226  }
227  }
228  if constexpr (Direction == 2) {
229  return -coeff * grad * inv_spacing;
230  } else {
231  return -coeff * grad * inv_spacing * map_numerator / map_denominator;
232  }
233 }
234 
235 /** Existing S-grid vertical-center gradient, including boundary and tile-edge
236  * spacing. */
237 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
239  const amrex::Array4<const amrex::Real>& scalar,
240  const int scalar_comp,
241  const int i,
242  const int j,
243  const int k,
244  const amrex::Real* dz,
245  const int klo,
246  const int khi,
247  const bool ext_dir_lo,
248  const bool ext_dir_hi) noexcept
249 {
250  if (ext_dir_lo) {
251  const auto w = StretchedDiffusionDirichletWeights(dz[k], dz[k + 1]);
252  return (amrex::Real(1.0) / dz[k]) *
253  (w.c1 * scalar(i, j, k - 1, scalar_comp) +
254  w.c2 * scalar(i, j, k, scalar_comp) +
255  w.c3 * scalar(i, j, k + 1, scalar_comp));
256  }
257  if (ext_dir_hi) {
258  const auto w = StretchedDiffusionDirichletWeights(dz[k - 1], dz[k - 2]);
259  return (amrex::Real(1.0) / dz[k - 1]) *
260  (-(
261  w.c1 * scalar(i, j, k, scalar_comp) +
262  w.c2 * scalar(i, j, k - 1, scalar_comp) +
263  w.c3 * scalar(i, j, k - 2, scalar_comp)));
264  }
265  const amrex::Real inv = (k == klo) ? amrex::Real(1.0) / dz[k]
266  : (k == khi + 1)
267  ? amrex::Real(1.0) / dz[k - 1]
268  : amrex::Real(2.0) / (dz[k] + dz[k - 1]);
269  return inv *
270  (scalar(i, j, k, scalar_comp) - scalar(i, j, k - 1, scalar_comp));
271 }
272 
273 /** Existing S-grid horizontal raw flux before its separate map-factor
274  * adjustment kernel. */
275 template <int Direction>
276 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
278  const amrex::Array4<const amrex::Real>& scalar,
279  const int scalar_comp,
280  const int i,
281  const int j,
282  const int k,
283  const amrex::Real coeff,
284  const amrex::Real inv_spacing,
285  const amrex::Real face_map) noexcept
286 {
287  static_assert(Direction == 0 || Direction == 1);
288  const amrex::Real delta =
289  Direction == 0
290  ? scalar(i, j, k, scalar_comp) - scalar(i - 1, j, k, scalar_comp)
291  : scalar(i, j, k, scalar_comp) - scalar(i, j - 1, k, scalar_comp);
292  const amrex::Real gradient = inv_spacing * delta;
293  return -coeff * face_map * gradient;
294 }
295 
296 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
298  const amrex::Real coeff,
299  const amrex::Real face_map,
300  const amrex::Real grad_xi,
301  const amrex::Real h_xi,
302  const amrex::Real grad_z) noexcept
303 {
304  return -coeff * face_map * (grad_xi - h_xi * grad_z);
305 }
306 
307 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
309  const amrex::Real coeff,
310  const amrex::Real face_map,
311  const amrex::Real grad_eta,
312  const amrex::Real h_eta,
313  const amrex::Real grad_z) noexcept
314 {
315  return -coeff * face_map * (grad_eta - h_eta * grad_z);
316 }
317 
318 /**
319  * Terrain x-face raw diffusion flux, including ERF's x-face map factor.
320  * `coeff` is the already selected effective face coefficient. Prescribed
321  * surface flux replacement is boundary policy and is intentionally outside
322  * this geometry primitive.
323  */
324 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
326  const amrex::Array4<const amrex::Real>& scalar,
327  const int scalar_comp,
328  const amrex::Real coeff,
329  const int i,
330  const int j,
331  const int k,
332  const amrex::Array4<const amrex::Real>& z_nd,
333  const amrex::Array4<const amrex::Real>& z_cc,
334  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
335  const amrex::Array4<const amrex::Real>& mf_ux) noexcept
336 {
337  const amrex::Real h_xi = Compute_h_xi_AtIface(i, j, k, cellSizeInv, z_nd);
338  const amrex::Real idz_hi =
339  amrex::Real(1.0) / (z_cc(i, j, k + 1) - z_cc(i, j, k - 1));
340  const amrex::Real idz_lo =
341  amrex::Real(1.0) / (z_cc(i - 1, j, k + 1) - z_cc(i - 1, j, k - 1));
342  const amrex::Real grad_z =
343  amrex::Real(0.5) * (scalar(i, j, k + 1, scalar_comp) * idz_hi +
344  scalar(i - 1, j, k + 1, scalar_comp) * idz_lo -
345  scalar(i, j, k - 1, scalar_comp) * idz_hi -
346  scalar(i - 1, j, k - 1, scalar_comp) * idz_lo);
347  const amrex::Real grad_xi =
348  cellSizeInv[0] *
349  (scalar(i, j, k, scalar_comp) - scalar(i - 1, j, k, scalar_comp));
350  return ScalarDiffusionFlux_Tx(coeff, mf_ux(i, j, 0), grad_xi, h_xi, grad_z);
351 }
352 
353 /** Terrain y-face raw diffusion flux, including ERF's y-face map factor. */
354 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
356  const amrex::Array4<const amrex::Real>& scalar,
357  const int scalar_comp,
358  const amrex::Real coeff,
359  const int i,
360  const int j,
361  const int k,
362  const amrex::Array4<const amrex::Real>& z_nd,
363  const amrex::Array4<const amrex::Real>& z_cc,
364  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
365  const amrex::Array4<const amrex::Real>& mf_vy) noexcept
366 {
367  const amrex::Real h_eta = Compute_h_eta_AtJface(i, j, k, cellSizeInv, z_nd);
368  const amrex::Real idz_hi =
369  amrex::Real(1.0) / (z_cc(i, j, k + 1) - z_cc(i, j, k - 1));
370  const amrex::Real idz_lo =
371  amrex::Real(1.0) / (z_cc(i, j - 1, k + 1) - z_cc(i, j - 1, k - 1));
372  const amrex::Real grad_z =
373  amrex::Real(0.5) * (scalar(i, j, k + 1, scalar_comp) * idz_hi +
374  scalar(i, j - 1, k + 1, scalar_comp) * idz_lo -
375  scalar(i, j, k - 1, scalar_comp) * idz_hi -
376  scalar(i, j - 1, k - 1, scalar_comp) * idz_lo);
377  const amrex::Real grad_eta =
378  cellSizeInv[1] *
379  (scalar(i, j, k, scalar_comp) - scalar(i, j - 1, k, scalar_comp));
380  return ScalarDiffusionFlux_Ty(coeff, mf_vy(i, j, 0), grad_eta, h_eta, grad_z);
381 }
382 
383 /**
384  * Terrain raw vertical diffusion flux at one k face. Dirichlet boundary
385  * stencils are selected explicitly by the caller; surface-layer replacement
386  * remains separate boundary policy.
387  */
388 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
390  const amrex::Array4<const amrex::Real>& scalar,
391  const int scalar_comp,
392  const amrex::Real coeff,
393  const int i,
394  const int j,
395  const int kface,
396  const amrex::Array4<const amrex::Real>& z_nd,
397  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
398  const bool ext_dir_lo,
399  const bool ext_dir_hi) noexcept
400 {
401  amrex::Real grad_z;
402  if (ext_dir_lo) {
403  const amrex::Real zm = Compute_Z_AtWFace(i, j, kface + 1, z_nd);
404  const amrex::Real dz0 = zm - Compute_Z_AtWFace(i, j, kface, z_nd);
405  const amrex::Real dz1 = Compute_Z_AtWFace(i, j, kface + 2, z_nd) - zm;
406  const amrex::Real idz0 = amrex::Real(1.0) / dz0;
407  const amrex::Real f = dz1 / dz0 + amrex::Real(2.0);
408  const amrex::Real f2 = f * f;
409  const amrex::Real c3 = amrex::Real(2.0) / (f - f2);
410  const amrex::Real c2 = -f2 * c3;
411  const amrex::Real c1 = -(amrex::Real(1.0) - f2) * c3;
412  grad_z = idz0 * (c1 * scalar(i, j, kface - 1, scalar_comp) +
413  c2 * scalar(i, j, kface, scalar_comp) +
414  c3 * scalar(i, j, kface + 1, scalar_comp));
415  } else if (ext_dir_hi) {
416  const amrex::Real zm = Compute_Z_AtWFace(i, j, kface - 1, z_nd);
417  const amrex::Real dz0 = Compute_Z_AtWFace(i, j, kface, z_nd) - zm;
418  const amrex::Real dz1 = zm - Compute_Z_AtWFace(i, j, kface - 2, z_nd);
419  const amrex::Real idz0 = amrex::Real(1.0) / dz0;
420  const amrex::Real f = dz1 / dz0 + amrex::Real(2.0);
421  const amrex::Real f2 = f * f;
422  const amrex::Real c3 = amrex::Real(2.0) / (f - f2);
423  const amrex::Real c2 = -f2 * c3;
424  const amrex::Real c1 = -(amrex::Real(1.0) - f2) * c3;
425  grad_z = idz0 * (-(
426  c1 * scalar(i, j, kface, scalar_comp) +
427  c2 * scalar(i, j, kface - 1, scalar_comp) +
428  c3 * scalar(i, j, kface - 2, scalar_comp)));
429  } else {
430  const amrex::Real h_zeta =
431  Compute_h_zeta_AtKface(i, j, kface, cellSizeInv, z_nd);
432  grad_z = (cellSizeInv[2] / h_zeta) * (scalar(i, j, kface, scalar_comp) -
433  scalar(i, j, kface - 1, scalar_comp));
434  }
435  return -coeff * grad_z;
436 }
437 
438 /**
439  * Raw terrain-coordinate F_z and its conservative transformed transfer are
440  * distinct quantities. G_z is the numerator
441  * F_z - m_x h_xi Fbar_x - m_y h_eta Fbar_y; T_z=G_z/(m_x m_y).
442  */
443 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
445  const amrex::Real raw_Fz,
446  const amrex::Real mx,
447  const amrex::Real h_xi,
448  const amrex::Real Fbar_x,
449  const amrex::Real my,
450  const amrex::Real h_eta,
451  const amrex::Real Fbar_y) noexcept
452 {
453  return raw_Fz - h_xi * mx * Fbar_x - h_eta * my * Fbar_y;
454 }
455 
456 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
458  const amrex::Real raw_Fx,
459  const amrex::Real h_zeta,
460  const amrex::Real my) noexcept
461 {
462  return raw_Fx * h_zeta / my;
463 }
464 
465 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
467  const amrex::Real raw_Fy,
468  const amrex::Real h_zeta,
469  const amrex::Real mx) noexcept
470 {
471  return raw_Fy * h_zeta / mx;
472 }
473 
474 /**
475  * Pointwise conversion to ERF's mapped conservative face-transfer convention.
476  * The transfer is an instantaneous rate and includes the supplied face-area
477  * measure; callers must not apply that measure again in the divergence.
478  */
479 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
481  const amrex::Real raw_flux,
482  const amrex::Real face_area) noexcept
483 {
484  return raw_flux * face_area;
485 }
486 
487 /** Convert a raw vertical diffusion flux to the mapped face-transfer space. */
488 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
490  const amrex::Real raw_flux,
491  const amrex::Real mx,
492  const amrex::Real my) noexcept
493 {
494  return raw_flux / (mx * my);
495 }
496 
497 /**
498  * Construct the native terrain numerator G_z at one vertical face. This
499  * helper owns x/y-face interpolation, physical-boundary extrapolation, and
500  * terrain cross terms; native divergence consumes G_z directly.
501  */
502 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
504  const int i,
505  const int j,
506  const int kface,
507  const amrex::Box& domain,
508  const amrex::Array4<const amrex::Real>& raw_xflux,
509  const amrex::Array4<const amrex::Real>& raw_yflux,
510  const amrex::Array4<const amrex::Real>& raw_zflux,
511  const int flux_comp,
512  const amrex::Array4<const amrex::Real>& z_nd,
513  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
514  const amrex::Array4<const amrex::Real>& mf_mx,
515  const amrex::Array4<const amrex::Real>& mf_my) noexcept
516 {
517  const int klo = domain.smallEnd(2);
518  const int khi = domain.bigEnd(2) + 1;
519  amrex::Real xbar, ybar;
520 
521  if (kface == klo) {
522  const amrex::Real xlo = myhalf * (raw_xflux(i, j, klo, flux_comp) +
523  raw_xflux(i + 1, j, klo, flux_comp));
524  const amrex::Real xhi = myhalf * (raw_xflux(i, j, klo + 1, flux_comp) +
525  raw_xflux(i + 1, j, klo + 1, flux_comp));
526  xbar = amrex::Real(1.5) * xlo - myhalf * xhi;
527  const amrex::Real ylo = myhalf * (raw_yflux(i, j, klo, flux_comp) +
528  raw_yflux(i, j + 1, klo, flux_comp));
529  const amrex::Real yhi = myhalf * (raw_yflux(i, j, klo + 1, flux_comp) +
530  raw_yflux(i, j + 1, klo + 1, flux_comp));
531  ybar = amrex::Real(1.5) * ylo - myhalf * yhi;
532  } else if (kface == khi) {
533  const amrex::Real xlo = myhalf * (raw_xflux(i, j, khi - 2, flux_comp) +
534  raw_xflux(i + 1, j, khi - 2, flux_comp));
535  const amrex::Real xhi = myhalf * (raw_xflux(i, j, khi - 1, flux_comp) +
536  raw_xflux(i + 1, j, khi - 1, flux_comp));
537  xbar = amrex::Real(1.5) * xhi - myhalf * xlo;
538  const amrex::Real ylo = myhalf * (raw_yflux(i, j, khi - 2, flux_comp) +
539  raw_yflux(i, j + 1, khi - 2, flux_comp));
540  const amrex::Real yhi = myhalf * (raw_yflux(i, j, khi - 1, flux_comp) +
541  raw_yflux(i, j + 1, khi - 1, flux_comp));
542  ybar = amrex::Real(1.5) * yhi - myhalf * ylo;
543  } else {
544  xbar = fourth * (raw_xflux(i, j, kface, flux_comp) +
545  raw_xflux(i + 1, j, kface, flux_comp) +
546  raw_xflux(i, j, kface - 1, flux_comp) +
547  raw_xflux(i + 1, j, kface - 1, flux_comp));
548  ybar = fourth * (raw_yflux(i, j, kface, flux_comp) +
549  raw_yflux(i, j + 1, kface, flux_comp) +
550  raw_yflux(i, j, kface - 1, flux_comp) +
551  raw_yflux(i, j + 1, kface - 1, flux_comp));
552  }
553 
554  const amrex::Real mx = mf_mx(i, j, 0);
555  const amrex::Real my = mf_my(i, j, 0);
556  const amrex::Real h_xi = Compute_h_xi_AtKface(i, j, kface, cellSizeInv, z_nd);
557  const amrex::Real h_eta =
558  Compute_h_eta_AtKface(i, j, kface, cellSizeInv, z_nd);
559  const amrex::Real Gz = TerrainDiffusionGz(
560  raw_zflux(i, j, kface, flux_comp), mx, h_xi, xbar, my, h_eta, ybar);
561  return Gz;
562 }
563 
564 /**
565  * Complete terrain vertical transfer at one k face. Terrain interpolation,
566  * boundary extrapolation and cross terms remain owned by the Gz helper above.
567  * Set suppress_transfer when the caller's resolved boundary policy suppresses
568  * the complete lower terrain-normal transfer.
569  *
570  * At an interior k face, raw_xflux and raw_yflux must be valid at kface and
571  * kface-1. At the physical bottom or top face, the authoritative Gz helper
572  * uses one-sided extrapolation and needs no out-of-domain z layer.
573  */
574 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
576  const int i,
577  const int j,
578  const int kface,
579  const amrex::Box& domain,
580  const amrex::Array4<const amrex::Real>& raw_xflux,
581  const amrex::Array4<const amrex::Real>& raw_yflux,
582  const amrex::Array4<const amrex::Real>& raw_zflux,
583  const int flux_comp,
584  const amrex::Array4<const amrex::Real>& z_nd,
585  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
586  const amrex::Array4<const amrex::Real>& mf_mx,
587  const amrex::Array4<const amrex::Real>& mf_my,
588  const bool suppress_transfer = false) noexcept
589 {
590  if (suppress_transfer) return amrex::Real(0.0);
592  i, j, kface, domain, raw_xflux, raw_yflux, raw_zflux, flux_comp, z_nd,
593  cellSizeInv, mf_mx, mf_my);
595  Gz, mf_mx(i, j, 0), mf_my(i, j, 0));
596 }
597 
598 /**
599  * Return the positive geometry-weighted mapped flux divergence. For
600  * detJ_cell <= 0, return zero without dividing. The diffusion array wrapper
601  * subtracts this value from the existing RHS.
602  */
603 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
605  const amrex::Real fx_hi,
606  const amrex::Real fx_lo,
607  const amrex::Real fy_hi,
608  const amrex::Real fy_lo,
609  const amrex::Real fz_hi,
610  const amrex::Real fz_lo,
611  const amrex::Real dx_inv,
612  const amrex::Real dy_inv,
613  const amrex::Real dzeta_inv,
614  const amrex::Real mx,
615  const amrex::Real my,
616  const amrex::Real detJ_cell) noexcept
617 {
618  if (detJ_cell <= amrex::Real(0.0))
619  return amrex::Real(0.0);
620  const amrex::Real map_over_jacobian = (mx * my) / detJ_cell;
621  return map_over_jacobian *
622  ((fx_hi - fx_lo) * dx_inv + (fy_hi - fy_lo) * dy_inv +
623  (fz_hi - fz_lo) * dzeta_inv);
624 }
625 
626 /** Native terrain divergence in the legacy arithmetic association. */
627 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real
629  const amrex::Real mapped_Fx_hi,
630  const amrex::Real mapped_Fx_lo,
631  const amrex::Real mapped_Fy_hi,
632  const amrex::Real mapped_Fy_lo,
633  const amrex::Real Gz_hi,
634  const amrex::Real Gz_lo,
635  const amrex::Real dx_inv,
636  const amrex::Real dy_inv,
637  const amrex::Real dz_inv,
638  const amrex::Real mfsq,
639  const amrex::Real detJ_cell) noexcept
640 {
641  amrex::Real stateContrib =
642  (mapped_Fx_hi - mapped_Fx_lo) * dx_inv * mfsq +
643  (mapped_Fy_hi - mapped_Fy_lo) * dy_inv * mfsq +
644  (Gz_hi - Gz_lo) * dz_inv;
645  stateContrib /= detJ_cell;
646  return stateContrib;
647 }
648 
649 /**
650  * Component-explicit N-grid face construction. Boundary-condition selection,
651  * map factors, prescribed surface fluxes and raw-flux diagnostics are explicit
652  * inputs; this operation does not inspect native conserved-state identity.
653  */
654 template <bool MultiplyMolecularByDensity, bool AddTurbulence>
655 inline void
657  const amrex::Box& bx,
658  const amrex::Box& domain,
659  const ScalarDiffusionFieldViews& field,
660  const ScalarDiffusionFluxPolicy& policy,
661  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
662  const amrex::Array4<const amrex::Real>& u,
663  const amrex::Array4<const amrex::Real>& v,
664  const amrex::Array4<const amrex::Real>& mf_ux,
665  const amrex::Array4<const amrex::Real>& mf_uy,
666  const amrex::Array4<const amrex::Real>& mf_vx,
667  const amrex::Array4<const amrex::Real>& mf_vy,
668  const amrex::BCRec* bc_ptr,
669  const int bc_comp)
670 {
671  using namespace amrex;
672  const auto dom_lo = lbound(domain);
673  const auto dom_hi = ubound(domain);
674  const Box xbx = surroundingNodes(bx, 0);
675  const Box ybx = surroundingNodes(bx, 1);
676  const Box zbx = surroundingNodes(bx, 2);
677  const Real dx_inv = cellSizeInv[0];
678  const Real dy_inv = cellSizeInv[1];
679  const Real dz_inv = cellSizeInv[2];
680  const auto coefficients = policy.coefficients;
681  const auto prescribed = policy.prescribed;
682  const auto diagnostic = policy.diagnostic;
683  const auto surface = policy.surface;
684  const int scalar_comp = field.scalar_comp;
685  const int rho_comp = field.rho_comp;
686  const int flux_comp = field.flux_comp;
687  const auto scalar = field.scalar;
688  const auto density = field.density;
689  const auto mu_turb = field.mu_turb;
690  const auto xflux = field.xflux;
691  const auto yflux = field.yflux;
692  const auto zflux = field.zflux;
693 
694  ParallelFor(xbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
695  const Real coeff = ScalarDiffusionFaceCoefficient<
696  MultiplyMolecularByDensity, AddTurbulence>(
697  density, rho_comp, mu_turb, coefficients, i, j, k, 1, 0, 0,
698  coefficients.eddy_h_comp);
699  const bool ext_lo =
700  ((bc_ptr[bc_comp].lo(0) == ERFBCType::ext_dir) ||
701  (bc_ptr[bc_comp].lo(0) == ERFBCType::ext_dir_prim) ||
702  (bc_ptr[bc_comp].lo(0) == ERFBCType::ext_dir_upwind &&
703  u(dom_lo.x, j, k) >= Real(0.0))) &&
704  i == dom_lo.x;
705  const bool ext_hi =
706  ((bc_ptr[bc_comp].hi(0) == ERFBCType::ext_dir) ||
707  (bc_ptr[bc_comp].hi(0) == ERFBCType::ext_dir_prim) ||
708  (bc_ptr[bc_comp].hi(0) == ERFBCType::ext_dir_upwind &&
709  u(dom_hi.x + 1, j, k) <= Real(0.0))) &&
710  i == dom_hi.x + 1;
711  const bool on_surface =
712  (surface.xlo && i == dom_lo.x) ||
713  (surface.xhi && i == dom_hi.x + 1);
714 
715  if (ext_lo) {
716  xflux(i, j, k, flux_comp) = ScalarDiffusionFlux_N<0>(
717  scalar, scalar_comp, i, j, k, coeff, dx_inv,
718  mf_ux(i, j, 0), mf_uy(i, j, 0), true, false);
719  } else if (ext_hi) {
720  xflux(i, j, k, flux_comp) = ScalarDiffusionFlux_N<0>(
721  scalar, scalar_comp, i, j, k, coeff, dx_inv,
722  mf_ux(i, j, 0), mf_uy(i, j, 0), false, true);
723  } else if (on_surface && prescribed.use_x_on_side) {
724  xflux(i, j, k, flux_comp) = prescribed.x(i, j, k, prescribed.x_comp);
725  } else {
726  xflux(i, j, k, flux_comp) = ScalarDiffusionFlux_N<0>(
727  scalar, scalar_comp, i, j, k, coeff, dx_inv,
728  mf_ux(i, j, 0), mf_uy(i, j, 0), false, false);
729  }
730  });
731 
732  ParallelFor(ybx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
733  const Real coeff = ScalarDiffusionFaceCoefficient<
734  MultiplyMolecularByDensity, AddTurbulence>(
735  density, rho_comp, mu_turb, coefficients, i, j, k, 0, 1, 0,
736  coefficients.eddy_h_comp);
737  const bool ext_lo =
738  ((bc_ptr[bc_comp].lo(1) == ERFBCType::ext_dir) ||
739  (bc_ptr[bc_comp].lo(1) == ERFBCType::ext_dir_prim) ||
740  (bc_ptr[bc_comp].lo(1) == ERFBCType::ext_dir_upwind &&
741  v(i, dom_lo.y, k) >= Real(0.0))) &&
742  j == dom_lo.y;
743  const bool ext_hi =
744  ((bc_ptr[bc_comp].hi(1) == ERFBCType::ext_dir) ||
745  (bc_ptr[bc_comp].hi(1) == ERFBCType::ext_dir_prim) ||
746  (bc_ptr[bc_comp].hi(1) == ERFBCType::ext_dir_upwind &&
747  v(i, dom_hi.y + 1, k) <= Real(0.0))) &&
748  j == dom_hi.y + 1;
749  const bool on_surface =
750  (surface.ylo && j == dom_lo.y) ||
751  (surface.yhi && j == dom_hi.y + 1);
752 
753  if (ext_lo) {
754  yflux(i, j, k, flux_comp) = ScalarDiffusionFlux_N<1>(
755  scalar, scalar_comp, i, j, k, coeff, dy_inv,
756  mf_vy(i, j, 0), mf_vx(i, j, 0), true, false);
757  } else if (ext_hi) {
758  yflux(i, j, k, flux_comp) = ScalarDiffusionFlux_N<1>(
759  scalar, scalar_comp, i, j, k, coeff, dy_inv,
760  mf_vy(i, j, 0), mf_vx(i, j, 0), false, true);
761  } else if (on_surface && prescribed.use_y_on_side) {
762  yflux(i, j, k, flux_comp) = prescribed.y(i, j, k, prescribed.y_comp);
763  } else {
764  yflux(i, j, k, flux_comp) = ScalarDiffusionFlux_N<1>(
765  scalar, scalar_comp, i, j, k, coeff, dy_inv,
766  mf_vy(i, j, 0), mf_vx(i, j, 0), false, false);
767  }
768  });
769 
770  ParallelFor(zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
771  const Real coeff = ScalarDiffusionFaceCoefficient<
772  MultiplyMolecularByDensity, AddTurbulence>(
773  density, rho_comp, mu_turb, coefficients, i, j, k, 0, 0, 1,
774  coefficients.eddy_v_comp);
775  const bool ext_lo =
776  ((bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir) ||
777  (bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir_prim)) &&
778  k == dom_lo.z;
779  const bool ext_hi =
780  ((bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir) ||
781  (bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir_prim)) &&
782  k == dom_hi.z + 1;
783  const bool on_surface =
784  (surface.zlo && k == dom_lo.z) ||
785  (surface.zhi && k == dom_hi.z + 1);
786 
787  if (ext_lo) {
788  zflux(i, j, k, flux_comp) = ScalarDiffusionFlux_N<2>(
789  scalar, scalar_comp, i, j, k, coeff, dz_inv, Real(1.0), Real(1.0),
790  true, false);
791  } else if (ext_hi) {
792  zflux(i, j, k, flux_comp) = ScalarDiffusionFlux_N<2>(
793  scalar, scalar_comp, i, j, k, coeff, dz_inv, Real(1.0), Real(1.0),
794  false, true);
795  } else if (on_surface && prescribed.use_z_on_side) {
796  zflux(i, j, k, flux_comp) = prescribed.z(i, j, k, prescribed.z_comp);
797  } else if (on_surface) {
798  zflux(i, j, k, flux_comp) = Real(0.0);
799  } else {
800  zflux(i, j, k, flux_comp) = ScalarDiffusionFlux_N<2>(
801  scalar, scalar_comp, i, j, k, coeff, dz_inv, Real(1.0), Real(1.0),
802  false, false);
803  }
804 
805  if (diagnostic.enabled && (diagnostic.write_on_surface || !on_surface)) {
806  diagnostic.z(i, j, k, diagnostic.z_comp) = zflux(i, j, k, flux_comp);
807  }
808  });
809 }
810 
811 /** Build all native N-grid face fluxes for any intensive scalar field. */
812 inline void
814  const amrex::Box& bx,
815  const amrex::Box& domain,
816  const ScalarDiffusionFieldViews& field,
817  const ScalarDiffusionFluxPolicy& policy,
818  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
819  const amrex::Array4<const amrex::Real>& u,
820  const amrex::Array4<const amrex::Real>& v,
821  const amrex::Array4<const amrex::Real>& mf_ux,
822  const amrex::Array4<const amrex::Real>& mf_uy,
823  const amrex::Array4<const amrex::Real>& mf_vx,
824  const amrex::Array4<const amrex::Real>& mf_vy,
825  const amrex::BCRec* bc_ptr,
826  const int bc_comp)
827 {
828  const auto mode = policy.coefficient_mode;
829  if (mode.molecular_is_kinematic) {
830  if (mode.add_turbulence) {
831  BuildScalarDiffusionFluxes_N_Impl<true, true>(
832  bx, domain, field, policy, cellSizeInv, u, v, mf_ux, mf_uy, mf_vx,
833  mf_vy, bc_ptr, bc_comp);
834  } else {
835  BuildScalarDiffusionFluxes_N_Impl<true, false>(
836  bx, domain, field, policy, cellSizeInv, u, v, mf_ux, mf_uy, mf_vx,
837  mf_vy, bc_ptr, bc_comp);
838  }
839  } else if (mode.add_turbulence) {
840  BuildScalarDiffusionFluxes_N_Impl<false, true>(
841  bx, domain, field, policy, cellSizeInv, u, v, mf_ux, mf_uy, mf_vx,
842  mf_vy, bc_ptr, bc_comp);
843  } else {
844  BuildScalarDiffusionFluxes_N_Impl<false, false>(
845  bx, domain, field, policy, cellSizeInv, u, v, mf_ux, mf_uy, mf_vx,
846  mf_vy, bc_ptr, bc_comp);
847  }
848 }
849 
850 template <bool MultiplyMolecularByDensity, bool AddTurbulence>
851 inline void
853  const amrex::Box& bx,
854  const amrex::Box& domain,
855  const ScalarDiffusionFieldViews& field,
856  const ScalarDiffusionFluxPolicy& policy,
857  const amrex::Real dx_inv,
858  const amrex::Real dy_inv,
859  const amrex::Real* dz,
860  const int klo,
861  const int khi,
862  const amrex::Array4<const amrex::Real>& mf_ux,
863  const amrex::Array4<const amrex::Real>& mf_uy,
864  const amrex::Array4<const amrex::Real>& mf_vx,
865  const amrex::Array4<const amrex::Real>& mf_vy,
866  const amrex::BCRec* bc_ptr,
867  const int bc_comp)
868 {
869  using namespace amrex;
870  const auto dom_lo = lbound(domain);
871  const auto dom_hi = ubound(domain);
872  const Box xbx = surroundingNodes(bx, 0);
873  const Box ybx = surroundingNodes(bx, 1);
874  const Box zbx = surroundingNodes(bx, 2);
875  const auto coefficients = policy.coefficients;
876  const auto prescribed = policy.prescribed;
877  const auto diagnostic = policy.diagnostic;
878  const auto surface = policy.surface;
879  const int scalar_comp = field.scalar_comp;
880  const int rho_comp = field.rho_comp;
881  const int flux_comp = field.flux_comp;
882  const auto scalar = field.scalar;
883  const auto density = field.density;
884  const auto mu_turb = field.mu_turb;
885  const auto xflux = field.xflux;
886  const auto yflux = field.yflux;
887  const auto zflux = field.zflux;
888 
889  ParallelFor(xbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
890  const Real coeff = ScalarDiffusionFaceCoefficient<
891  MultiplyMolecularByDensity, AddTurbulence>(
892  density, rho_comp, mu_turb, coefficients, i, j, k, 1, 0, 0,
893  coefficients.eddy_h_comp);
894  const bool on_surface =
895  (surface.xlo && i == dom_lo.x) ||
896  (surface.xhi && i == dom_hi.x + 1);
897  if (on_surface && prescribed.use_x_on_side) {
898  xflux(i, j, k, flux_comp) = prescribed.x(i, j, k, prescribed.x_comp);
899  } else {
900  xflux(i, j, k, flux_comp) = ScalarDiffusionFlux_S_Horizontal<0>(
901  scalar, scalar_comp, i, j, k, coeff, dx_inv, mf_ux(i, j, 0));
902  }
903  });
904 
905  ParallelFor(ybx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
906  const Real coeff = ScalarDiffusionFaceCoefficient<
907  MultiplyMolecularByDensity, AddTurbulence>(
908  density, rho_comp, mu_turb, coefficients, i, j, k, 0, 1, 0,
909  coefficients.eddy_h_comp);
910  const bool on_surface =
911  (surface.ylo && j == dom_lo.y) ||
912  (surface.yhi && j == dom_hi.y + 1);
913  if (on_surface && prescribed.use_y_on_side) {
914  yflux(i, j, k, flux_comp) = prescribed.y(i, j, k, prescribed.y_comp);
915  } else {
916  yflux(i, j, k, flux_comp) = ScalarDiffusionFlux_S_Horizontal<1>(
917  scalar, scalar_comp, i, j, k, coeff, dy_inv, mf_vy(i, j, 0));
918  }
919  });
920 
921  ParallelFor(zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
922  const Real coeff = ScalarDiffusionFaceCoefficient<
923  MultiplyMolecularByDensity, AddTurbulence>(
924  density, rho_comp, mu_turb, coefficients, i, j, k, 0, 0, 1,
925  coefficients.eddy_v_comp);
926  const bool ext_lo =
927  ((bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir) ||
928  (bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir_prim)) &&
929  k == dom_lo.z;
930  const bool ext_hi =
931  ((bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir) ||
932  (bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir_prim)) &&
933  k == dom_hi.z + 1;
934  const bool on_surface =
935  (surface.zlo && k == dom_lo.z) ||
936  (surface.zhi && k == dom_hi.z + 1);
937  const Real grad = StretchedScalarGradient(
938  scalar, scalar_comp, i, j, k, dz, klo, khi, ext_lo, ext_hi);
939  if (on_surface && prescribed.use_z_on_side) {
940  zflux(i, j, k, flux_comp) = prescribed.z(i, j, k, prescribed.z_comp);
941  } else if (on_surface) {
942  zflux(i, j, k, flux_comp) = Real(0.0);
943  } else {
944  zflux(i, j, k, flux_comp) = -coeff * grad;
945  }
946  if (diagnostic.enabled && (diagnostic.write_on_surface || !on_surface)) {
947  diagnostic.z(i, j, k, diagnostic.z_comp) = zflux(i, j, k, flux_comp);
948  }
949  });
950 
951  // S-grid raw horizontal fluxes are adjusted in the same separate kernels as
952  // the native implementation before scaling or applying divergence.
953  ParallelFor(xbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
954  xflux(i, j, k, flux_comp) /= mf_uy(i, j, 0);
955  });
956  ParallelFor(ybx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
957  yflux(i, j, k, flux_comp) /= mf_vx(i, j, 0);
958  });
959 }
960 
961 /** Build and map-adjust all native S-grid face fluxes for an intensive field. */
962 inline void
964  const amrex::Box& bx,
965  const amrex::Box& domain,
966  const ScalarDiffusionFieldViews& field,
967  const ScalarDiffusionFluxPolicy& policy,
968  const amrex::Real dx_inv,
969  const amrex::Real dy_inv,
970  const amrex::Real* dz,
971  const int klo,
972  const int khi,
973  const amrex::Array4<const amrex::Real>& mf_ux,
974  const amrex::Array4<const amrex::Real>& mf_uy,
975  const amrex::Array4<const amrex::Real>& mf_vx,
976  const amrex::Array4<const amrex::Real>& mf_vy,
977  const amrex::BCRec* bc_ptr,
978  const int bc_comp)
979 {
980  const auto mode = policy.coefficient_mode;
981  if (mode.molecular_is_kinematic) {
982  if (mode.add_turbulence) {
983  BuildScalarDiffusionFluxes_S_Impl<true, true>(
984  bx, domain, field, policy, dx_inv, dy_inv, dz, klo, khi, mf_ux,
985  mf_uy, mf_vx, mf_vy, bc_ptr, bc_comp);
986  } else {
987  BuildScalarDiffusionFluxes_S_Impl<true, false>(
988  bx, domain, field, policy, dx_inv, dy_inv, dz, klo, khi, mf_ux,
989  mf_uy, mf_vx, mf_vy, bc_ptr, bc_comp);
990  }
991  } else if (mode.add_turbulence) {
992  BuildScalarDiffusionFluxes_S_Impl<false, true>(
993  bx, domain, field, policy, dx_inv, dy_inv, dz, klo, khi, mf_ux,
994  mf_uy, mf_vx, mf_vy, bc_ptr, bc_comp);
995  } else {
996  BuildScalarDiffusionFluxes_S_Impl<false, false>(
997  bx, domain, field, policy, dx_inv, dy_inv, dz, klo, khi, mf_ux,
998  mf_uy, mf_vx, mf_vy, bc_ptr, bc_comp);
999  }
1000 }
1001 
1002 template <bool MultiplyMolecularByDensity, bool AddTurbulence>
1003 inline void
1005  const amrex::Box& bx,
1006  const amrex::Box& domain,
1007  const ScalarDiffusionFieldViews& field,
1008  const ScalarDiffusionFluxPolicy& policy,
1009  const bool rotate,
1010  const amrex::Array4<const amrex::Real>& z_nd,
1011  const amrex::Array4<const amrex::Real>& z_cc,
1012  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
1013  const amrex::Array4<const amrex::Real>& mf_ux,
1014  const amrex::Array4<const amrex::Real>& mf_vy,
1015  const amrex::BCRec* bc_ptr,
1016  const int bc_comp)
1017 {
1018  using namespace amrex;
1019  const auto dom_lo = lbound(domain);
1020  const auto dom_hi = ubound(domain);
1021  Box xbx = surroundingNodes(bx, 0);
1022  Box ybx = surroundingNodes(bx, 1);
1023  const Box zbx = surroundingNodes(bx, 2);
1024  if (xbx.smallEnd(2) != dom_lo.z) xbx.growLo(2, 1);
1025  if (ybx.smallEnd(2) != dom_lo.z) ybx.growLo(2, 1);
1026  if (xbx.bigEnd(2) != dom_hi.z) xbx.growHi(2, 1);
1027  if (ybx.bigEnd(2) != dom_hi.z) ybx.growHi(2, 1);
1028 
1029  const auto coefficients = policy.coefficients;
1030  const auto prescribed = policy.prescribed;
1031  const auto diagnostic = policy.diagnostic;
1032  const auto surface = policy.surface;
1033  const int scalar_comp = field.scalar_comp;
1034  const int rho_comp = field.rho_comp;
1035  const int flux_comp = field.flux_comp;
1036  const auto scalar = field.scalar;
1037  const auto density = field.density;
1038  const auto mu_turb = field.mu_turb;
1039  const auto xflux = field.xflux;
1040  const auto yflux = field.yflux;
1041  const auto zflux = field.zflux;
1042 
1043  ParallelFor(xbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1044  const Real coeff = ScalarDiffusionFaceCoefficient<
1045  MultiplyMolecularByDensity, AddTurbulence>(
1046  density, rho_comp, mu_turb, coefficients, i, j, k, 1, 0, 0,
1047  coefficients.eddy_h_comp);
1048  const bool on_side =
1049  (surface.xlo && i == dom_lo.x) ||
1050  (surface.xhi && i == dom_hi.x + 1);
1051  const bool on_bottom_z =
1052  surface.zlo && rotate && k == dom_lo.z;
1053 
1054  if (on_side && prescribed.use_x_on_side) {
1055  xflux(i, j, k, flux_comp) = prescribed.x(i, j, k, prescribed.x_comp);
1056  } else if (on_bottom_z && prescribed.use_x_on_bottom_z) {
1057  xflux(i, j, k, flux_comp) = prescribed.x(i, j, 0, prescribed.x_comp);
1058  } else {
1059  xflux(i, j, k, flux_comp) = TerrainScalarDiffusionFluxAtIFace(
1060  scalar, scalar_comp, coeff, i, j, k, z_nd, z_cc, cellSizeInv, mf_ux);
1061  }
1062  });
1063 
1064  ParallelFor(ybx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1065  const Real coeff = ScalarDiffusionFaceCoefficient<
1066  MultiplyMolecularByDensity, AddTurbulence>(
1067  density, rho_comp, mu_turb, coefficients, i, j, k, 0, 1, 0,
1068  coefficients.eddy_h_comp);
1069  const bool on_side =
1070  (surface.ylo && j == dom_lo.y) ||
1071  (surface.yhi && j == dom_hi.y + 1);
1072  const bool on_bottom_z =
1073  surface.zlo && rotate && k == dom_lo.z;
1074 
1075  if (on_side && prescribed.use_y_on_side) {
1076  yflux(i, j, k, flux_comp) = prescribed.y(i, j, k, prescribed.y_comp);
1077  } else if (on_bottom_z && prescribed.use_y_on_bottom_z) {
1078  yflux(i, j, k, flux_comp) = prescribed.y(i, j, 0, prescribed.y_comp);
1079  } else {
1080  yflux(i, j, k, flux_comp) = TerrainScalarDiffusionFluxAtJFace(
1081  scalar, scalar_comp, coeff, i, j, k, z_nd, z_cc, cellSizeInv, mf_vy);
1082  }
1083  });
1084 
1085  ParallelFor(zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1086  const Real coeff = ScalarDiffusionFaceCoefficient<
1087  MultiplyMolecularByDensity, AddTurbulence>(
1088  density, rho_comp, mu_turb, coefficients, i, j, k, 0, 0, 1,
1089  coefficients.eddy_v_comp);
1090  const bool ext_lo =
1091  ((bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir) ||
1092  (bc_ptr[bc_comp].lo(2) == ERFBCType::ext_dir_prim)) &&
1093  k == dom_lo.z;
1094  const bool ext_hi =
1095  ((bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir) ||
1096  (bc_ptr[bc_comp].hi(2) == ERFBCType::ext_dir_prim)) &&
1097  k == dom_hi.z + 1;
1098  const bool on_surface =
1099  (surface.zlo && k == dom_lo.z) ||
1100  (surface.zhi && k == dom_hi.z + 1);
1101 
1102  const Real raw_flux = TerrainScalarDiffusionFluxAtKFace(
1103  scalar, scalar_comp, coeff, i, j, k, z_nd, cellSizeInv, ext_lo, ext_hi);
1104 
1105  if (on_surface && prescribed.use_z_on_side) {
1106  zflux(i, j, k, flux_comp) = prescribed.z(i, j, k, prescribed.z_comp);
1107  } else if (on_surface) {
1108  zflux(i, j, k, flux_comp) = Real(0.0);
1109  } else {
1110  zflux(i, j, k, flux_comp) = raw_flux;
1111  }
1112  if (diagnostic.enabled && (diagnostic.write_on_surface || !on_surface)) {
1113  diagnostic.z(i, j, k, diagnostic.z_comp) = zflux(i, j, k, flux_comp);
1114  }
1115  });
1116 }
1117 
1118 /** Build all native terrain-grid face fluxes, including the grown horizontal
1119  * face regions needed by the mapped z-face transfer. */
1120 inline void
1122  const amrex::Box& bx,
1123  const amrex::Box& domain,
1124  const ScalarDiffusionFieldViews& field,
1125  const ScalarDiffusionFluxPolicy& policy,
1126  const bool rotate,
1127  const amrex::Array4<const amrex::Real>& z_nd,
1128  const amrex::Array4<const amrex::Real>& z_cc,
1129  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
1130  const amrex::Array4<const amrex::Real>& mf_ux,
1131  const amrex::Array4<const amrex::Real>& mf_vy,
1132  const amrex::BCRec* bc_ptr,
1133  const int bc_comp)
1134 {
1135  const auto mode = policy.coefficient_mode;
1136  if (mode.molecular_is_kinematic) {
1137  if (mode.add_turbulence) {
1138  BuildScalarDiffusionFluxes_T_Impl<true, true>(
1139  bx, domain, field, policy, rotate, z_nd, z_cc, cellSizeInv, mf_ux,
1140  mf_vy, bc_ptr, bc_comp);
1141  } else {
1142  BuildScalarDiffusionFluxes_T_Impl<true, false>(
1143  bx, domain, field, policy, rotate, z_nd, z_cc, cellSizeInv, mf_ux,
1144  mf_vy, bc_ptr, bc_comp);
1145  }
1146  } else if (mode.add_turbulence) {
1147  BuildScalarDiffusionFluxes_T_Impl<false, true>(
1148  bx, domain, field, policy, rotate, z_nd, z_cc, cellSizeInv, mf_ux,
1149  mf_vy, bc_ptr, bc_comp);
1150  } else {
1151  BuildScalarDiffusionFluxes_T_Impl<false, false>(
1152  bx, domain, field, policy, rotate, z_nd, z_cc, cellSizeInv, mf_ux,
1153  mf_vy, bc_ptr, bc_comp);
1154  }
1155 }
1156 
1157 /**
1158  * Materialize the canonical N-grid face transfers. N-grid x/y builder outputs
1159  * already carry their map-factor ratios; only the vertical flux is divided by
1160  * the cell-centered horizontal map-factor product.
1161  */
1162 inline void
1164  const amrex::Box& bx,
1165  const amrex::Array4<const amrex::Real>& raw_x,
1166  const int raw_x_comp,
1167  const amrex::Array4<const amrex::Real>& raw_y,
1168  const int raw_y_comp,
1169  const amrex::Array4<const amrex::Real>& raw_z,
1170  const int raw_z_comp,
1171  const amrex::Array4<amrex::Real>& mapped_x,
1172  const int mapped_x_comp,
1173  const amrex::Array4<amrex::Real>& mapped_y,
1174  const int mapped_y_comp,
1175  const amrex::Array4<amrex::Real>& mapped_z,
1176  const int mapped_z_comp,
1177  const amrex::Array4<const amrex::Real>& mf_mx,
1178  const amrex::Array4<const amrex::Real>& mf_my)
1179 {
1180  const amrex::Box xbx = amrex::surroundingNodes(bx, 0);
1181  const amrex::Box ybx = amrex::surroundingNodes(bx, 1);
1182  const amrex::Box zbx = amrex::surroundingNodes(bx, 2);
1184  xbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1185  mapped_x(i, j, k, mapped_x_comp) =
1187  raw_x(i, j, k, raw_x_comp), amrex::Real(1.0));
1188  });
1190  ybx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1191  mapped_y(i, j, k, mapped_y_comp) =
1193  raw_y(i, j, k, raw_y_comp), amrex::Real(1.0));
1194  });
1196  zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1197  mapped_z(i, j, k, mapped_z_comp) =
1199  raw_z(i, j, k, raw_z_comp), mf_mx(i, j, 0), mf_my(i, j, 0));
1200  });
1201 }
1202 
1203 /**
1204  * Materialize the canonical S-grid face transfers. ax/ay restore the side
1205  * Jacobian measure omitted by the native S-grid divergence; detJ and the
1206  * computational z spacing are supplied separately to the mapped divergence.
1207  */
1208 inline void
1210  const amrex::Box& bx,
1211  const amrex::Array4<const amrex::Real>& raw_x,
1212  const int raw_x_comp,
1213  const amrex::Array4<const amrex::Real>& raw_y,
1214  const int raw_y_comp,
1215  const amrex::Array4<const amrex::Real>& raw_z,
1216  const int raw_z_comp,
1217  const amrex::Array4<amrex::Real>& mapped_x,
1218  const int mapped_x_comp,
1219  const amrex::Array4<amrex::Real>& mapped_y,
1220  const int mapped_y_comp,
1221  const amrex::Array4<amrex::Real>& mapped_z,
1222  const int mapped_z_comp,
1223  const amrex::Array4<const amrex::Real>& ax,
1224  const amrex::Array4<const amrex::Real>& ay,
1225  const amrex::Array4<const amrex::Real>& mf_mx,
1226  const amrex::Array4<const amrex::Real>& mf_my)
1227 {
1228  const amrex::Box xbx = amrex::surroundingNodes(bx, 0);
1229  const amrex::Box ybx = amrex::surroundingNodes(bx, 1);
1230  const amrex::Box zbx = amrex::surroundingNodes(bx, 2);
1232  xbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1233  mapped_x(i, j, k, mapped_x_comp) =
1235  raw_x(i, j, k, raw_x_comp), ax(i, j, k));
1236  });
1238  ybx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1239  mapped_y(i, j, k, mapped_y_comp) =
1241  raw_y(i, j, k, raw_y_comp), ay(i, j, k));
1242  });
1244  zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1245  mapped_z(i, j, k, mapped_z_comp) =
1247  raw_z(i, j, k, raw_z_comp), mf_mx(i, j, 0), mf_my(i, j, 0));
1248  });
1249 }
1250 
1251 /**
1252  * Materialize the canonical terrain-following transfers using ERF's existing
1253  * face mappings and authoritative Gz construction. The lower suppression
1254  * switch zeros the complete lower transfer, including terrain cross terms.
1255  *
1256  * Corresponding directional inputs and outputs may alias in place, including
1257  * the same component. The z transfer is launched first on AMReX's ordered
1258  * execution stream so it reads raw x/y fluxes before the x/y mapping launches
1259  * can overwrite them. Cross-direction aliasing is not supported.
1260  *
1261  * For each interior z face mapped from bx, raw_x and raw_y must be valid at
1262  * both kface and kface-1. Thus, if bx.smallEnd(2) is above the domain bottom,
1263  * raw_x/raw_y must include bx.smallEnd(2)-1; if bx.bigEnd(2) is below the
1264  * domain top, they must include bx.bigEnd(2)+1. No outside-domain layer is
1265  * required at a physical bottom/top face because the Gz helper uses one-sided
1266  * extrapolation. Raw candidates produced by BuildScalarDiffusionFluxes_T
1267  * already provide this z coverage.
1268  */
1269 inline void
1271  const amrex::Box& bx,
1272  const amrex::Box& domain,
1273  const amrex::Array4<const amrex::Real>& raw_x,
1274  const amrex::Array4<const amrex::Real>& raw_y,
1275  const amrex::Array4<const amrex::Real>& raw_z,
1276  const int raw_flux_comp,
1277  const amrex::Array4<amrex::Real>& mapped_x,
1278  const int mapped_x_comp,
1279  const amrex::Array4<amrex::Real>& mapped_y,
1280  const int mapped_y_comp,
1281  const amrex::Array4<amrex::Real>& mapped_z,
1282  const int mapped_z_comp,
1283  const amrex::Array4<const amrex::Real>& z_nd,
1284  const amrex::Array4<const amrex::Real>& ax,
1285  const amrex::Array4<const amrex::Real>& ay,
1286  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
1287  const amrex::Array4<const amrex::Real>& mf_mx,
1288  const amrex::Array4<const amrex::Real>& mf_uy,
1289  const amrex::Array4<const amrex::Real>& mf_my,
1290  const amrex::Array4<const amrex::Real>& mf_vx,
1291  const bool suppress_lower_surface_z_transfer)
1292 {
1293  const amrex::Box xbx = amrex::surroundingNodes(bx, 0);
1294  const amrex::Box ybx = amrex::surroundingNodes(bx, 1);
1295  const amrex::Box zbx = amrex::surroundingNodes(bx, 2);
1296  const amrex::Array4<const amrex::Real> xflux(raw_x);
1297  const amrex::Array4<const amrex::Real> yflux(raw_y);
1298  const amrex::Array4<const amrex::Real> zflux(raw_z);
1300  zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1301  const bool suppress = suppress_lower_surface_z_transfer &&
1302  k == domain.smallEnd(2);
1303  mapped_z(i, j, k, mapped_z_comp) =
1305  i, j, k, domain, xflux, yflux, zflux, raw_flux_comp, z_nd,
1306  cellSizeInv, mf_mx, mf_my, suppress);
1307  });
1308  amrex::ParallelFor(xbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1309  mapped_x(i, j, k, mapped_x_comp) = TerrainDiffusionMappedTx(
1310  raw_x(i, j, k, raw_flux_comp), ax(i, j, k), mf_uy(i, j, 0));
1311  });
1312  amrex::ParallelFor(ybx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1313  mapped_y(i, j, k, mapped_y_comp) = TerrainDiffusionMappedTy(
1314  raw_y(i, j, k, raw_flux_comp), ay(i, j, k), mf_vx(i, j, 0));
1315  });
1316 }
1317 
1318 /**
1319  * Add the divergence of caller-supplied mapped transfers to rhs. Inputs are
1320  * only the accepted face arrays and the mapped cell geometry; in particular,
1321  * this operation cannot reconstruct a raw or terrain diffusion candidate.
1322  * The supplied face arrays and mapped-geometry convention are the same as
1323  * ApplyScalarAdvectionFluxDivergence. This diffusion operation differs in RHS
1324  * semantics: it accumulates -divergence into an existing RHS and leaves cells
1325  * with nonpositive detJ unchanged, whereas advection assigns its output and
1326  * zeroes such cells.
1327  */
1328 inline void
1330  const amrex::Box& bx,
1331  const amrex::Array4<const amrex::Real>& mapped_x,
1332  const int mapped_x_comp,
1333  const amrex::Array4<const amrex::Real>& mapped_y,
1334  const int mapped_y_comp,
1335  const amrex::Array4<const amrex::Real>& mapped_z,
1336  const int mapped_z_comp,
1337  const amrex::Array4<amrex::Real>& rhs,
1338  const int rhs_comp,
1339  const amrex::Array4<const amrex::Real>& detJ,
1340  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
1341  const amrex::Array4<const amrex::Real>& mf_mx,
1342  const amrex::Array4<const amrex::Real>& mf_my)
1343 {
1344  const amrex::Real dx_inv = cellSizeInv[0];
1345  const amrex::Real dy_inv = cellSizeInv[1];
1346  const amrex::Real dzeta_inv = cellSizeInv[2];
1348  bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1349  if (detJ(i, j, k) > amrex::Real(0.0)) {
1350  rhs(i, j, k, rhs_comp) -= ScalarDiffusionMappedFluxDivergence(
1351  mapped_x(i + 1, j, k, mapped_x_comp),
1352  mapped_x(i, j, k, mapped_x_comp),
1353  mapped_y(i, j + 1, k, mapped_y_comp),
1354  mapped_y(i, j, k, mapped_y_comp),
1355  mapped_z(i, j, k + 1, mapped_z_comp),
1356  mapped_z(i, j, k, mapped_z_comp), dx_inv, dy_inv, dzeta_inv,
1357  mf_mx(i, j, 0), mf_my(i, j, 0), detJ(i, j, k));
1358  }
1359  });
1360 }
1361 
1362 /** Apply native terrain diffusion in one kernel, mapping each face inline. */
1363 inline void
1365  const amrex::Box& bx,
1366  const amrex::Box& domain,
1367  const ScalarDiffusionFieldViews& field,
1368  const amrex::Array4<const amrex::Real>& z_nd,
1369  const amrex::Array4<const amrex::Real>& ax,
1370  const amrex::Array4<const amrex::Real>& ay,
1371  const amrex::Array4<const amrex::Real>& detJ,
1372  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
1373  const amrex::Array4<const amrex::Real>& mf_mx,
1374  const amrex::Array4<const amrex::Real>& mf_uy,
1375  const amrex::Array4<const amrex::Real>& mf_my,
1376  const amrex::Array4<const amrex::Real>& mf_vx,
1377  const bool suppress_lower_surface_z_flux)
1378 {
1379  using namespace amrex;
1380  const auto dom_lo = lbound(domain);
1381  const Real dx_inv = cellSizeInv[0];
1382  const Real dy_inv = cellSizeInv[1];
1383  const Real dz_inv = cellSizeInv[2];
1384  const Array4<const Real> xflux(field.xflux);
1385  const Array4<const Real> yflux(field.yflux);
1386  const Array4<const Real> zflux(field.zflux);
1387  const auto rhs = field.rhs;
1388  const int flux_comp = field.flux_comp;
1389  const int rhs_comp = field.rhs_comp;
1390 
1391  ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1392  const Real mapped_Fx_lo = TerrainDiffusionMappedTx(
1393  xflux(i, j, k, flux_comp), ax(i, j, k), mf_uy(i, j, 0));
1394  const Real mapped_Fx_hi = TerrainDiffusionMappedTx(
1395  xflux(i + 1, j, k, flux_comp), ax(i + 1, j, k), mf_uy(i + 1, j, 0));
1396  const Real mapped_Fy_lo = TerrainDiffusionMappedTy(
1397  yflux(i, j, k, flux_comp), ay(i, j, k), mf_vx(i, j, 0));
1398  const Real mapped_Fy_hi = TerrainDiffusionMappedTy(
1399  yflux(i, j + 1, k, flux_comp), ay(i, j + 1, k), mf_vx(i, j + 1, 0));
1400  const Real Gz_lo = (suppress_lower_surface_z_flux && k == dom_lo.z)
1401  ? Real(0.0)
1403  i, j, k, domain, xflux, yflux, zflux, flux_comp,
1404  z_nd, cellSizeInv, mf_mx, mf_my);
1405  const Real Gz_hi = TerrainScalarDiffusionGzAtKFace(
1406  i, j, k + 1, domain, xflux, yflux, zflux, flux_comp, z_nd, cellSizeInv,
1407  mf_mx, mf_my);
1408  const Real mfsq = mf_mx(i, j, 0) * mf_my(i, j, 0);
1409  const Real stateContrib = TerrainDiffusionDivergence_T(
1410  mapped_Fx_hi, mapped_Fx_lo, mapped_Fy_hi, mapped_Fy_lo, Gz_hi, Gz_lo,
1411  dx_inv, dy_inv, dz_inv, mfsq, detJ(i, j, k));
1412  rhs(i, j, k, rhs_comp) -= stateContrib;
1413  });
1414 }
1415 
1416 /**
1417  * Apply the conservative N-grid divergence to a separately selected RHS
1418  * component. Face scratch is component-selected independently in each view.
1419  */
1420 inline void
1422  const amrex::Box& bx,
1423  const amrex::Array4<const amrex::Real>& xflux,
1424  const amrex::Array4<const amrex::Real>& yflux,
1425  const amrex::Array4<const amrex::Real>& raw_Fz,
1426  const int flux_comp,
1427  const amrex::Array4<amrex::Real>& rhs,
1428  const int rhs_comp,
1429  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
1430  const amrex::Array4<const amrex::Real>& mf_mx,
1431  const amrex::Array4<const amrex::Real>& mf_my)
1432 {
1433  const amrex::Real dx_inv = cellSizeInv[0];
1434  const amrex::Real dy_inv = cellSizeInv[1];
1435  const amrex::Real dz_inv = cellSizeInv[2];
1436  amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1437  const amrex::Real mfsq = mf_mx(i, j, 0) * mf_my(i, j, 0);
1438  rhs(i, j, k, rhs_comp) -=
1439  (xflux(i + 1, j, k, flux_comp) - xflux(i, j, k, flux_comp)) * dx_inv *
1440  mfsq +
1441  (yflux(i, j + 1, k, flux_comp) - yflux(i, j, k, flux_comp)) * dy_inv *
1442  mfsq +
1443  (raw_Fz(i, j, k + 1, flux_comp) - raw_Fz(i, j, k, flux_comp)) * dz_inv;
1444  });
1445 }
1446 
1447 /** S-grid conservative divergence; vertical cell width is the receiver's dz[k].
1448  */
1449 inline void
1451  const amrex::Box& bx,
1452  const amrex::Array4<const amrex::Real>& xflux,
1453  const amrex::Array4<const amrex::Real>& yflux,
1454  const amrex::Array4<const amrex::Real>& raw_Fz,
1455  const int flux_comp,
1456  const amrex::Array4<amrex::Real>& rhs,
1457  const int rhs_comp,
1458  const amrex::Array4<const amrex::Real>& mf_mx,
1459  const amrex::Array4<const amrex::Real>& mf_my,
1460  const amrex::Real dx_inv,
1461  const amrex::Real dy_inv,
1462  const amrex::Real* dz)
1463 {
1464  amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1465  const amrex::Real mfsq = mf_mx(i, j, 0) * mf_my(i, j, 0);
1466  const amrex::Real dzk_inv = amrex::Real(1.0) / dz[k];
1467  const amrex::Real stateContrib =
1468  (xflux(i + 1, j, k, flux_comp) - xflux(i, j, k, flux_comp)) * dx_inv *
1469  mfsq +
1470  (yflux(i, j + 1, k, flux_comp) - yflux(i, j, k, flux_comp)) * dy_inv *
1471  mfsq +
1472  (raw_Fz(i, j, k + 1, flux_comp) - raw_Fz(i, j, k, flux_comp)) * dzk_inv;
1473  rhs(i, j, k, rhs_comp) -= stateContrib;
1474  });
1475 }
1476 
1477 /**
1478  * erf.implicit_terrain_metric: the implicit vertical solve also takes the compact
1479  * terrain-metric flux -K_h M_s dphi/dz (ERF_TerrainImplicitMetric.H) on the interior
1480  * zeta faces, so add implicit_fac * K_h M_s dphi/dz to the explicit raw F_z there. K_h
1481  * is the horizontal coefficient of the x/y fluxes, averaged to the z face; the bottom and
1482  * top faces, which carry the boundary condition, are left alone.
1483  */
1484 template <bool MultiplyMolecularByDensity, bool AddTurbulence>
1485 inline void
1487  const amrex::Box& zbx,
1488  const amrex::Box& domain,
1489  const ScalarDiffusionFieldViews& field,
1490  const ScalarDiffusionCoefficients& coefficients,
1491  const amrex::Array4<const amrex::Real>& z_nd,
1492  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
1493  const amrex::Array4<const amrex::Real>& mf_mx,
1494  const amrex::Array4<const amrex::Real>& mf_my,
1495  const amrex::Real implicit_fac)
1496 {
1497  using namespace amrex;
1498  const int dom_klo = domain.smallEnd(2);
1499  const int dom_khi = domain.bigEnd(2);
1500  const auto scalar = field.scalar;
1501  const auto density = field.density;
1502  const auto mu_turb = field.mu_turb;
1503  const auto zflux = field.zflux;
1504  const int scalar_comp = field.scalar_comp;
1505  const int rho_comp = field.rho_comp;
1506  const int flux_comp = field.flux_comp;
1507  ParallelFor(zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1508  if (!TerrainMetricImplicitFace(k, dom_klo, dom_khi)) { return; }
1510  MultiplyMolecularByDensity, AddTurbulence>(
1511  density, rho_comp, mu_turb, coefficients, i, j, k, 0, 0, 1,
1512  coefficients.eddy_h_comp);
1513  const Real h_zeta = Compute_h_zeta_AtKface(i, j, k, cellSizeInv, z_nd);
1514  const Real grad_z = (cellSizeInv[2] / h_zeta) *
1515  (scalar(i, j, k, scalar_comp) - scalar(i, j, k - 1, scalar_comp));
1516  const Real metric = TerrainMetricScalarFactor(
1517  Compute_h_xi_AtKface(i, j, k, cellSizeInv, z_nd),
1518  Compute_h_eta_AtKface(i, j, k, cellSizeInv, z_nd),
1519  mf_mx(i, j, 0), mf_my(i, j, 0));
1520  zflux(i, j, k, flux_comp) += implicit_fac * kh * metric * grad_z;
1521  });
1522 }
1523 
1524 /** Dispatch SubtractTerrainMetricImplicitPart_T_Impl on the coefficient mode. */
1525 inline void
1527  const amrex::Box& bx,
1528  const amrex::Box& domain,
1529  const ScalarDiffusionFieldViews& field,
1530  const ScalarDiffusionFluxPolicy& policy,
1531  const amrex::Array4<const amrex::Real>& z_nd,
1532  const amrex::GpuArray<amrex::Real, AMREX_SPACEDIM>& cellSizeInv,
1533  const amrex::Array4<const amrex::Real>& mf_mx,
1534  const amrex::Array4<const amrex::Real>& mf_my,
1535  const amrex::Real implicit_fac)
1536 {
1537  const amrex::Box zbx = amrex::surroundingNodes(bx, 2);
1538  const auto mode = policy.coefficient_mode;
1539  if (mode.molecular_is_kinematic) {
1540  if (mode.add_turbulence) {
1541  SubtractTerrainMetricImplicitPart_T_Impl<true, true>(
1542  zbx, domain, field, policy.coefficients, z_nd, cellSizeInv, mf_mx, mf_my, implicit_fac);
1543  } else {
1544  SubtractTerrainMetricImplicitPart_T_Impl<true, false>(
1545  zbx, domain, field, policy.coefficients, z_nd, cellSizeInv, mf_mx, mf_my, implicit_fac);
1546  }
1547  } else if (mode.add_turbulence) {
1548  SubtractTerrainMetricImplicitPart_T_Impl<false, true>(
1549  zbx, domain, field, policy.coefficients, z_nd, cellSizeInv, mf_mx, mf_my, implicit_fac);
1550  } else {
1551  SubtractTerrainMetricImplicitPart_T_Impl<false, false>(
1552  zbx, domain, field, policy.coefficients, z_nd, cellSizeInv, mf_mx, mf_my, implicit_fac);
1553  }
1554 }
1555 
1556 /** Scale only raw F_z for the explicit integration fraction. */
1557 inline void
1559  const amrex::Box& zbx,
1560  const amrex::Array4<amrex::Real>& raw_Fz,
1561  const int flux_comp,
1562  const amrex::Real explicit_fac)
1563 {
1564  amrex::ParallelFor(zbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept {
1565  raw_Fz(i, j, k, flux_comp) *= explicit_fac;
1566  });
1567 }
1568 
1569 #endif // ERF_SCALAR_DIFFUSION_H_
const int klo
Definition: ERF_InitCustomPert_ABL.H:75
const int khi
Definition: ERF_InitCustomPert_Bubble.H:21
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
Dimensionless numeric literals and pure mathematical constants.
constexpr amrex::Real fourth
Definition: ERF_NumericalConstants.H:42
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
void BuildScalarDiffusionFluxes_S(const amrex::Box &bx, const amrex::Box &domain, const ScalarDiffusionFieldViews &field, const ScalarDiffusionFluxPolicy &policy, const amrex::Real dx_inv, const amrex::Real dy_inv, const amrex::Real *dz, const int klo, const int khi, const amrex::Array4< const amrex::Real > &mf_ux, const amrex::Array4< const amrex::Real > &mf_uy, const amrex::Array4< const amrex::Real > &mf_vx, const amrex::Array4< const amrex::Real > &mf_vy, const amrex::BCRec *bc_ptr, const int bc_comp)
Definition: ERF_ScalarDiffusion.H:963
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real ScalarDiffusionFlux_Tx(const amrex::Real coeff, const amrex::Real face_map, const amrex::Real grad_xi, const amrex::Real h_xi, const amrex::Real grad_z) noexcept
Definition: ERF_ScalarDiffusion.H:297
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real ScalarDiffusionFlux_N(const amrex::Array4< const amrex::Real > &scalar, const int scalar_comp, const int i, const int j, const int k, const amrex::Real coeff, const amrex::Real inv_spacing, const amrex::Real map_numerator, const amrex::Real map_denominator, const bool ext_dir_lo, const bool ext_dir_hi) noexcept
Definition: ERF_ScalarDiffusion.H:164
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE StretchedDiffusionBoundaryWeights StretchedDiffusionDirichletWeights(const amrex::Real dz_near, const amrex::Real dz_next) noexcept
Definition: ERF_ScalarDiffusion.H:145
void BuildScalarDiffusionFluxes_N(const amrex::Box &bx, const amrex::Box &domain, const ScalarDiffusionFieldViews &field, const ScalarDiffusionFluxPolicy &policy, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &u, const amrex::Array4< const amrex::Real > &v, const amrex::Array4< const amrex::Real > &mf_ux, const amrex::Array4< const amrex::Real > &mf_uy, const amrex::Array4< const amrex::Real > &mf_vx, const amrex::Array4< const amrex::Real > &mf_vy, const amrex::BCRec *bc_ptr, const int bc_comp)
Definition: ERF_ScalarDiffusion.H:813
void BuildScalarDiffusionFluxes_S_Impl(const amrex::Box &bx, const amrex::Box &domain, const ScalarDiffusionFieldViews &field, const ScalarDiffusionFluxPolicy &policy, const amrex::Real dx_inv, const amrex::Real dy_inv, const amrex::Real *dz, const int klo, const int khi, const amrex::Array4< const amrex::Real > &mf_ux, const amrex::Array4< const amrex::Real > &mf_uy, const amrex::Array4< const amrex::Real > &mf_vx, const amrex::Array4< const amrex::Real > &mf_vy, const amrex::BCRec *bc_ptr, const int bc_comp)
Definition: ERF_ScalarDiffusion.H:852
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real TerrainDiffusionGz(const amrex::Real raw_Fz, const amrex::Real mx, const amrex::Real h_xi, const amrex::Real Fbar_x, const amrex::Real my, const amrex::Real h_eta, const amrex::Real Fbar_y) noexcept
Definition: ERF_ScalarDiffusion.H:444
void BuildScalarDiffusionFluxes_T_Impl(const amrex::Box &bx, const amrex::Box &domain, const ScalarDiffusionFieldViews &field, const ScalarDiffusionFluxPolicy &policy, const bool rotate, const amrex::Array4< const amrex::Real > &z_nd, const amrex::Array4< const amrex::Real > &z_cc, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_ux, const amrex::Array4< const amrex::Real > &mf_vy, const amrex::BCRec *bc_ptr, const int bc_comp)
Definition: ERF_ScalarDiffusion.H:1004
void ApplyScalarDiffusionFluxDivergence_N(const amrex::Box &bx, const amrex::Array4< const amrex::Real > &xflux, const amrex::Array4< const amrex::Real > &yflux, const amrex::Array4< const amrex::Real > &raw_Fz, const int flux_comp, const amrex::Array4< amrex::Real > &rhs, const int rhs_comp, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_my)
Definition: ERF_ScalarDiffusion.H:1421
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real ScalarDiffusionFaceCoefficient(const amrex::Array4< const amrex::Real > &rho, const int rho_comp, const amrex::Array4< const amrex::Real > &mu_turb, const ScalarDiffusionCoefficients &coefficients, const int i, const int j, const int k, const int di, const int dj, const int dk, const int eddy_comp) noexcept
Definition: ERF_ScalarDiffusion.H:113
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real ScalarDiffusionMappedHorizontalTransfer(const amrex::Real raw_flux, const amrex::Real face_area) noexcept
Definition: ERF_ScalarDiffusion.H:480
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real ScalarDiffusionMappedFluxDivergence(const amrex::Real fx_hi, const amrex::Real fx_lo, const amrex::Real fy_hi, const amrex::Real fy_lo, const amrex::Real fz_hi, const amrex::Real fz_lo, const amrex::Real dx_inv, const amrex::Real dy_inv, const amrex::Real dzeta_inv, const amrex::Real mx, const amrex::Real my, const amrex::Real detJ_cell) noexcept
Definition: ERF_ScalarDiffusion.H:604
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real ScalarDiffusionMappedVerticalTransfer(const amrex::Real raw_flux, const amrex::Real mx, const amrex::Real my) noexcept
Definition: ERF_ScalarDiffusion.H:489
void BuildScalarDiffusionMappedTransfers_S(const amrex::Box &bx, const amrex::Array4< const amrex::Real > &raw_x, const int raw_x_comp, const amrex::Array4< const amrex::Real > &raw_y, const int raw_y_comp, const amrex::Array4< const amrex::Real > &raw_z, const int raw_z_comp, const amrex::Array4< amrex::Real > &mapped_x, const int mapped_x_comp, const amrex::Array4< amrex::Real > &mapped_y, const int mapped_y_comp, const amrex::Array4< amrex::Real > &mapped_z, const int mapped_z_comp, const amrex::Array4< const amrex::Real > &ax, const amrex::Array4< const amrex::Real > &ay, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_my)
Definition: ERF_ScalarDiffusion.H:1209
void ScaleScalarDiffusionVerticalFlux(const amrex::Box &zbx, const amrex::Array4< amrex::Real > &raw_Fz, const int flux_comp, const amrex::Real explicit_fac)
Definition: ERF_ScalarDiffusion.H:1558
void BuildScalarDiffusionMappedTransfers_N(const amrex::Box &bx, const amrex::Array4< const amrex::Real > &raw_x, const int raw_x_comp, const amrex::Array4< const amrex::Real > &raw_y, const int raw_y_comp, const amrex::Array4< const amrex::Real > &raw_z, const int raw_z_comp, const amrex::Array4< amrex::Real > &mapped_x, const int mapped_x_comp, const amrex::Array4< amrex::Real > &mapped_y, const int mapped_y_comp, const amrex::Array4< amrex::Real > &mapped_z, const int mapped_z_comp, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_my)
Definition: ERF_ScalarDiffusion.H:1163
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real TerrainScalarDiffusionFluxAtJFace(const amrex::Array4< const amrex::Real > &scalar, const int scalar_comp, const amrex::Real coeff, const int i, const int j, const int k, const amrex::Array4< const amrex::Real > &z_nd, const amrex::Array4< const amrex::Real > &z_cc, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_vy) noexcept
Definition: ERF_ScalarDiffusion.H:355
void ApplyScalarDiffusionFluxDivergence_S(const amrex::Box &bx, const amrex::Array4< const amrex::Real > &xflux, const amrex::Array4< const amrex::Real > &yflux, const amrex::Array4< const amrex::Real > &raw_Fz, const int flux_comp, const amrex::Array4< amrex::Real > &rhs, const int rhs_comp, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_my, const amrex::Real dx_inv, const amrex::Real dy_inv, const amrex::Real *dz)
Definition: ERF_ScalarDiffusion.H:1450
void SubtractTerrainMetricImplicitPart_T_Impl(const amrex::Box &zbx, const amrex::Box &domain, const ScalarDiffusionFieldViews &field, const ScalarDiffusionCoefficients &coefficients, const amrex::Array4< const amrex::Real > &z_nd, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_my, const amrex::Real implicit_fac)
Definition: ERF_ScalarDiffusion.H:1486
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real ScalarDiffusionFlux_Ty(const amrex::Real coeff, const amrex::Real face_map, const amrex::Real grad_eta, const amrex::Real h_eta, const amrex::Real grad_z) noexcept
Definition: ERF_ScalarDiffusion.H:308
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real TerrainScalarDiffusionFluxAtKFace(const amrex::Array4< const amrex::Real > &scalar, const int scalar_comp, const amrex::Real coeff, const int i, const int j, const int kface, const amrex::Array4< const amrex::Real > &z_nd, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const bool ext_dir_lo, const bool ext_dir_hi) noexcept
Definition: ERF_ScalarDiffusion.H:389
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real ScalarDiffusionFlux_S_Horizontal(const amrex::Array4< const amrex::Real > &scalar, const int scalar_comp, const int i, const int j, const int k, const amrex::Real coeff, const amrex::Real inv_spacing, const amrex::Real face_map) noexcept
Definition: ERF_ScalarDiffusion.H:277
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real TerrainScalarDiffusionMappedTransferAtKFace(const int i, const int j, const int kface, const amrex::Box &domain, const amrex::Array4< const amrex::Real > &raw_xflux, const amrex::Array4< const amrex::Real > &raw_yflux, const amrex::Array4< const amrex::Real > &raw_zflux, const int flux_comp, const amrex::Array4< const amrex::Real > &z_nd, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_my, const bool suppress_transfer=false) noexcept
Definition: ERF_ScalarDiffusion.H:575
void SubtractTerrainMetricImplicitPart_T(const amrex::Box &bx, const amrex::Box &domain, const ScalarDiffusionFieldViews &field, const ScalarDiffusionFluxPolicy &policy, const amrex::Array4< const amrex::Real > &z_nd, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_my, const amrex::Real implicit_fac)
Definition: ERF_ScalarDiffusion.H:1526
void ApplyScalarMappedFluxDivergence(const amrex::Box &bx, const amrex::Array4< const amrex::Real > &mapped_x, const int mapped_x_comp, const amrex::Array4< const amrex::Real > &mapped_y, const int mapped_y_comp, const amrex::Array4< const amrex::Real > &mapped_z, const int mapped_z_comp, const amrex::Array4< amrex::Real > &rhs, const int rhs_comp, const amrex::Array4< const amrex::Real > &detJ, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_my)
Definition: ERF_ScalarDiffusion.H:1329
void ApplyScalarDiffusionMappedDivergence_T(const amrex::Box &bx, const amrex::Box &domain, const ScalarDiffusionFieldViews &field, const amrex::Array4< const amrex::Real > &z_nd, const amrex::Array4< const amrex::Real > &ax, const amrex::Array4< const amrex::Real > &ay, const amrex::Array4< const amrex::Real > &detJ, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_uy, const amrex::Array4< const amrex::Real > &mf_my, const amrex::Array4< const amrex::Real > &mf_vx, const bool suppress_lower_surface_z_flux)
Definition: ERF_ScalarDiffusion.H:1364
void BuildScalarDiffusionFluxes_N_Impl(const amrex::Box &bx, const amrex::Box &domain, const ScalarDiffusionFieldViews &field, const ScalarDiffusionFluxPolicy &policy, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &u, const amrex::Array4< const amrex::Real > &v, const amrex::Array4< const amrex::Real > &mf_ux, const amrex::Array4< const amrex::Real > &mf_uy, const amrex::Array4< const amrex::Real > &mf_vx, const amrex::Array4< const amrex::Real > &mf_vy, const amrex::BCRec *bc_ptr, const int bc_comp)
Definition: ERF_ScalarDiffusion.H:656
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real TerrainDiffusionDivergence_T(const amrex::Real mapped_Fx_hi, const amrex::Real mapped_Fx_lo, const amrex::Real mapped_Fy_hi, const amrex::Real mapped_Fy_lo, const amrex::Real Gz_hi, const amrex::Real Gz_lo, const amrex::Real dx_inv, const amrex::Real dy_inv, const amrex::Real dz_inv, const amrex::Real mfsq, const amrex::Real detJ_cell) noexcept
Definition: ERF_ScalarDiffusion.H:628
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real StretchedScalarGradient(const amrex::Array4< const amrex::Real > &scalar, const int scalar_comp, const int i, const int j, const int k, const amrex::Real *dz, const int klo, const int khi, const bool ext_dir_lo, const bool ext_dir_hi) noexcept
Definition: ERF_ScalarDiffusion.H:238
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real TerrainScalarDiffusionFluxAtIFace(const amrex::Array4< const amrex::Real > &scalar, const int scalar_comp, const amrex::Real coeff, const int i, const int j, const int k, const amrex::Array4< const amrex::Real > &z_nd, const amrex::Array4< const amrex::Real > &z_cc, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_ux) noexcept
Definition: ERF_ScalarDiffusion.H:325
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real TerrainDiffusionMappedTy(const amrex::Real raw_Fy, const amrex::Real h_zeta, const amrex::Real mx) noexcept
Definition: ERF_ScalarDiffusion.H:466
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real TerrainScalarDiffusionGzAtKFace(const int i, const int j, const int kface, const amrex::Box &domain, const amrex::Array4< const amrex::Real > &raw_xflux, const amrex::Array4< const amrex::Real > &raw_yflux, const amrex::Array4< const amrex::Real > &raw_zflux, const int flux_comp, const amrex::Array4< const amrex::Real > &z_nd, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_my) noexcept
Definition: ERF_ScalarDiffusion.H:503
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real TerrainDiffusionMappedTx(const amrex::Real raw_Fx, const amrex::Real h_zeta, const amrex::Real my) noexcept
Definition: ERF_ScalarDiffusion.H:457
void BuildScalarDiffusionMappedTransfers_T(const amrex::Box &bx, const amrex::Box &domain, const amrex::Array4< const amrex::Real > &raw_x, const amrex::Array4< const amrex::Real > &raw_y, const amrex::Array4< const amrex::Real > &raw_z, const int raw_flux_comp, const amrex::Array4< amrex::Real > &mapped_x, const int mapped_x_comp, const amrex::Array4< amrex::Real > &mapped_y, const int mapped_y_comp, const amrex::Array4< amrex::Real > &mapped_z, const int mapped_z_comp, const amrex::Array4< const amrex::Real > &z_nd, const amrex::Array4< const amrex::Real > &ax, const amrex::Array4< const amrex::Real > &ay, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_mx, const amrex::Array4< const amrex::Real > &mf_uy, const amrex::Array4< const amrex::Real > &mf_my, const amrex::Array4< const amrex::Real > &mf_vx, const bool suppress_lower_surface_z_transfer)
Definition: ERF_ScalarDiffusion.H:1270
void BuildScalarDiffusionFluxes_T(const amrex::Box &bx, const amrex::Box &domain, const ScalarDiffusionFieldViews &field, const ScalarDiffusionFluxPolicy &policy, const bool rotate, const amrex::Array4< const amrex::Real > &z_nd, const amrex::Array4< const amrex::Real > &z_cc, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &mf_ux, const amrex::Array4< const amrex::Real > &mf_vy, const amrex::BCRec *bc_ptr, const int bc_comp)
Definition: ERF_ScalarDiffusion.H:1121
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool TerrainMetricImplicitFace(const int kface, const int dom_lo, const int dom_hi) noexcept
Definition: ERF_TerrainImplicitMetric.H:72
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real TerrainMetricScalarFactor(const amrex::Real h_xi, const amrex::Real h_eta, const amrex::Real mx, const amrex::Real my) noexcept
Definition: ERF_TerrainImplicitMetric.H:56
AMREX_GPU_HOST_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:440
AMREX_GPU_HOST_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:392
AMREX_GPU_HOST_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:463
AMREX_GPU_HOST_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:416
AMREX_GPU_HOST_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:736
AMREX_GPU_HOST_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:299
@ ext_dir
Definition: ERF_IndexDefines.H:300
@ ext_dir_prim
Definition: ERF_IndexDefines.H:303
@ ext_dir_upwind
Definition: ERF_IndexDefines.H:308
@ rho
Definition: ERF_Kessler.H:25
@ dz
Definition: ERF_AdvanceWDM6.cpp:272
Definition: ERF_ConsoleIO.cpp:17
real(c_double), parameter c2
Definition: ERF_module_model_constants.F90:35
real(c_double), private c1
Definition: ERF_module_mp_morr_two_moment.F90:212
real(c_double), private di
Definition: ERF_module_mp_morr_two_moment.F90:203
Definition: ERF_ScalarDiffusion.H:49
bool molecular_is_kinematic
Definition: ERF_ScalarDiffusion.H:50
bool add_turbulence
Definition: ERF_ScalarDiffusion.H:51
Definition: ERF_ScalarDiffusion.H:23
int eddy_v_comp
Definition: ERF_ScalarDiffusion.H:26
int eddy_h_comp
Definition: ERF_ScalarDiffusion.H:25
amrex::Real molecular_coeff
Definition: ERF_ScalarDiffusion.H:24
Definition: ERF_ScalarDiffusion.H:33
int rho_comp
Definition: ERF_ScalarDiffusion.H:37
amrex::Array4< amrex::Real > xflux
Definition: ERF_ScalarDiffusion.H:39
amrex::Array4< amrex::Real > rhs
Definition: ERF_ScalarDiffusion.H:43
int rhs_comp
Definition: ERF_ScalarDiffusion.H:44
amrex::Array4< const amrex::Real > scalar
Definition: ERF_ScalarDiffusion.H:34
amrex::Array4< amrex::Real > yflux
Definition: ERF_ScalarDiffusion.H:40
int flux_comp
Definition: ERF_ScalarDiffusion.H:42
amrex::Array4< const amrex::Real > density
Definition: ERF_ScalarDiffusion.H:36
int scalar_comp
Definition: ERF_ScalarDiffusion.H:35
amrex::Array4< amrex::Real > zflux
Definition: ERF_ScalarDiffusion.H:41
amrex::Array4< const amrex::Real > mu_turb
Definition: ERF_ScalarDiffusion.H:38
Definition: ERF_ScalarDiffusion.H:92
ScalarDiffusionRawFluxDiagnostic diagnostic
Definition: ERF_ScalarDiffusion.H:96
ScalarDiffusionPrescribedFluxes prescribed
Definition: ERF_ScalarDiffusion.H:95
ScalarDiffusionCoefficientMode coefficient_mode
Definition: ERF_ScalarDiffusion.H:94
ScalarDiffusionSurfaceFaces surface
Definition: ERF_ScalarDiffusion.H:97
ScalarDiffusionCoefficients coefficients
Definition: ERF_ScalarDiffusion.H:93
Definition: ERF_ScalarDiffusion.H:56
bool use_z_on_side
Definition: ERF_ScalarDiffusion.H:59
int z_comp
Definition: ERF_ScalarDiffusion.H:67
bool use_y_on_bottom_z
Definition: ERF_ScalarDiffusion.H:61
amrex::Array4< const amrex::Real > x
Definition: ERF_ScalarDiffusion.H:62
amrex::Array4< const amrex::Real > z
Definition: ERF_ScalarDiffusion.H:64
bool use_x_on_side
Definition: ERF_ScalarDiffusion.H:57
int y_comp
Definition: ERF_ScalarDiffusion.H:66
bool use_x_on_bottom_z
Definition: ERF_ScalarDiffusion.H:60
amrex::Array4< const amrex::Real > y
Definition: ERF_ScalarDiffusion.H:63
bool use_y_on_side
Definition: ERF_ScalarDiffusion.H:58
int x_comp
Definition: ERF_ScalarDiffusion.H:65
Definition: ERF_ScalarDiffusion.H:72
int z_comp
Definition: ERF_ScalarDiffusion.H:76
amrex::Array4< amrex::Real > z
Definition: ERF_ScalarDiffusion.H:75
bool write_on_surface
Definition: ERF_ScalarDiffusion.H:74
bool enabled
Definition: ERF_ScalarDiffusion.H:73
Definition: ERF_ScalarDiffusion.H:81
bool zlo
Definition: ERF_ScalarDiffusion.H:86
bool xlo
Definition: ERF_ScalarDiffusion.H:82
bool ylo
Definition: ERF_ScalarDiffusion.H:84
bool xhi
Definition: ERF_ScalarDiffusion.H:83
bool yhi
Definition: ERF_ScalarDiffusion.H:85
bool zhi
Definition: ERF_ScalarDiffusion.H:87
Definition: ERF_ScalarDiffusion.H:140
amrex::Real c2
Definition: ERF_ScalarDiffusion.H:141
amrex::Real c3
Definition: ERF_ScalarDiffusion.H:141
amrex::Real c1
Definition: ERF_ScalarDiffusion.H:141