ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ShocMoments Class Reference

#include <ERF_ShocMoments.H>

Static Public Member Functions

static void calc_var_or_covar (const ShocColumnData &col, amrex::Real tunefac, const amrex::FArrayBox &isotropy_zi, const amrex::FArrayBox &tkh_zi, const amrex::FArrayBox &invar1, const amrex::FArrayBox &invar2, amrex::FArrayBox &outvar)
 
static void calc_vertflux (const ShocColumnData &col, const amrex::FArrayBox &tkh_zi, const amrex::FArrayBox &invar, amrex::FArrayBox &vertflux)
 
static void diagnose_second_moments (ShocColumnData &col, const ShocRuntimeOptions &opts)
 
static void clip_third_moments (const ShocColumnData &col, const amrex::FArrayBox &w_sec_zi, amrex::FArrayBox &w3)
 
static void diagnose_third_moments (ShocColumnData &col, const ShocRuntimeOptions &opts)
 
static void diagnose_moments (ShocColumnData &col, const ShocRuntimeOptions &opts)
 

Member Function Documentation

◆ calc_var_or_covar()

void ShocMoments::calc_var_or_covar ( const ShocColumnData col,
amrex::Real  tunefac,
const amrex::FArrayBox &  isotropy_zi,
const amrex::FArrayBox &  tkh_zi,
const amrex::FArrayBox &  invar1,
const amrex::FArrayBox &  invar2,
amrex::FArrayBox &  outvar 
)
static
256 {
257  auto out = outvar.array();
258  const auto iso = isotropy_zi.const_array();
259  const auto tkh = tkh_zi.const_array();
260  const auto v1 = invar1.const_array();
261  const auto v2 = invar2.const_array();
262  const auto zt = col.zt.const_array();
263  const auto zi = col.zi.const_array();
264  const auto layout = col.layout;
265  const Box col_box(IntVect(0,0,0), IntVect(layout.ncell - 1, 0, 0));
266  ParallelFor(col_box, [=] AMREX_GPU_DEVICE (int ic, int, int) noexcept
267  {
268  for (int k = 1; k < layout.nlev; ++k) {
269  const Real dz_zi = cell_spacing_at_iface(zt, zi, layout, ic, k);
270  const Real grid_dz2 = 1.0_rt / (dz_zi * dz_zi);
271  out(ic,k,0) = tunefac * (iso(ic,k,0) * tkh(ic,k,0)) * grid_dz2 *
272  (v1(ic,k-1,0) - v1(ic,k,0)) * (v2(ic,k-1,0) - v2(ic,k,0));
273  }
274  });
275 }
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);})
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ zi
Definition: ERF_AdvanceWDM6.cpp:276
ShocColumnLayout layout
Definition: ERF_ShocTypes.H:73
amrex::FArrayBox zi
Definition: ERF_ShocTypes.H:77
amrex::FArrayBox zt
Definition: ERF_ShocTypes.H:76
Here is the call graph for this function:

◆ calc_vertflux()

void ShocMoments::calc_vertflux ( const ShocColumnData col,
const amrex::FArrayBox &  tkh_zi,
const amrex::FArrayBox &  invar,
amrex::FArrayBox &  vertflux 
)
static
282 {
283  auto out = vertflux.array();
284  const auto tkh = tkh_zi.const_array();
285  const auto v = invar.const_array();
286  const auto zt = col.zt.const_array();
287  const auto zi = col.zi.const_array();
288  const auto layout = col.layout;
289  const Box col_box(IntVect(0,0,0), IntVect(layout.ncell - 1, 0, 0));
290  ParallelFor(col_box, [=] AMREX_GPU_DEVICE (int ic, int, int) noexcept
291  {
292  for (int k = 1; k < layout.nlev; ++k) {
293  const Real dz_zi = cell_spacing_at_iface(zt, zi, layout, ic, k);
294  // E3SM uses top-down vertical indexing. ERF columns are bottom-up,
295  // so the interface gradient must use upper-minus-lower to preserve
296  // the same physical downgradient flux sign.
297  out(ic,k,0) = -(tkh(ic,k,0) / dz_zi) * (v(ic,k,0) - v(ic,k-1,0));
298  }
299  });
300 }
Here is the call graph for this function:

