ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_AdvanceWDM6.cpp File Reference
#include "ERF_WDM6.H"
#include "ERF_MicrophysicsConstants.H"
#include "ERF_Constants.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 
)
622  {
623  // Only activate if supersaturated and cloud exists
624  if (qv <= qvs || qc < Real(1.e-8)) return;
625 
626  // Supersaturation
627  Real supersaturation = qv / qvs - Real(1.0);
628  supersaturation = amrex::min(supersaturation, Real(0.0048)); // satmax - 1
629 
630  // Critical supersaturation based on updraft strength
631  // From WRF: stronger updrafts -> more activation
632  Real s_crit = Real(0.6) * std::pow(amrex::max(w, Real(0.01)), Real(0.5));
633 
634  if (supersaturation > s_crit && nn > Real(0.0)) {
635  // Activation rate: convert aerosols to droplets
636  Real nc_activate = amrex::min(nn, Real(0.1) * (supersaturation - s_crit) / s_crit * nn);
637 
638  // Update concentrations
639  nc += nc_activate;
640  nn -= nc_activate;
641 
642  // Enforce physical bounds
643  nc = amrex::max(nc, Real(1.e1));
644  nn = amrex::max(nn, Real(0.0));
645  }
646 }
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ qv
Definition: ERF_Kessler.H:31
@ nc
Definition: ERF_Morrison.H:46
@ qc
Definition: ERF_SatAdj.H:42
@ nn
Definition: ERF_WDM6.H:32

◆ 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 
)
80  {
81  return (amrex::max(b,qmin_arg)-c)
82  /(Real(1.0)+d*d/(rv_arg*e)*c/(a*a));
83 }

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 
)
28  {
29  return cpd_arg*(Real(1.0)-amrex::max(x,qmin_arg))
30  +amrex::max(x,qmin_arg)*cpv_arg;
31 }

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 
)
39  {
40 #ifdef ERF_WDM6_F32_LITERALS
41  return Real(std::pow(static_cast<float>(base), static_cast<float>(exponent)));
42 #else
43  return Real(std::pow(base, exponent));
44 #endif
45 }

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 
)
64  {
65  return d*a*a/(wdm6_xka(c,d)*rv_arg*c*c)
66  + Real(1.0)/(e*wdm6_diffus(c,b));
67 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_xka(Real x, Real y)
Definition: ERF_AdvanceWDM6.cpp:58
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_diffus(Real x, Real y)
Definition: ERF_AdvanceWDM6.cpp:48

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 
)
48  {
49  return (wdm6_literal(8.794e-5)*std::exp(std::log(x)*wdm6_literal(1.81)))/y;
50 }
constexpr amrex::Real wdm6_literal(double d)
Definition: ERF_WDM6.H:96

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 
)
112  {
113  // Fortran statement function, ERF_module_mp_wdm6.F90:562:
114  // lamdac(x,y,z) = exp(log(((pidnc*z)/(x*y)))*((.33333333)))
115  // called as lamdac(qci(i,k,1), den(i,k), ncr(i,k,2)), so x=qc, y=den, z=nc.
116  // The exponent literal is unsuffixed in the Fortran and therefore obeys the
117  // LITERAL PRECISION CONTRACT; it is not 1/3.
118  return std::exp(std::log((pidnc_arg * nc) / (qc * den)) * wdm6_literal(0.33333333));
119 }
@ den
Definition: ERF_AdvanceWDM6.cpp:272

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 
)
207  {
208  // Graupel slope parameter (single-moment)
209  return std::sqrt(std::sqrt(pidn0g_arg/(x*y)));
210 }

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

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 
)
201  {
202  // Snow slope parameter (single-moment)
203  return std::sqrt(std::sqrt(pidn0s_arg*z/(x*y)));
204 }

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

