ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_AdvanceWDM6.cpp File Reference
#include "ERF_WDM6.H"
#include <AMReX_IArrayBox.H>
#include <AMReX_Reduce.H>
#include <algorithm>
#include <array>
#include <cctype>
#include <cstdio>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <string>
#include <vector>
Include dependency graph for ERF_AdvanceWDM6.cpp:

Namespaces

 WDM6SedCellScratch
 
 WDM6SedNodeScratch
 

Enumerations

enum  {
  WDM6SedCellScratch::wd , WDM6SedCellScratch::wa , WDM6SedCellScratch::wa2 , WDM6SedCellScratch::was ,
  WDM6SedCellScratch::qn , WDM6SedCellScratch::qn2 , WDM6SedCellScratch::qq , WDM6SedCellScratch::qq2 ,
  WDM6SedCellScratch::qr , WDM6SedCellScratch::qr2 , WDM6SedCellScratch::dz , WDM6SedCellScratch::den ,
  WDM6SedCellScratch::denfac , WDM6SedCellScratch::tk , WDM6SedCellScratch::work_col , WDM6SedCellScratch::rq_col ,
  WDM6SedCellScratch::rq2_col , WDM6SedCellScratch::NumComps
}
 
enum  {
  WDM6SedNodeScratch::wi , WDM6SedNodeScratch::zi , WDM6SedNodeScratch::za , WDM6SedNodeScratch::dza ,
  WDM6SedNodeScratch::qa , WDM6SedNodeScratch::qa2 , WDM6SedNodeScratch::qmi , WDM6SedNodeScratch::qpi ,
  WDM6SedNodeScratch::NumComps
}
 

Functions

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_cpmcal (Real x, Real qmin_arg, Real cpd_arg, Real cpv_arg)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_xlcal (Real x, Real xlv0_arg, Real xlv1_arg, Real t0c_arg)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_default_real_pow (double base, double exponent)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_diffus (Real x, Real y)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_viscos (Real x, Real y)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_xka (Real x, Real y)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_diffac (Real a, Real b, Real c, Real d, Real e, Real rv_arg)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_venfac (Real a, Real b, Real c, Real den0_arg)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_conden (Real a, Real b, Real c, Real d, Real e, Real qmin_arg, Real rv_arg)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdar (Real qr, Real den, Real nr, Real pidnr_arg)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdac_exact (Real qc, Real den, Real nc, Real pidnc_arg)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_rslopec_exact (Real qc, Real den, Real nc, Real pidnc_arg)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_xni_exact (Real qi, Real den, Real qmin_arg)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_slope_rain_cell (Real qr, Real nr, Real den, Real denfac, Real qcrmin_arg, Real nrmin_arg, Real rslopermax_arg, Real rsloperbmax_arg, Real rsloper2max_arg, Real rsloper3max_arg, Real bvtr_arg, Real pvtr_arg, Real pvtrn_arg, Real pidnr_arg, Real &rslope, Real &rslopeb, Real &rslope2, Real &rslope3, Real &vt, Real &vtn)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdas (Real x, Real y, Real z, Real pidn0s_arg)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdag (Real x, Real y, Real pidn0g_arg)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_slope_snow_cell (Real qs, Real den, Real denfac, Real t, Real pidn0s_arg, Real alpha_arg, Real n0smax_arg, Real n0s_arg, Real t0c_arg, Real qcrmin_arg, Real rslopesmax_arg, Real rslopesbmax_arg, Real rslopes2max_arg, Real rslopes3max_arg, Real bvts_arg, Real pvts_arg, Real &rslope, Real &rslopeb, Real &rslope2, Real &rslope3, Real &vt, Real &n0sfac)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_slope_graup_cell (Real qg, Real den, Real denfac, Real pidn0g_arg, Real qcrmin_arg, Real rslopegmax_arg, Real rslopegbmax_arg, Real rslopeg2max_arg, Real rslopeg3max_arg, Real bvtg_arg, Real pvtg_arg, Real &rslope, Real &rslopeb, Real &rslope2, Real &rslope3, Real &vt)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_nislfv_rain_plm6_column (int km, Real *precip1, Real *precip2, Real dt, int iter, Real pidn0s, Real pidn0g, Real qcrmin, Real alpha, Real n0smax, Real n0s, Real t0c, Real rslopesmax, Real rslopesbmax, Real rslopes2max, Real rslopes3max, Real bvts, Real pvts, Real rslopegmax, Real rslopegbmax, Real rslopeg2max, Real rslopeg3max, Real bvtg, Real pvtg, Array4< Real > const &sed_cell, Array4< Real > const &sed_node, int i_s, int j_s, int klo_s)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_mean_droplet_diameter (Real qc, Real nc, Real den, Real)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_ccn_activation (Real &nc, Real &nn, const Real qv, const Real qc, const Real qvs, const Real, const Real w, const Real, const Real, const Real)
 