◆ clip_third_moments()

void ShocMoments::clip_third_moments ( const ShocColumnData col,
const amrex::FArrayBox &  w_sec_zi,
amrex::FArrayBox &  w3 
)
static
360 {
361  auto w3 = w3_fab.array();
362  const auto wsec = w_sec_zi.const_array();
363 
364  const auto layout = col.layout;
365  const Box col_box(IntVect(0,0,0), IntVect(layout.ncell - 1, 0, 0));
366  ParallelFor(col_box, [=] AMREX_GPU_DEVICE (int ic, int, int) noexcept
367  {
368  for (int k = 0; k <= layout.nlev; ++k) {
369  const Real wsec3 = wsec(ic,k,0) * wsec(ic,k,0) * wsec(ic,k,0);
370  const Real clip_cond = shoc_w3clip() * std::sqrt(amrex::max(0.0_rt, 2.0_rt * wsec3));
371  // E3SM/EAMxx replaces an out-of-range w3 with a fixed positive
372  // constant (0.02). That discards the sign of the third moment,
373  // so a strongly negatively skewed column is handed back a
374  // positively skewed w3, and it can also move |w3| further away
375  // from the bound it is supposed to enforce when clip_cond is
376  // small. Limit the magnitude to clip_cond instead and keep the
377  // sign, which is continuous in w3 and respects the bound.
378  if (amrex::Math::abs(w3(ic,k,0)) > clip_cond) {
379  w3(ic,k,0) = std::copysign(clip_cond, w3(ic,k,0));
380  }
381  }
382  });
383 }
Here is the call graph for this function:

◆ diagnose_moments()

void ShocMoments::diagnose_moments ( ShocColumnData col,
const ShocRuntimeOptions opts 
)
static
493 {
494  diagnose_second_moments(col, opts);
495  diagnose_third_moments(col, opts);
496 }
static void diagnose_second_moments(ShocColumnData &col, const ShocRuntimeOptions &opts)
Definition: ERF_ShocMoments.cpp:303
static void diagnose_third_moments(ShocColumnData &col, const ShocRuntimeOptions &opts)
Definition: ERF_ShocMoments.cpp:386

Referenced by ShocDiagnostics::diagnose_post_implicit().

Here is the caller graph for this function:

◆ diagnose_second_moments()