◆ 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 
)
310 {
311  auto WD = [&](int k) -> amrex::Real& {
312  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::wd);
313  };
314  auto WA = [&](int k) -> amrex::Real& {
315  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::wa);
316  };
317  auto WA2 = [&](int k) -> amrex::Real& {
318  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::wa2);
319  };
320  auto WAS = [&](int k) -> amrex::Real& {
321  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::was);
322  };
323  auto QN = [&](int k) -> amrex::Real& {
324  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qn);
325  };
326  auto QN2 = [&](int k) -> amrex::Real& {
327  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qn2);
328  };
329  auto QQ = [&](int k) -> amrex::Real& {
330  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qq);
331  };
332  auto QQ2 = [&](int k) -> amrex::Real& {
333  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qq2);
334  };
335  auto QR = [&](int k) -> amrex::Real& {
336  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qr);
337  };
338  auto QR2 = [&](int k) -> amrex::Real& {
339  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::qr2);
340  };
341  auto DZ = [&](int k) -> amrex::Real& {
342  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::dz);
343  };
344  auto DEN = [&](int k) -> amrex::Real& {
345  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::den);
346  };
347  auto DENFAC = [&](int k) -> amrex::Real& {
348  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::denfac);
349  };
350  auto TK = [&](int k) -> amrex::Real& {
351  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::tk);
352  };
353  auto WW = [&](int k) -> amrex::Real& {
354  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::work_col);
355  };
356  auto RQL = [&](int k) -> amrex::Real& {
357  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::rq_col);
358  };
359  auto RQL2 = [&](int k) -> amrex::Real& {
360  return sed_cell(i_s, j_s, klo_s + k, WDM6SedCellScratch::rq2_col);
361  };
362 
363  auto WI = [&](int k) -> amrex::Real& {
364  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::wi);
365  };
366  auto ZI = [&](int k) -> amrex::Real& {
367  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::zi);
368  };
369  auto ZA = [&](int k) -> amrex::Real& {
370  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::za);
371  };
372  auto DZA = [&](int k) -> amrex::Real& {
373  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::dza);
374  };
375  auto QA = [&](int k) -> amrex::Real& {
376  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::qa);
377  };
378  auto QA2 = [&](int k) -> amrex::Real& {
379  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::qa2);
380  };
381  auto QMI = [&](int k) -> amrex::Real& {
382  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::qmi);
383  };
384  auto QPI = [&](int k) -> amrex::Real& {
385  return sed_node(i_s, j_s, klo_s + k, WDM6SedNodeScratch::qpi);
386  };
387 
388  Real allold = Real(0.0);
389  for (int k = 0; k < km; ++k) {
390  QQ(k) = RQL(k);
391  QQ2(k) = RQL2(k);
392  WD(k) = WW(k);
393  allold += QQ(k) + QQ2(k);
394  }
395 
396  precip1[0] = Real(0.0);
397  precip2[0] = Real(0.0);
398  if (allold <= Real(0.0)) {
399  return;
400  }
401 
402  ZI(0) = Real(0.0);
403  for (int k = 0; k < km; ++k) {
404  ZI(k + 1) = ZI(k) + DZ(k);
405  }
406 
407  auto rebuild = [&]() {
408  WI(0) = WW(0);
409  if (km > 1) {
410  WI(1) = Real(0.5) * (WW(1) + WW(0));
411  for (int k = 2; k < km - 1; ++k) {
412  WI(k) = Real(9.0) / Real(16.0) * (WW(k) + WW(k - 1))
413  - Real(1.0) / Real(16.0) * (WW(k + 1) + WW(k - 2));
414  }
415  WI(km - 1) = Real(0.5) * (WW(km - 1) + WW(km - 2));
416  }
417  WI(km) = WW(km - 1);
418  for (int k = 1; k < km; ++k) {
419  if (WW(k) == Real(0.0)) WI(k) = WW(k - 1);
420  }
421 
422  constexpr Real con1 = wdm6_literal(0.05);
423  for (int k = km - 1; k >= 0; --k) {
424  const Real decfl = (WI(k + 1) - WI(k)) * dt / DZ(k);
425  if (decfl > con1) {
426  WI(k) = WI(k + 1) - con1 * DZ(k) / dt;
427  }
428  }
429 
430  for (int k = 0; k <= km; ++k) {
431  ZA(k) = ZI(k) - WI(k) * dt;
432  }
433  for (int k = 0; k < km; ++k) {
434  DZA(k) = ZA(k + 1) - ZA(k);
435  QA(k) = QQ(k) * DZ(k) / DZA(k);
436  QA2(k) = QQ2(k) * DZ(k) / DZA(k);
437  QR(k) = QA(k) / DEN(k);
438  QR2(k) = QA2(k) / DEN(k);
439  }
440  DZA(km) = ZI(km) - ZA(km);
441  QA(km) = Real(0.0);
442  QA2(km) = Real(0.0);
443  };
444 
445  rebuild();
446 
447  if (iter > 0) {
448  for (int k = 0; k < km; ++k) {
449  Real rslope, rslopeb, rslope2, rslope3, vt, n0sfac_dummy;
450  wdm6_slope_snow_cell(QR(k), DEN(k), DENFAC(k), TK(k),
451  pidn0s, alpha, n0smax, n0s, t0c, qcrmin,
453  bvts, pvts, rslope, rslopeb, rslope2, rslope3, vt,
454  n0sfac_dummy);
455  WA(k) = vt;
456  wdm6_slope_graup_cell(QR2(k), DEN(k), DENFAC(k),
459  rslope, rslopeb, rslope2, rslope3, vt);
460  WA2(k) = vt;
461  }
462  for (int k = 0; k < km; ++k) {
463  const Real tmpq = amrex::max(QR(k) + QR2(k), wdm6_literal(1.0e-15));
464  WA(k) = (tmpq > wdm6_literal(1.0e-15))
465  ? (WA(k) * QR(k) + WA2(k) * QR2(k)) / tmpq
466  : Real(0.0);
467  WW(k) = Real(0.5) * (WD(k) + WA(k));
468  WAS(k) = WA(k);
469  }
470  rebuild();
471  }
472 
473  for (int ist = 0; ist < 2; ++ist) {
474  const int qn_comp = (ist == 0)
477  const int qa_comp = (ist == 0)
480  auto qn_dst = [&](int k) -> amrex::Real& {
481  return sed_cell(i_s, j_s, klo_s + k, qn_comp);
482  };
483  auto qa_src = [&](int k) -> amrex::Real& {
484  return sed_node(i_s, j_s, klo_s + k, qa_comp);
485  };
486  Real* precip_dst = (ist == 0) ? precip1 : precip2;
487 
488  QPI(0) = qa_src(0);
489  QMI(0) = qa_src(0);
490  QMI(km) = qa_src(km);
491  QPI(km) = qa_src(km);
492  for (int k = 1; k < km; ++k) {
493  const Real dip = (qa_src(k + 1) - qa_src(k)) / (DZA(k + 1) + DZA(k));
494  const Real dim = (qa_src(k) - qa_src(k - 1)) / (DZA(k - 1) + DZA(k));
495  if (dip * dim <= Real(0.0)) {
496  QMI(k) = qa_src(k);
497  QPI(k) = qa_src(k);
498  } else {
499  QPI(k) = qa_src(k) + Real(0.5) * (dip + dim) * DZA(k);
500  QMI(k) = Real(2.0) * qa_src(k) - QPI(k);
501  if (QPI(k) < Real(0.0) || QMI(k) < Real(0.0)) {
502  QPI(k) = qa_src(k);
503  QMI(k) = qa_src(k);
504  }
505  }
506  }
507 
508  for (int k = 0; k < km; ++k) {
509  qn_dst(k) = Real(0.0);
510  }
511  int kb = 0;
512  int kt = 0;
513  for (int k = 0; k < km; ++k) {
514  // Fortran backs both brackets off by one at the TOP of every k
515  // before searching forward:
516  // kb=max(kb-1,1) ; kt=max(kt-1,1)
517  // Omitting this let the forward search start above the correct
518  // bracket, so cells whose departure interval reaches back below the
519  // previous k's bracket were remapped from the wrong donor.
520  kb = amrex::max(kb - 1, 0);
521  kt = amrex::max(kt - 1, 0);
522 
523  if (ZI(k) >= ZA(km)) break;
524 
525  for (int kk = kb; kk < km; ++kk) {
526  if (ZI(k) <= ZA(kk + 1)) {
527  kb = kk;
528  break;
529  }
530  }
531  for (int kk = kt; kk < km; ++kk) {
532  if (ZI(k + 1) <= ZA(kk)) {
533  kt = kk;
534  break;
535  }
536  }
537  // Fortran writes a bare kt = kt - 1 with no floor. Clamping it to 0
538  // is not equivalent: when the decrement should drop kt BELOW kb,
539  // the Fortran falls through both branches and leaves qn(k) at zero,
540  // whereas the clamp can make kt equal kb and take the single-cell
541  // branch against the wrong donor. kt is only used as an index
542  // inside the two branches, both of which require kt >= kb >= 0, so
543  // a negative value here is safe.
544  kt = kt - 1;
545 
546  if (kt == kb) {
547  const Real tl = (ZI(k) - ZA(kb)) / DZA(kb);
548  const Real th = (ZI(k + 1) - ZA(kb)) / DZA(kb);
549  const Real qqd = Real(0.5) * (QPI(kb) - QMI(kb));
550  qn_dst(k) = (qqd * th * th + QMI(kb) * th
551  - qqd * tl * tl - QMI(kb) * tl) / (th - tl);
552  } else if (kt > kb) {
553  const Real tl = (ZI(k) - ZA(kb)) / DZA(kb);
554  const Real qqd = Real(0.5) * (QPI(kb) - QMI(kb));
555  const Real qql = qqd * tl * tl + QMI(kb) * tl;
556  const Real dql = qa_src(kb) - qql;
557  Real zsum = (Real(1.0) - tl) * DZA(kb);
558  Real qsum = dql * DZA(kb);
559  for (int m = kb + 1; m < kt; ++m) {
560  zsum += DZA(m);
561  qsum += qa_src(m) * DZA(m);
562  }
563  const Real th = (ZI(k + 1) - ZA(kt)) / DZA(kt);
564  const Real dqh = Real(0.5) * (QPI(kt) - QMI(kt)) * th * th + QMI(kt) * th;
565  zsum += th * DZA(kt);
566  qsum += dqh * DZA(kt);
567  qn_dst(k) = qsum / zsum;
568  }
569  }
570 
571  precip_dst[0] = Real(0.0);
572  for (int k = 0; k < km; ++k) {
573  if (ZA(k) < Real(0.0) && ZA(k + 1) < Real(0.0)) {
574  precip_dst[0] += qa_src(k) * DZA(k);
575  } else if (ZA(k) < Real(0.0) && ZA(k + 1) >= Real(0.0)) {
576  precip_dst[0] += qa_src(k) * (Real(0.0) - ZA(k));
577  break;
578  } else {
579  break;
580  }
581  }
582  }
583 
584  for (int k = 0; k < km; ++k) {
585  RQL(k) = QN(k);
586  RQL2(k) = QN2(k);
587  }
588 }
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:217
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:247
@ n0s
Definition: ERF_AdvanceMorrison.cpp:43
@ QR
Definition: ERF_IndexDefines.H:142
constexpr int DZ
Definition: ERF_TwoStreamColumn.H:599
@ qn2
Definition: ERF_AdvanceWDM6.cpp:271
@ was
Definition: ERF_AdvanceWDM6.cpp:271
@ tk
Definition: ERF_AdvanceWDM6.cpp:272
@ qn
Definition: ERF_AdvanceWDM6.cpp:271
@ wa2
Definition: ERF_AdvanceWDM6.cpp:271
@ qr2
Definition: ERF_AdvanceWDM6.cpp:271
@ work_col
Definition: ERF_AdvanceWDM6.cpp:272
@ denfac
Definition: ERF_AdvanceWDM6.cpp:272
@ rq2_col
Definition: ERF_AdvanceWDM6.cpp:272
@ wa
Definition: ERF_AdvanceWDM6.cpp:271
@ qq
Definition: ERF_AdvanceWDM6.cpp:271
@ qq2
Definition: ERF_AdvanceWDM6.cpp:271
@ wd
Definition: ERF_AdvanceWDM6.cpp:271
@ rq_col
Definition: ERF_AdvanceWDM6.cpp:272
@ dz
Definition: ERF_AdvanceWDM6.cpp:272
@ zi
Definition: ERF_AdvanceWDM6.cpp:276
@ qa2
Definition: ERF_AdvanceWDM6.cpp:276
@ qpi
Definition: ERF_AdvanceWDM6.cpp:276
@ qa
Definition: ERF_AdvanceWDM6.cpp:276
@ za
Definition: ERF_AdvanceWDM6.cpp:276
@ wi
Definition: ERF_AdvanceWDM6.cpp:276
@ qmi
Definition: ERF_AdvanceWDM6.cpp:276
@ dza
Definition: ERF_AdvanceWDM6.cpp:276
@ qsum
Definition: ERF_WSM6.H:323
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 
)
122  {
123  // rslopec is the RECIPROCAL of lamdac: the Fortran writes
124  // rslopec(i,k) = 1./lamdac(qci(i,k,1),den(i,k),ncr(i,k,2))
125  // at both of its two sites (G3 at :915 and G11 at :1544). Returning lamdac
126  // itself here put rslopec off by 1/lamdac^2, about nine orders of magnitude
127  // once cloud water exists. It was invisible until cloud water first formed,
128  // because with qc <= qmin both legs take the rslopecmax branch instead.
129  return Real(1.0) / wdm6_lamdac_exact(qc, den, nc, pidnc_arg);
130 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdac_exact(Real qc, Real den, Real nc, Real pidnc_arg)
Definition: ERF_AdvanceWDM6.cpp:112

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 
)
254 {
255  if (qg <= qcrmin_arg) {
256  rslope = rslopegmax_arg;
257  rslopeb = rslopegbmax_arg;
258  rslope2 = rslopeg2max_arg;
259  rslope3 = rslopeg3max_arg;
260  } else {
261  rslope = Real(1.0)/wdm6_lamdag(qg,den,pidn0g_arg);
262  rslopeb = std::pow(rslope,bvtg_arg);
263  rslope2 = rslope*rslope;
264  rslope3 = rslope2*rslope;
265  }
266  vt = pvtg_arg*rslopeb*denfac;
267  if (qg <= Real(0.0)) { vt = Real(0.0); }
268 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_lamdag(Real x, Real y, Real pidn0g_arg)
Definition: ERF_AdvanceWDM6.cpp:207
@ qg
Definition: ERF_WDM6.H:31

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

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

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 
)
70  {
71  // Fortran: exp(log((viscos(b,c)/diffus(b,a)))*((.3333333))) / sqrt(viscos) * sqrt(sqrt(den0/c))
72  return std::exp(std::log(wdm6_viscos(b,c)/wdm6_diffus(b,a))
73  *wdm6_literal(0.3333333))
74  /std::sqrt(wdm6_viscos(b,c))
75  *std::sqrt(std::sqrt(den0_arg/c));
76 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE Real wdm6_viscos(Real x, Real y)
Definition: ERF_AdvanceWDM6.cpp:53

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 
)
53  {
54  return wdm6_literal(1.496e-6)*(x*std::sqrt(x))/(x+Real(120.0))/y;
55 }

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 
)
58  {
59  return Real(1.414e3)*wdm6_viscos(x,y)*y;
60 }

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 
)
34  {
35  return xlv0_arg - xlv1_arg*(x - t0c_arg);
36 }

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 
)
133  {
134  Real temp = den * amrex::max(qi, qmin_arg);
135  temp = std::sqrt(std::sqrt(temp * temp * temp));
136  return amrex::min(amrex::max(Real(5.38e7) * temp, Real(1.e3)), Real(1.e6));
137 }
@ qi
Definition: ERF_WDM6.H:28

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().