Variables

constexpr amrex::Real wdm6_slope_t0c = wdm6_literal(273.15)
 

Function Documentation

◆ wdm6_ccn_activation()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_ccn_activation ( Real nc,
Real nn,
const Real  qv,
const Real  qc,
const Real  qvs,
const  Real,
const Real  w,
const  Real,
const  Real,
const  Real 
)
620  {
621  // Only activate if supersaturated and cloud exists
622  if (qv <= qvs || qc < Real(1.e-8)) return;
623 
624  // Supersaturation
625  Real supersaturation = qv / qvs - Real(1.0);
626  supersaturation = amrex::min(supersaturation, Real(0.0048)); // satmax - 1
627 
628  // Critical supersaturation based on updraft strength
629  // From WRF: stronger updrafts -> more activation
630  Real s_crit = Real(0.6) * std::pow(amrex::max(w, Real(0.01)), Real(0.5));
631 
632  if (supersaturation > s_crit && nn > Real(0.0)) {
633  // Activation rate: convert aerosols to droplets
634  Real nc_activate = amrex::min(nn, Real(0.1) * (supersaturation - s_crit) / s_crit * nn);
635 
636  // Update concentrations
637  nc += nc_activate;
638  nn -= nc_activate;
639 
640  // Enforce physical bounds
641  nc = amrex::max(nc, Real(1.e1));
642  nn = amrex::max(nn, Real(0.0));
643  }
644 }
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ qv
Definition: ERF_Kessler.H:30
@ nc
Definition: ERF_Morrison.H:45
@ qc
Definition: ERF_SatAdj.H:41
@ nn
Definition: ERF_WDM6.H:31

◆ wdm6_conden()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_conden ( Real  a,
Real  b,
Real  c,
Real  d,
Real  e,
Real  qmin_arg,
Real  rv_arg 
)
78  {
79  return (amrex::max(b,qmin_arg)-c)
80  /(Real(1.0)+d*d/(rv_arg*e)*c/(a*a));
81 }

Referenced by WDM6::Advance().

Here is the caller graph for this function:

◆ wdm6_cpmcal()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_cpmcal ( Real  x,
Real  qmin_arg,
Real  cpd_arg,
Real  cpv_arg 
)
26  {
27  return cpd_arg*(Real(1.0)-amrex::max(x,qmin_arg))
28  +amrex::max(x,qmin_arg)*cpv_arg;
29 }

Referenced by WDM6::Advance().

Here is the caller graph for this function:

◆ wdm6_default_real_pow()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_default_real_pow ( double  base,
double  exponent 
)
37  {
38 #ifdef ERF_WDM6_F32_LITERALS
39  return Real(std::pow(static_cast<float>(base), static_cast<float>(exponent)));
40 #else
41  return Real(std::pow(base, exponent));
42 #endif
43 }

Referenced by WDM6::Advance().

Here is the caller graph for this function:

◆ wdm6_diffac()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_diffac ( Real  a,
Real  b,
Real  c,
Real  d,
Real  e,
Real  rv_arg 
)
62  {
63  return d*a*a/(wdm6_xka(c,d)*rv_arg*c*c)
64  + Real(1.0)/(e*wdm6_diffus(c,b));
65 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_xka(Real x, Real y)
Definition: ERF_AdvanceWDM6.cpp:56
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_diffus(Real x, Real y)
Definition: ERF_AdvanceWDM6.cpp:46

Referenced by WDM6::Advance().

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

◆ wdm6_diffus()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_diffus ( Real  x,
Real  y 
)
46  {
47  return (wdm6_literal(8.794e-5)*std::exp(std::log(x)*wdm6_literal(1.81)))/y;
48 }
constexpr amrex::Real wdm6_literal(double d)
Definition: ERF_WDM6.H:45

Referenced by wdm6_diffac(), and wdm6_venfac().

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

◆ wdm6_lamdac_exact()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdac_exact ( Real  qc,
Real  den,
Real  nc,
Real  pidnc_arg 
)
110  {
111  // Fortran statement function, ERF_module_mp_wdm6.F90:562:
112  // lamdac(x,y,z) = exp(log(((pidnc*z)/(x*y)))*((.33333333)))
113  // called as lamdac(qci(i,k,1), den(i,k), ncr(i,k,2)), so x=qc, y=den, z=nc.
114  // The exponent literal is unsuffixed in the Fortran and therefore obeys the
115  // LITERAL PRECISION CONTRACT; it is not 1/3.
116  return std::exp(std::log((pidnc_arg * nc) / (qc * den)) * wdm6_literal(0.33333333));
117 }
@ den
Definition: ERF_AdvanceWDM6.cpp:270

Referenced by wdm6_rslopec_exact().

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

◆ wdm6_lamdag()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdag ( Real  x,
Real  y,
Real  pidn0g_arg 
)
205  {
206  // Graupel slope parameter (single-moment)
207  return std::sqrt(std::sqrt(pidn0g_arg/(x*y)));
208 }

Referenced by wdm6_slope_graup_cell().

Here is the caller graph for this function:

◆ wdm6_lamdar()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdar ( Real  qr,
Real  den,
Real  nr,
Real  pidnr_arg 
)
88  {
89  // Fortran statement function, ERF_module_mp_wdm6.F90:3277 and :3357:
90  // lamdar(x,y,z) = exp(log(((pidnr*z)/(x*y)))*((.33333333)))
91  // called as lamdar(qrs(i,k,1), den(i,k), ncr(i,k)), so x=qr, y=den, z=nr:
92  // the denominator is qr*den, NOT qr alone.
93  //
94  // This carried the identical defect already fixed in wdm6_lamdac: it wrote
95  // (pidnr*nr*den)/(den*qr), in which den cancels, dropping the /den entirely
96  // and inflating rslope by den^(-1/3). Measured at (199,3,43): the rain slope
97  // was 3.098e-05 native against 2.833e-05 on the bridge, a ratio of 1.0936
98  // against den = 0.765, matching den^(-1/3) exactly. That pushed avedia
99  // across the di82 threshold in G16a, 8.9365e-05 native against 8.1717e-05,
100  // so the bridge collapsed rain back into cloud (qr and nr to zero, qc and nc
101  // incremented) while the native path left the cell untouched.
102  //
103  // The old qr/nr guard is dropped: every caller already gates on
104  // qr <= qcrmin || nr <= nrmin with the same values, and the branch returned
105  // 1/5.0e4, a slope, where the caller expects a lambda and inverts it again.
106  return std::exp(std::log((pidnr_arg * nr) / (qr * den)) * wdm6_literal(0.33333333));
107 }
@ nr
Definition: ERF_Morrison.H:46
@ qr
Definition: ERF_AdvanceWDM6.cpp:269

Referenced by wdm6_slope_rain_cell().

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

◆ wdm6_lamdas()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdas ( Real  x,
Real  y,
Real  z,
Real  pidn0s_arg 
)
199  {
200  // Snow slope parameter (single-moment)
201  return std::sqrt(std::sqrt(pidn0s_arg*z/(x*y)));
202 }

Referenced by wdm6_slope_snow_cell().

Here is the caller graph for this function:

◆ wdm6_mean_droplet_diameter()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_mean_droplet_diameter ( Real  qc,
Real  nc,
Real  den,
Real   
)
589  {
590  // Volume-weighted mean diameter of cloud droplets
591  // Returns diameter in meters
592  if (nc < Real(1.e1) || qc < Real(1.e-9)) return Real(0.0);
593 
594  // Mean mass = rho * qc / nc
595  // Mean volume = mean mass / rho_water
596  // Mean diameter = (6 * volume / pi)^(1/3)
597  Real mean_mass = den * qc / (nc * den); // kg per droplet
598  Real mean_volume = mean_mass / Real(1000.0); // m^3 per droplet
599  Real diameter = std::pow(Real(6.0) * mean_volume / Real(3.14159265359), Real(1.0)/Real(3.0));
600 
601  return diameter;
602 }

◆ wdm6_nislfv_rain_plm6_column()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_nislfv_rain_plm6_column ( int  km,
Real precip1,
Real precip2,
Real  dt,
int  iter,
Real  pidn0s,
Real  pidn0g,
Real  qcrmin,
Real  alpha,
Real  n0smax,
Real  n0s,
Real  t0c,
Real  rslopesmax,
Real  rslopesbmax,
Real  rslopes2max,
Real  rslopes3max,
Real  bvts,
Real  pvts,
Real  rslopegmax,
Real  rslopegbmax,
Real  rslopeg2max,
Real  rslopeg3max,
Real  bvtg,
Real  pvtg,
Array4< Real > const &  sed_cell,
Array4< Real > const &  sed_node,
int  i_s,
int  j_s,
int  klo_s 
)
308 {
309  auto WD = [&](int k) -> amrex::Real& {
310  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::wd);
311  };
312  auto WA = [&](int k) -> amrex::Real& {
313  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::wa);
314  };
315  auto WA2 = [&](int k) -> amrex::Real& {
316  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::wa2);
317  };
318  auto WAS = [&](int k) -> amrex::Real& {
319  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::was);
320  };
321  auto QN = [&](int k) -> amrex::Real& {
322  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qn);
323  };
324  auto QN2 = [&](int k) -> amrex::Real& {
325  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qn2);
326  };
327  auto QQ = [&](int k) -> amrex::Real& {
328  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qq);
329  };
330  auto QQ2 = [&](int k) -> amrex::Real& {
331  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qq2);
332  };
333  auto QR = [&](int k) -> amrex::Real& {
334  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qr);
335  };
336  auto QR2 = [&](int k) -> amrex::Real& {
337  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qr2);
338  };
339  auto DZ = [&](int k) -> amrex::Real& {
340  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::dz);
341  };
342  auto DEN = [&](int k) -> amrex::Real& {
343  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::den);
344  };
345  auto DENFAC = [&](int k) -> amrex::Real& {
346  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::denfac);
347  };
348  auto TK = [&](int k) -> amrex::Real& {
349  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::tk);
350  };
351  auto WW = [&](int k) -> amrex::Real& {
352  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::work_col);
353  };
354  auto RQL = [&](int k) -> amrex::Real& {
355  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::rq_col);
356  };
357  auto RQL2 = [&](int k) -> amrex::Real& {
358  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::rq2_col);
359  };
360 
361  auto WI = [&](int k) -> amrex::Real& {
362  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::wi);
363  };
364  auto ZI = [&](int k) -> amrex::Real& {
365  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::zi);
366  };
367  auto ZA = [&](int k) -> amrex::Real& {
368  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::za);
369  };
370  auto DZA = [&](int k) -> amrex::Real& {
371  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::dza);
372  };
373  auto QA = [&](int k) -> amrex::Real& {
374  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::qa);
375  };
376  auto QA2 = [&](int k) -> amrex::Real& {
377  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::qa2);
378  };
379  auto QMI = [&](int k) -> amrex::Real& {
380  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::qmi);
381  };
382  auto QPI = [&](int k) -> amrex::Real& {
383  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::qpi);
384  };
385 
386  Real allold = Real(0.0);
387  for (int k = 0; k < km; ++k) {
388  QQ(k) = RQL(k);
389  QQ2(k) = RQL2(k);
390  WD(k) = WW(k);
391  allold += QQ(k) + QQ2(k);
392  }
393 
394  precip1[0] = Real(0.0);
395  precip2[0] = Real(0.0);
396  if (allold <= Real(0.0)) {
397  return;
398  }
399 
400  ZI(0) = Real(0.0);
401  for (int k = 0; k < km; ++k) {
402  ZI(k + 1) = ZI(k) + DZ(k);
403  }
404 
405  auto rebuild = [&]() {
406  WI(0) = WW(0);
407  if (km > 1) {
408  WI(1) = Real(0.5) * (WW(1) + WW(0));
409  for (int k = 2; k < km - 1; ++k) {
410  WI(k) = Real(9.0) / Real(16.0) * (WW(k) + WW(k - 1))
411  - Real(1.0) / Real(16.0) * (WW(k + 1) + WW(k - 2));
412  }
413  WI(km - 1) = Real(0.5) * (WW(km - 1) + WW(km - 2));
414  }
415  WI(km) = WW(km - 1);
416  for (int k = 1; k < km; ++k) {
417  if (WW(k) == Real(0.0)) WI(k) = WW(k - 1);
418  }
419 
420  constexpr Real con1 = wdm6_literal(0.05);
421  for (int k = km - 1; k >= 0; --k) {
422  const Real decfl = (WI(k + 1) - WI(k)) * dt / DZ(k);
423  if (decfl > con1) {
424  WI(k) = WI(k + 1) - con1 * DZ(k) / dt;
425  }
426  }
427 
428  for (int k = 0; k <= km; ++k) {
429  ZA(k) = ZI(k) - WI(k) * dt;
430  }
431  for (int k = 0; k < km; ++k) {
432  DZA(k) = ZA(k + 1) - ZA(k);
433  QA(k) = QQ(k) * DZ(k) / DZA(k);
434  QA2(k) = QQ2(k) * DZ(k) / DZA(k);
435  QR(k) = QA(k) / DEN(k);
436  QR2(k) = QA2(k) / DEN(k);
437  }
438  DZA(km) = ZI(km) - ZA(km);
439  QA(km) = Real(0.0);
440  QA2(km) = Real(0.0);
441  };
442 
443  rebuild();
444 
445  if (iter > 0) {
446  for (int k = 0; k < km; ++k) {
447  Real rslope, rslopeb, rslope2, rslope3, vt, n0sfac_dummy;
448  wdm6_slope_snow_cell(QR(k), DEN(k), DENFAC(k), TK(k),
449  pidn0s, alpha, n0smax, n0s, t0c, qcrmin,
451  bvts, pvts, rslope, rslopeb, rslope2, rslope3, vt,
452  n0sfac_dummy);
453  WA(k) = vt;
454  wdm6_slope_graup_cell(QR2(k), DEN(k), DENFAC(k),
457  rslope, rslopeb, rslope2, rslope3, vt);
458  WA2(k) = vt;
459  }
460  for (int k = 0; k < km; ++k) {
461  const Real tmpq = amrex::max(QR(k) + QR2(k), wdm6_literal(1.0e-15));
462  WA(k) = (tmpq > wdm6_literal(1.0e-15))
463  ? (WA(k) * QR(k) + WA2(k) * QR2(k)) / tmpq
464  : Real(0.0);
465  WW(k) = Real(0.5) * (WD(k) + WA(k));
466  WAS(k) = WA(k);
467  }
468  rebuild();
469  }
470 
471  for (int ist = 0; ist < 2; ++ist) {
472  const int qn_comp = (ist == 0)
475  const int qa_comp = (ist == 0)
478  auto qn_dst = [&](int k) -> amrex::Real& {
479  return sed_cell(i_s, j_s, klo_s + k, qn_comp);
480  };
481  auto qa_src = [&](int k) -> amrex::Real& {
482  return sed_node(i_s, j_s, klo_s + k, qa_comp);
483  };
484  Real* precip_dst = (ist == 0) ? precip1 : precip2;
485 
486  QPI(0) = qa_src(0);
487  QMI(0) = qa_src(0);
488  QMI(km) = qa_src(km);
489  QPI(km) = qa_src(km);
490  for (int k = 1; k < km; ++k) {
491  const Real dip = (qa_src(k + 1) - qa_src(k)) / (DZA(k + 1) + DZA(k));
492  const Real dim = (qa_src(k) - qa_src(k - 1)) / (DZA(k - 1) + DZA(k));
493  if (dip * dim <= Real(0.0)) {
494  QMI(k) = qa_src(k);
495  QPI(k) = qa_src(k);
496  } else {
497  QPI(k) = qa_src(k) + Real(0.5) * (dip + dim) * DZA(k);
498  QMI(k) = Real(2.0) * qa_src(k) - QPI(k);
499  if (QPI(k) < Real(0.0) || QMI(k) < Real(0.0)) {
500  QPI(k) = qa_src(k);
501  QMI(k) = qa_src(k);
502  }
503  }
504  }
505 
506  for (int k = 0; k < km; ++k) {
507  qn_dst(k) = Real(0.0);
508  }
509  int kb = 0;
510  int kt = 0;
511  for (int k = 0; k < km; ++k) {
512  // Fortran backs both brackets off by one at the TOP of every k
513  // before searching forward:
514  // kb=max(kb-1,1) ; kt=max(kt-1,1)
515  // Omitting this let the forward search start above the correct
516  // bracket, so cells whose departure interval reaches back below the
517  // previous k's bracket were remapped from the wrong donor.
518  kb = amrex::max(kb - 1, 0);
519  kt = amrex::max(kt - 1, 0);
520 
521  if (ZI(k) >= ZA(km)) break;
522 
523  for (int kk = kb; kk < km; ++kk) {
524  if (ZI(k) <= ZA(kk + 1)) {
525  kb = kk;
526  break;
527  }
528  }
529  for (int kk = kt; kk < km; ++kk) {
530  if (ZI(k + 1) <= ZA(kk)) {
531  kt = kk;
532  break;
533  }
534  }
535  // Fortran writes a bare kt = kt - 1 with no floor. Clamping it to 0
536  // is not equivalent: when the decrement should drop kt BELOW kb,
537  // the Fortran falls through both branches and leaves qn(k) at zero,
538  // whereas the clamp can make kt equal kb and take the single-cell
539  // branch against the wrong donor. kt is only used as an index
540  // inside the two branches, both of which require kt >= kb >= 0, so
541  // a negative value here is safe.
542  kt = kt - 1;
543 
544  if (kt == kb) {
545  const Real tl = (ZI(k) - ZA(kb)) / DZA(kb);
546  const Real th = (ZI(k + 1) - ZA(kb)) / DZA(kb);
547  const Real qqd = Real(0.5) * (QPI(kb) - QMI(kb));
548  qn_dst(k) = (qqd * th * th + QMI(kb) * th
549  - qqd * tl * tl - QMI(kb) * tl) / (th - tl);
550  } else if (kt > kb) {
551  const Real tl = (ZI(k) - ZA(kb)) / DZA(kb);
552  const Real qqd = Real(0.5) * (QPI(kb) - QMI(kb));
553  const Real qql = qqd * tl * tl + QMI(kb) * tl;
554  const Real dql = qa_src(kb) - qql;
555  Real zsum = (Real(1.0) - tl) * DZA(kb);
556  Real qsum = dql * DZA(kb);
557  for (int m = kb + 1; m < kt; ++m) {
558  zsum += DZA(m);
559  qsum += qa_src(m) * DZA(m);
560  }
561  const Real th = (ZI(k + 1) - ZA(kt)) / DZA(kt);
562  const Real dqh = Real(0.5) * (QPI(kt) - QMI(kt)) * th * th + QMI(kt) * th;
563  zsum += th * DZA(kt);
564  qsum += dqh * DZA(kt);
565  qn_dst(k) = qsum / zsum;
566  }
567  }
568 
569  precip_dst[0] = Real(0.0);
570  for (int k = 0; k < km; ++k) {
571  if (ZA(k) < Real(0.0) && ZA(k + 1) < Real(0.0)) {
572  precip_dst[0] += qa_src(k) * DZA(k);
573  } else if (ZA(k) < Real(0.0) && ZA(k + 1) >= Real(0.0)) {
574  precip_dst[0] += qa_src(k) * (Real(0.0) - ZA(k));
575  break;
576  } else {
577  break;
578  }
579  }
580  }
581 
582  for (int k = 0; k < km; ++k) {
583  RQL(k) = QN(k);
584  RQL2(k) = QN2(k);
585  }
586 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_slope_snow_cell(Real qs, Real den, Real denfac, Real t, Real pidn0s_arg, Real alpha_arg, Real n0smax_arg, Real n0s_arg, Real t0c_arg, Real qcrmin_arg, Real rslopesmax_arg, Real rslopesbmax_arg, Real rslopes2max_arg, Real rslopes3max_arg, Real bvts_arg, Real pvts_arg, Real &rslope, Real &rslopeb, Real &rslope2, Real &rslope3, Real &vt, Real &n0sfac)
Definition: ERF_AdvanceWDM6.cpp:215
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_slope_graup_cell(Real qg, Real den, Real denfac, Real pidn0g_arg, Real qcrmin_arg, Real rslopegmax_arg, Real rslopegbmax_arg, Real rslopeg2max_arg, Real rslopeg3max_arg, Real bvtg_arg, Real pvtg_arg, Real &rslope, Real &rslopeb, Real &rslope2, Real &rslope3, Real &vt)
Definition: ERF_AdvanceWDM6.cpp:245
@ n0s
Definition: ERF_AdvanceMorrison.cpp:43
@ QR
Definition: ERF_IndexDefines.H:142
@ qn2
Definition: ERF_AdvanceWDM6.cpp:269
@ was
Definition: ERF_AdvanceWDM6.cpp:269
@ tk
Definition: ERF_AdvanceWDM6.cpp:270
@ qn
Definition: ERF_AdvanceWDM6.cpp:269
@ wa2
Definition: ERF_AdvanceWDM6.cpp:269
@ qr2
Definition: ERF_AdvanceWDM6.cpp:269
@ work_col
Definition: ERF_AdvanceWDM6.cpp:270
@ denfac
Definition: ERF_AdvanceWDM6.cpp:270
@ rq2_col
Definition: ERF_AdvanceWDM6.cpp:270
@ wa
Definition: ERF_AdvanceWDM6.cpp:269
@ qq
Definition: ERF_AdvanceWDM6.cpp:269
@ qq2
Definition: ERF_AdvanceWDM6.cpp:269
@ wd
Definition: ERF_AdvanceWDM6.cpp:269
@ rq_col
Definition: ERF_AdvanceWDM6.cpp:270
@ dz
Definition: ERF_AdvanceWDM6.cpp:270
@ zi
Definition: ERF_AdvanceWDM6.cpp:274
@ qa2
Definition: ERF_AdvanceWDM6.cpp:274
@ qpi
Definition: ERF_AdvanceWDM6.cpp:274
@ qa
Definition: ERF_AdvanceWDM6.cpp:274
@ za
Definition: ERF_AdvanceWDM6.cpp:274
@ wi
Definition: ERF_AdvanceWDM6.cpp:274
@ qmi
Definition: ERF_AdvanceWDM6.cpp:274
@ dza
Definition: ERF_AdvanceWDM6.cpp:274
@ qsum
Definition: ERF_WSM6.H:234
real(kind=kind_phys), save rslopes3max
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), save rslopegmax
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), save rslopesmax
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), parameter, private n0smax
Definition: ERF_module_mp_wdm6.F90:60
real(kind=kind_phys), parameter, private alpha
Definition: ERF_module_mp_wdm6.F90:62
real(kind=kind_phys), save pidn0s
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), save rslopeg3max
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), save pvtg
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), save bvtg
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), save pvts
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), save rslopeg2max
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), parameter, private bvts
Definition: ERF_module_mp_wdm6.F90:66
real(kind=kind_phys), save rslopesbmax
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), save pidn0g
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), save rslopegbmax
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), save rslopes2max
Definition: ERF_module_mp_wdm6.F90:100
real(kind=kind_phys), parameter, private qcrmin
Definition: ERF_module_mp_wdm6.F90:85