void ShocMoments::diagnose_second_moments ( ShocColumnData col,
const ShocRuntimeOptions opts 
)
static
305 {
306  FArrayBox isotropy_zi, tkh_zi, tk_zi;
307  const Box iface_box(IntVect(0,0,0), IntVect(col.layout.ncell - 1, col.layout.nlev, 0));
308  isotropy_zi.resize(iface_box, 1, The_Async_Arena());
309  tkh_zi.resize(iface_box, 1, The_Async_Arena());
310  tk_zi.resize(iface_box, 1, The_Async_Arena());
311 
312  interpolate_cc_to_iface(col, col.isotropy, isotropy_zi, 0.0);
313  interpolate_cc_to_iface(col, col.tkh, tkh_zi, 0.0);
314  interpolate_cc_to_iface(col, col.tk, tk_zi, 0.0);
315 
316  auto thl_sec = col.thl_sec.array();
317  auto qw_sec = col.qw_sec.array();
318  auto qwthl_sec = col.qwthl_sec.array();
319  auto w_sec = col.w_sec.array();
320 
321  const auto tke = col.tke.const_array();
322  const auto layout = col.layout;
323  const Box col_box(IntVect(0,0,0), IntVect(layout.ncell - 1, 0, 0));
324  ParallelFor(col_box, [=] AMREX_GPU_DEVICE (int ic, int, int) noexcept
325  {
326  for (int k = 0; k < layout.nlev; ++k) {
327  w_sec(ic,k,0) = opts.shoc_1p5tke ? 0.0_rt : opts.w2tune * (2.0_rt / 3.0_rt) * tke(ic,k,0);
328  }
329  });
330 
331  if (opts.shoc_1p5tke) {
332  ParallelFor(col_box, [=] AMREX_GPU_DEVICE (int ic, int, int) noexcept
333  {
334  for (int k = 0; k <= layout.nlev; ++k) {
335  thl_sec(ic,k,0) = 0.0_rt;
336  qw_sec(ic,k,0) = 0.0_rt;
337  qwthl_sec(ic,k,0) = 0.0_rt;
338  }
339  });
340  } else {
341  calc_var_or_covar(col, opts.thl2tune, isotropy_zi, tkh_zi, col.thetal, col.thetal, col.thl_sec);
342  calc_var_or_covar(col, opts.qw2tune, isotropy_zi, tkh_zi, col.qw, col.qw, col.qw_sec);
343  calc_var_or_covar(col, opts.qwthl2tune, isotropy_zi, tkh_zi, col.thetal, col.qw, col.qwthl_sec);
344  }
345 
346  calc_vertflux(col, tkh_zi, col.thetal, col.wthl_sec);
347  calc_vertflux(col, tkh_zi, col.qw, col.wqw_sec);
348  calc_vertflux(col, tkh_zi, col.tke, col.wtke_sec);
349  calc_vertflux(col, tk_zi, col.u, col.uw_sec);
350  calc_vertflux(col, tk_zi, col.v, col.vw_sec);
351 
352  apply_second_moment_boundary_conditions(col);
353  apply_top_taper_to_second_moments(col, opts);
354 }
static void calc_var_or_covar(const ShocColumnData &col, amrex::Real tunefac, const amrex::FArrayBox &isotropy_zi, const amrex::FArrayBox &tkh_zi, const amrex::FArrayBox &invar1, const amrex::FArrayBox &invar2, amrex::FArrayBox &outvar)
Definition: ERF_ShocMoments.cpp:249
static void calc_vertflux(const ShocColumnData &col, const amrex::FArrayBox &tkh_zi, const amrex::FArrayBox &invar, amrex::FArrayBox &vertflux)
Definition: ERF_ShocMoments.cpp:278
amrex::FArrayBox w_sec
Definition: ERF_ShocTypes.H:126
amrex::FArrayBox tk
Definition: ERF_ShocTypes.H:104
amrex::FArrayBox vw_sec
Definition: ERF_ShocTypes.H:123
amrex::FArrayBox wqw_sec
Definition: ERF_ShocTypes.H:121
amrex::FArrayBox uw_sec
Definition: ERF_ShocTypes.H:122
amrex::FArrayBox isotropy
Definition: ERF_ShocTypes.H:103
amrex::FArrayBox tke
Definition: ERF_ShocTypes.H:91
amrex::FArrayBox qw_sec
Definition: ERF_ShocTypes.H:118
amrex::FArrayBox tkh
Definition: ERF_ShocTypes.H:105
amrex::FArrayBox qw
Definition: ERF_ShocTypes.H:89
amrex::FArrayBox v
Definition: ERF_ShocTypes.H:93
amrex::FArrayBox wtke_sec
Definition: ERF_ShocTypes.H:124
amrex::FArrayBox u
Definition: ERF_ShocTypes.H:92
amrex::FArrayBox wthl_sec
Definition: ERF_ShocTypes.H:120
amrex::FArrayBox thl_sec
Definition: ERF_ShocTypes.H:117
amrex::FArrayBox qwthl_sec
Definition: ERF_ShocTypes.H:119
amrex::FArrayBox thetal
Definition: ERF_ShocTypes.H:85
int nlev
Definition: ERF_ShocTypes.H:64
int ncell
Definition: ERF_ShocTypes.H:63
bool shoc_1p5tke
Definition: ERF_ShocTypes.H:35
amrex::Real w2tune
Definition: ERF_ShocTypes.H:26
amrex::Real qwthl2tune
Definition: ERF_ShocTypes.H:25
amrex::Real thl2tune
Definition: ERF_ShocTypes.H:23
amrex::Real qw2tune
Definition: ERF_ShocTypes.H:24
Here is the call graph for this function:

◆ diagnose_third_moments()

