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 <limits>
#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 
)
631  {
632  // Only activate if supersaturated and cloud exists
633  if (qv <= qvs || qc < Real(1.e-8)) return;
634 
635  // Supersaturation
636  Real supersaturation = qv / qvs - Real(1.0);
637  supersaturation = amrex::min(supersaturation, Real(0.0048)); // satmax - 1
638 
639  // Critical supersaturation based on updraft strength
640  // From WRF: stronger updrafts -> more activation
641  Real s_crit = Real(0.6) * std::pow(amrex::max(w, Real(0.01)), Real(0.5));
642 
643  if (supersaturation > s_crit && nn > Real(0.0)) {
644  // Activation rate: convert aerosols to droplets
645  Real nc_activate = amrex::min(nn, Real(0.1) * (supersaturation - s_crit) / s_crit * nn);
646 
647  // Update concentrations
648  nc += nc_activate;
649  nn -= nc_activate;
650 
651  // Enforce physical bounds
652  nc = amrex::max(nc, Real(1.e1));
653  nn = amrex::max(nn, Real(0.0));
654  }
655 }
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:23
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ qv
Definition: ERF_Kessler.H:31
@ nc
Definition: ERF_Morrison.H:47
@ nn
Definition: ERF_WDM6.H:33
@ qc
Definition: ERF_Morrison.H:110

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

Referenced by WDM6::AdvanceColumns().

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

Referenced by WDM6::AdvanceColumns().

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

Referenced by WDM6::AdvanceColumns().

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

Referenced by WDM6::AdvanceColumns().

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

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

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

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

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

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   
)
600  {
601  // Volume-weighted mean diameter of cloud droplets
602  // Returns diameter in meters
603  if (nc < Real(1.e1) || qc < Real(1.e-9)) return Real(0.0);
604 
605  // Mean mass = rho * qc / nc
606  // Mean volume = mean mass / rho_water
607  // Mean diameter = (6 * volume / pi)^(1/3)
608  Real mean_mass = den * qc / (nc * den); // kg per droplet
609  Real mean_volume = mean_mass / Real(1000.0); // m^3 per droplet
610  Real diameter = std::pow(Real(6.0) * mean_volume / Real(3.14159265359), Real(1.0)/Real(3.0));
611 
612  return diameter;
613 }

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

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

Referenced by WDM6::AdvanceColumns().

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

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

Referenced by WDM6::AdvanceColumns().

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

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

Referenced by WDM6::AdvanceColumns().

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

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

Referenced by WDM6::AdvanceColumns(), 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 
)
35  {
36  return xlv0_arg - xlv1_arg*(x - t0c_arg);
37 }

Referenced by WDM6::AdvanceColumns().

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

Referenced by WDM6::AdvanceColumns().

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