Referenced by WDM6::Advance().

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

◆ wdm6_rslopec_exact()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_rslopec_exact ( Real  qc,
Real  den,
Real  nc,
Real  pidnc_arg 
)
120  {
121  // rslopec is the RECIPROCAL of lamdac: the Fortran writes
122  // rslopec(i,k) = 1./lamdac(qci(i,k,1),den(i,k),ncr(i,k,2))
123  // at both of its two sites (G3 at :915 and G11 at :1544). Returning lamdac
124  // itself here put rslopec off by 1/lamdac^2, about nine orders of magnitude
125  // once cloud water exists. It was invisible until cloud water first formed,
126  // because with qc <= qmin both legs take the rslopecmax branch instead.
127  return Real(1.0) / wdm6_lamdac_exact(qc, den, nc, pidnc_arg);
128 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdac_exact(Real qc, Real den, Real nc, Real pidnc_arg)
Definition: ERF_AdvanceWDM6.cpp:110

Referenced by WDM6::Advance().

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

◆ wdm6_slope_graup_cell()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_slope_graup_cell ( Real  qg,
Real  den,
Real  denfac,
Real  pidn0g_arg,
Real  qcrmin_arg,
Real  rslopegmax_arg,
Real  rslopegbmax_arg,
Real  rslopeg2max_arg,
Real  rslopeg3max_arg,
Real  bvtg_arg,
Real  pvtg_arg,
Real rslope,
Real rslopeb,
Real rslope2,
Real rslope3,
Real vt 
)
252 {
253  if (qg <= qcrmin_arg) {
254  rslope = rslopegmax_arg;
255  rslopeb = rslopegbmax_arg;
256  rslope2 = rslopeg2max_arg;
257  rslope3 = rslopeg3max_arg;
258  } else {
259  rslope = Real(1.0)/wdm6_lamdag(qg,den,pidn0g_arg);
260  rslopeb = std::pow(rslope,bvtg_arg);
261  rslope2 = rslope*rslope;
262  rslope3 = rslope2*rslope;
263  }
264  vt = pvtg_arg*rslopeb*denfac;
265  if (qg <= Real(0.0)) { vt = Real(0.0); }
266 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdag(Real x, Real y, Real pidn0g_arg)
Definition: ERF_AdvanceWDM6.cpp:205
@ qg
Definition: ERF_WDM6.H:30

Referenced by WDM6::Advance(), and wdm6_nislfv_rain_plm6_column().

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

◆ wdm6_slope_rain_cell()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_slope_rain_cell ( Real  qr,
Real  nr,
Real  den,
Real  denfac,
Real  qcrmin_arg,
Real  nrmin_arg,
Real  rslopermax_arg,
Real  rsloperbmax_arg,
Real  rsloper2max_arg,
Real  rsloper3max_arg,
Real  bvtr_arg,
Real  pvtr_arg,
Real  pvtrn_arg,
Real  pidnr_arg,
Real rslope,
Real rslopeb,
Real rslope2,
Real rslope3,
Real vt,
Real vtn 
)
147 {
148  if (qr <= qcrmin_arg || nr <= nrmin_arg) {
149  rslope = rslopermax_arg;
150  rslopeb = rsloperbmax_arg;
151  rslope2 = rsloper2max_arg;
152  rslope3 = rsloper3max_arg;
153  } else {
154  // The rain-slope cap is unsuffixed in the Fortran at BOTH of its sites,
155  // slope_wdm6 :3420 and slope_rain :3493:
156  // rslope(i,k,1) = min(1./lamdar(...),1.e-3)
157  // so it obeys the LITERAL PRECISION CONTRACT: float32(1.e-3) widens to
158  // 0.0010000000474974513, not the exact double 1e-3. The cap therefore
159  // sits +4.749745e-08 relative ABOVE the port's, and the gap propagates
160  // as a power: rslopeb = rslope**bvtr, rslope2, rslope3, so rslope3
161  // carries 3x it at 1.424924e-07.
162  //
163  // This is a min(), so it binds only where 1./lamdar exceeds the cap --
164  // rare, and identically zero for the first fifteen steps of the Bubble
165  // case, which is why a bitwise-clean 10-step run did not expose it.
166  rslope = amrex::min(Real(1.0) / wdm6_lamdar(qr, den, nr, pidnr_arg),
167  wdm6_literal(1.e-3));
168  rslopeb = std::pow(rslope, bvtr_arg);
169  rslope2 = rslope * rslope;
170  rslope3 = rslope2 * rslope;
171  }
172  vt = pvtr_arg * rslopeb * denfac;
173  vtn = pvtrn_arg * rslopeb * denfac;
174  if (qr <= Real(0.0)) { vt = Real(0.0); }
175  if (nr <= Real(0.0)) { vtn = Real(0.0); }
176 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdar(Real qr, Real den, Real nr, Real pidnr_arg)
Definition: ERF_AdvanceWDM6.cpp:88

Referenced by WDM6::Advance().

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

◆ wdm6_slope_snow_cell()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void wdm6_slope_snow_cell ( Real  qs,
Real  den,
Real  denfac,
Real  t,
Real  pidn0s_arg,
Real  alpha_arg,
Real  n0smax_arg,
Real  n0s_arg,
Real  t0c_arg,
Real  qcrmin_arg,
Real  rslopesmax_arg,
Real  rslopesbmax_arg,
Real  rslopes2max_arg,
Real  rslopes3max_arg,
Real  bvts_arg,
Real  pvts_arg,
Real rslope,
Real rslopeb,
Real rslope2,
Real rslope3,
Real vt,
Real n0sfac 
)
225 {
226  Real supcol = t0c_arg - t;
227  n0sfac = amrex::max(amrex::min(std::exp(alpha_arg*supcol),
228  n0smax_arg/n0s_arg), Real(1.0));
229  if (qs <= qcrmin_arg) {
230  rslope = rslopesmax_arg;
231  rslopeb = rslopesbmax_arg;
232  rslope2 = rslopes2max_arg;
233  rslope3 = rslopes3max_arg;
234  } else {
235  rslope = Real(1.0)/wdm6_lamdas(qs,den,n0sfac,pidn0s_arg);
236  rslopeb = std::pow(rslope,bvts_arg);
237  rslope2 = rslope*rslope;
238  rslope3 = rslope2*rslope;
239  }
240  vt = pvts_arg*rslopeb*denfac;
241  if (qs <= Real(0.0)) { vt = Real(0.0); }
242 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdas(Real x, Real y, Real z, Real pidn0s_arg)
Definition: ERF_AdvanceWDM6.cpp:199
@ qs
Definition: ERF_WDM6.H:29
@ n0sfac
Definition: ERF_WSM6.H:244
@ t
Definition: ERF_WSM6.H:183

Referenced by WDM6::Advance(), and wdm6_nislfv_rain_plm6_column().

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

◆ wdm6_venfac()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_venfac ( Real  a,
Real  b,
Real  c,
Real  den0_arg 
)
68  {
69  // Fortran: exp(log((viscos(b,c)/diffus(b,a)))*((.3333333))) / sqrt(viscos) * sqrt(sqrt(den0/c))
70  return std::exp(std::log(wdm6_viscos(b,c)/wdm6_diffus(b,a))
71  *wdm6_literal(0.3333333))
72  /std::sqrt(wdm6_viscos(b,c))
73  *std::sqrt(std::sqrt(den0_arg/c));
74 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_viscos(Real x, Real y)
Definition: ERF_AdvanceWDM6.cpp:51

Referenced by WDM6::Advance().

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

◆ wdm6_viscos()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_viscos ( Real  x,
Real  y 
)
51  {
52  return wdm6_literal(1.496e-6)*(x*std::sqrt(x))/(x+Real(120.0))/y;
53 }

Referenced by wdm6_venfac(), and wdm6_xka().

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

◆ wdm6_xka()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_xka ( Real  x,
Real  y 
)
56  {
57  return Real(1.414e3)*wdm6_viscos(x,y)*y;
58 }

Referenced by WDM6::Advance(), and wdm6_diffac().

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

◆ wdm6_xlcal()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_xlcal ( Real  x,
Real  xlv0_arg,
Real  xlv1_arg,
Real  t0c_arg 
)
32  {
33  return xlv0_arg - xlv1_arg*(x - t0c_arg);
34 }

Referenced by WDM6::Advance().

Here is the caller graph for this function:

◆ wdm6_xni_exact()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_xni_exact ( Real  qi,
Real  den,
Real  qmin_arg 
)
131  {
132  Real temp = den * amrex::max(qi, qmin_arg);
133  temp = std::sqrt(std::sqrt(temp * temp * temp));
134  return amrex::min(amrex::max(Real(5.38e7) * temp, Real(1.e3)), Real(1.e6));
135 }
@ qi
Definition: ERF_WDM6.H:27

Referenced by WDM6::Advance().

Here is the caller graph for this function:

Variable Documentation

◆ wdm6_slope_t0c

constexpr amrex::Real wdm6_slope_t0c = wdm6_literal(273.15)
constexpr

Referenced by WDM6::Advance().