void ShocMoments::diagnose_third_moments ( ShocColumnData col,
const ShocRuntimeOptions opts 
)
static
388 {
389  FArrayBox isotropy_zi, brunt_zi, w_sec_zi, thetal_zi;
390  const Box iface_box(IntVect(0,0,0), IntVect(col.layout.ncell - 1, col.layout.nlev, 0));
391  isotropy_zi.resize(iface_box, 1, The_Async_Arena());
392  brunt_zi.resize(iface_box, 1, The_Async_Arena());
393  w_sec_zi.resize(iface_box, 1, The_Async_Arena());
394  thetal_zi.resize(iface_box, 1, The_Async_Arena());
395 
396  interpolate_cc_to_iface(col, col.isotropy, isotropy_zi, 0.0);
397  interpolate_cc_to_iface(col, col.brunt, brunt_zi, shoc_large_neg());
398  interpolate_cc_to_iface(col, col.w_sec, w_sec_zi, (2.0_rt / 3.0_rt) * shoc_min_tke());
399  interpolate_cc_to_iface(col, col.thetal, thetal_zi, 0.0);
400 
401  auto w3 = col.w3.array();
402  const auto w_sec = col.w_sec.const_array();
403  const auto thl_sec = col.thl_sec.const_array();
404  const auto wthl_sec = col.wthl_sec.const_array();
405  const auto tke = col.tke.const_array();
406  const auto dz = col.dz.const_array();
407  const auto isotropy_i = isotropy_zi.const_array();
408  const auto brunt_i = brunt_zi.const_array();
409  const auto w_sec_i = w_sec_zi.const_array();
410  const auto thetal_i = thetal_zi.const_array();
411  const auto zt = col.zt.const_array();
412  const auto zi = col.zi.const_array();
413  const auto layout = col.layout;
414  const Box col_box(IntVect(0,0,0), IntVect(layout.ncell - 1, 0, 0));
415  ParallelFor(col_box, [=] AMREX_GPU_DEVICE (int ic, int, int) noexcept
416  {
417  w3(ic,0,0) = 0.0_rt;
418  w3(ic,layout.nlev,0) = 0.0_rt;
419 
420  for (int k = 1; k < layout.nlev; ++k) {
421  if (opts.shoc_1p5tke) {
422  w3(ic,k,0) = 0.0_rt;
423  continue;
424  }
425 
426  const Real dz_zt_k = amrex::max(dz(ic,k,0), eps());
427  const Real dz_zt_km1 = amrex::max(dz(ic,k-1,0), eps());
428  const Real dz_zi = cell_spacing_at_iface(zt, zi, layout, ic, k);
429  const Real thedz = 1.0_rt / dz_zi;
430  const Real thedz2 = 1.0_rt / (dz_zt_k + dz_zt_km1);
431  const Real iso = isotropy_i(ic,k,0);
432  const Real isosq = iso * iso;
433  const Real buoy_sgs2 = isosq * brunt_i(ic,k,0);
434  const Real bet2 = CONST_GRAV / amrex::max(thetal_i(ic,k,0), eps());
435 
436  // E3SM's top-down indexing forms above-minus-below centered
437  // differences here. In ERF's bottom-up ordering, the upper
438  // neighbor is k+1 and the lower neighbor is k-1, while the cell
439  // just above interface k is k and the one below is k-1.
440  const Real thl_sec_diff = thl_sec(ic,amrex::min(k+1, layout.nlev),0) - thl_sec(ic,k-1,0);
441  const Real wthl_sec_diff = wthl_sec(ic,amrex::min(k+1, layout.nlev),0) - wthl_sec(ic,k-1,0);
442  const Real wsec_diff = w_sec(ic,k,0) - w_sec(ic,k-1,0);
443  const Real tke_diff = tke(ic,k,0) - tke(ic,k-1,0);
444 
445  const Real c = opts.c_diag_3rd_mom;
446  const Real a0 = (0.52_rt / (c * c)) / amrex::max(c - 2.0_rt, eps());
447  const Real a1 = 0.87_rt / (c * c);
448  const Real a2 = 0.5_rt / c;
449  const Real a3 = 0.6_rt / (c * amrex::max(c - 2.0_rt, eps()));
450  const Real a4 = 2.4_rt / (3.0_rt * c + 5.0_rt);
451  const Real a5 = 0.6_rt / (c * (3.0_rt + 5.0_rt * c));
452  const Real bet2_sq = bet2 * bet2;
453  const Real bet2_cu = bet2_sq * bet2;
454  const Real iso_cu = isosq * iso;
455 
456  const Real f0 = thedz2 * bet2_cu * isosq * isosq *
457  wthl_sec(ic,k,0) * thl_sec_diff;
458  const Real f1 = thedz2 * bet2_sq * iso_cu *
459  (wthl_sec(ic,k,0) * wthl_sec_diff +
460  0.5_rt * w_sec_i(ic,k,0) * thl_sec_diff);
461  const Real f2 = thedz * bet2 * isosq * wthl_sec(ic,k,0) * wsec_diff +
462  2.0_rt * thedz2 * bet2 * isosq * w_sec_i(ic,k,0) * wthl_sec_diff;
463  const Real f3 = thedz2 * bet2 * isosq * w_sec_i(ic,k,0) * wthl_sec_diff +
464  thedz * bet2 * isosq * (wthl_sec(ic,k,0) * tke_diff);
465  const Real f4 = thedz * iso * w_sec_i(ic,k,0) * (wsec_diff + tke_diff);
466  const Real f5 = thedz * iso * w_sec_i(ic,k,0) * wsec_diff;
467 
468  const Real denom0 = signed_denominator(1.0_rt - (a1 + a3) * buoy_sgs2);
469  const Real omega0 = a4 / signed_denominator(1.0_rt - a5 * buoy_sgs2);
470  const Real omega1 = omega0 / (2.0_rt * c);
471  const Real omega2 = omega1 * f3 + 1.25_rt * omega0 * f4;
472  const Real x0 = (a2 * buoy_sgs2 * (1.0_rt - a3 * buoy_sgs2)) / denom0;
473  const Real y0 = (2.0_rt * a2 * buoy_sgs2 * x0) / signed_denominator(1.0_rt - a3 * buoy_sgs2);
474  const Real x1 = (a0 * f0 + a1 * f1 + a2 * (1.0_rt - a3 * buoy_sgs2) * f2) / denom0;
475  const Real y1 = (2.0_rt * a2 * (buoy_sgs2 * x1 + (a0 / amrex::max(a1, eps())) * f0 + f1)) /
476  signed_denominator(1.0_rt - a3 * buoy_sgs2);
477  const Real aa0 = omega0 * x0 + omega1 * y0;
478  const Real aa1 = omega0 * x1 + omega1 * y1 + omega2;
479 
480  const Real denom = signed_denominator(c - 1.2_rt * x0 + aa0);
481  Real w3_val = (aa1 - 1.2_rt * x1 - 1.5_rt * f5) / denom;
482  w3(ic,k,0) = w3_val;
483  }
484  });
485 
486  clip_third_moments(col, w_sec_zi, col.w3);
487  apply_top_taper_to_third_moments(col, opts);
488 }
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:56
static void clip_third_moments(const ShocColumnData &col, const amrex::FArrayBox &w_sec_zi, amrex::FArrayBox &w3)
Definition: ERF_ShocMoments.cpp:357
@ dz
Definition: ERF_AdvanceWDM6.cpp:272
real(c_double), parameter a2
Definition: ERF_module_model_constants.F90:95
real(c_double), parameter a3
Definition: ERF_module_model_constants.F90:96
real(c_double), parameter a4
Definition: ERF_module_model_constants.F90:97
amrex::FArrayBox dz
Definition: ERF_ShocTypes.H:78
amrex::FArrayBox w3
Definition: ERF_ShocTypes.H:125
amrex::FArrayBox brunt
Definition: ERF_ShocTypes.H:102
amrex::Real c_diag_3rd_mom
Definition: ERF_ShocTypes.H:29
Here is the call graph for this function:

The documentation for this class was generated from the following files: