ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_SuperDropletPCRiming.H
Go to the documentation of this file.
1 #ifndef SUPERDROPLET_PC_RIMING_H_
2 #define SUPERDROPLET_PC_RIMING_H_
3 
4 #include <cmath>
5 #include <algorithm>
6 
7 #include <AMReX_REAL.H>
8 #include <AMReX_GpuQualifiers.H>
9 #include <AMReX_Math.H>
10 
11 #include "ERF_NumericalConstants.H"
13 #include "ERF_MicrophysicsUtils.H"
15 
16 #ifdef ERF_USE_PARTICLES
17 
18 /*! \brief Riming microphysics helpers (dynamic viscosity, Rasmussen-Heymsfield
19  * impact velocity, ice surface temperature, and Heymsfield-Pflaum rime density).
20  * Factored out of ERF_SuperDropletPCCoalescence.cpp into this header, and made
21  * host+device, so they can be exercised by unit tests. */
22 namespace SDRiming
23 {
24  using amrex::Real;
25  using amrex::ParticleReal;
26 
27  /*! \brief Compute dynamic viscosity */
28  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
29  auto viscCoeff ( const ParticleReal a_T /*!< temperature */ )
30  {
31  auto T_degC = a_T - ParticleReal(tmelt); // [K] => [degC]
32  ParticleReal visc_coeff = ParticleReal(zero);
33  if( T_degC >= ParticleReal(zero) ) {
34  visc_coeff = ( ParticleReal(1.7180) + ParticleReal(4.9E-3)*T_degC ) * ParticleReal(1.E-5);
35  } else {
36  visc_coeff = ( ParticleReal(1.7180) + ParticleReal(4.9E-3)*T_degC - ParticleReal(1.2E-5)*T_degC*T_degC ) * ParticleReal(1.E-5);
37  }
38  return visc_coeff;
39  }
40 
41  /*! \brief Impact velocity ratio from Rasmussen and Heymsfield (1985)
42  *
43  * Computes the ratio of impact velocity to relative velocity for ice-droplet
44  * collisions as a function of Reynolds and Stokes numbers.
45  *
46  * Reference: Rasmussen, R. M., and A. J. Heymsfield, 1985: A generalized
47  * form for impact velocities used to determine graupel accretional densities.
48  * J. Atmos. Sci., 42, 2275-2279.
49  * https://doi.org/10.1175/1520-0469(1985)042<2275:AGFFIV>2.0.CO;2
50  *
51  * Polynomial fit: v_impact/v_rel = A0 + A1*w + A2*w^2 + A3*w^3 + A4*w^4
52  * where w = log10(St)
53  */
54  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
55  auto impactVelocity_RasmussenHeymsfield1985( const ParticleReal a_Re, /*!< Reynolds number */
56  const ParticleReal a_St /*!< Stokes number */ )
57  {
58  // the fits only apply for St >= 0.1; log10(0) raises FE_DIVBYZERO under fpe_trap_zero
59  ParticleReal w(zero), w2(zero), w3(zero), w4(zero);
60  if (a_St >= ParticleReal(0.1)) {
61  w = std::log10(a_St);
62  w2 = w*w;
63  w3 = w2*w;
64  w4 = w3*w;
65  }
66  ParticleReal retval = ParticleReal(zero);
67  if(a_Re < (ParticleReal(10.0)+ParticleReal(30.0))*ParticleReal(myhalf)) {
68  if (a_St < ParticleReal(0.4)) {
69  retval = ParticleReal(zero);
70  } else if (a_St<ParticleReal(10.0)) {
71  retval = ParticleReal(0.1701) + ParticleReal(0.7246)*w + ParticleReal(0.2257)*w2 - ParticleReal(1.13)*w3 + ParticleReal(0.5756)*w4;
72  } else {
73  retval = ParticleReal(0.57);
74  }
75  } else if (a_Re < (ParticleReal(30.0)+ParticleReal(100.0))*ParticleReal(myhalf)) {
76  if (a_St < ParticleReal(0.1)) {
77  retval = ParticleReal(zero);
78  } else if (a_St < ParticleReal(10.0)) {
79  retval = ParticleReal(0.2927) + ParticleReal(0.5085)*w - ParticleReal(0.03453)*w2 - ParticleReal(0.2184)*w3 + ParticleReal(0.03595)*w4;
80  } else {
81  retval = ParticleReal(0.59);
82  }
83  } else if (a_Re < (ParticleReal(100.0)+ParticleReal(300.0))*ParticleReal(myhalf)) {
84  if (a_St < ParticleReal(0.1)) {
85  retval = ParticleReal(zero);
86  } else if (a_St < ParticleReal(10.0)) {
87  retval = ParticleReal(0.3272) + ParticleReal(0.4907)*w - ParticleReal(0.09452)*w2 - ParticleReal(0.1906)*w3 + ParticleReal(0.07105)*w4;
88  } else {
89  retval = ParticleReal(0.61);
90  }
91  } else {
92  if (a_St < ParticleReal(0.1)) {
93  retval = ParticleReal(zero);
94  } else if (a_St < ParticleReal(10.0)) {
95  retval = ParticleReal(0.356) + ParticleReal(0.4738)*w - ParticleReal(0.1233)*w2 - ParticleReal(0.1618)*w3 + ParticleReal(0.08087)*w4;
96  } else {
97  retval = ParticleReal(0.63);
98  }
99  }
100  retval = std::max(retval,ParticleReal(zero));
101  return retval;
102  }
103 
104  /*! \brief Ice surface temperature */
105  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
106  ParticleReal iceSurfaceTemperature( const ParticleReal a_T, /*!< temperature */
107  const ParticleReal a_P, /*!< pressure */
108  const ParticleReal a_qv, /*!< vapour fraction */
109  const ParticleReal a_D, /*!< diffusivity coeff */
110  const SDMassChangeUtils_SV::dMdt<ParticleReal>& a_dmdt /*!< mass change utilities */)
111  {
112  // erf_qsati takes pressure in mbar and erf_esati returns mbar; a_P arrives in Pa
113  Real qsat_r = Real(zero); erf_qsati(a_T, a_P/Real(100.0), qsat_r);
114  ParticleReal qsat = ParticleReal(qsat_r);
115  auto sup_sat = a_qv/qsat - ParticleReal(one);
116  auto drho = sup_sat / (a_D * a_dmdt.Fk_plus_Fd(a_T, ParticleReal(erf_esati(a_T)*Real(100.0)), a_D));
117  auto dT = (a_dmdt.L * a_D / a_dmdt.K) * drho;
118  return a_T + dT;
119  }
120 
121  /*! \brief Rime density parameterization from Heymsfield and Pflaum (1985)
122  *
123  * Computes the density of rime accreted onto ice particles based on the
124  * dimensionless impact parameter Y = -r[um] * v_impact / T_surf[degC].
125  *
126  * Reference: Heymsfield, A. J., and J. C. Pflaum, 1985: A quantitative
127  * assessment of the accuracy of techniques for calculating graupel growth.
128  * J. Atmos. Sci., 42, 2264-2274.
129  *
130  * Two regimes based on surface temperature and Y parameter:
131  * - Low-density (T_surf <= -5 degC or Y < 1.6): rho = 0.30*Y^0.44 [Eq. 8]
132  * - High-density (otherwise): rho = exp(-0.03115 - 1.703*Y + 0.9116*Y^2 - 0.1224*Y^3) [Eq. 9]
133  *
134  * Output bounded to 0.1 <= rho_rime <= 0.91 g/cm^3 (100-910 kg/m^3)
135  */
136  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
137  auto rimeDensity_HeymsfieldPflaum1985( const ParticleReal a_radius, /*!< droplet radius [m] */
138  const ParticleReal a_a, /*!< ice particle equatorial radius [m] */
139  const ParticleReal a_c, /*!< ice particle polar radius [m] */
140  const ParticleReal a_vz_w, /*!< vertical velocity of water droplet [m/s] */
141  const ParticleReal a_vz_i, /*!< vertical velocity of ice particle [m/s] */
142  const ParticleReal a_rho_w, /*!< water density [kg/m^3] */
143  const ParticleReal a_D, /*!< diffusivity coefficient [m^2/s] */
144  const SDMassChangeUtils_SV::dMdt<ParticleReal>& a_dmdt, /*!< mass change utilities */
145  const ParticleReal a_T, /*!< temperature [K] */
146  const ParticleReal a_rhom, /*!< moist air density [kg/m^3] */
147  const ParticleReal a_P, /*!< pressure [Pa] */
148  const ParticleReal a_qv /*!< water vapor mixing ratio [kg/kg] */)
149  {
150  auto r_um = a_radius * ParticleReal(1.0e6);
151  auto mu = viscCoeff(a_T);
152 
153  auto maxD = ParticleReal(two) * std::max(a_a,a_c);
154  auto Re = a_rhom * maxD * std::abs(a_vz_i) / mu;
155  auto eqr_i = std::cbrt(a_a*a_a*a_c);
156  auto St = ParticleReal(two)*std::abs(a_vz_i)*a_radius*a_radius*a_rho_w/(ParticleReal(9.0)*mu*eqr_i);
157 
158  auto v_impact_ratio = impactVelocity_RasmussenHeymsfield1985(Re,St);
159  auto v_impact = std::abs(a_vz_i - a_vz_w) * v_impact_ratio;
160 
161  auto T_surf = std::min(ParticleReal(-0.01), iceSurfaceTemperature(a_T,a_P,a_qv,a_D,a_dmdt) - ParticleReal(tmelt));
162  auto var_Y = -r_um * v_impact/T_surf;
163 
164  ParticleReal rho_rime = ParticleReal(zero);
165  if ((T_surf <= ParticleReal(-5.0)) || (var_Y < ParticleReal(1.6))) {
166  rho_rime = ParticleReal(0.30) * std::pow(var_Y, ParticleReal(0.44));
167  } else {
168  var_Y = std::min(var_Y,ParticleReal(3.5));
169  rho_rime = std::exp(ParticleReal(-0.03115) - ParticleReal(1.7030)*var_Y + ParticleReal(0.9116)*var_Y*var_Y - ParticleReal(0.1224)*var_Y*var_Y*var_Y);
170  }
171  rho_rime = std::min(ParticleReal(0.91),std::max(rho_rime,ParticleReal(0.1))) * ParticleReal(1000.0);
172  return rho_rime;
173  }
174 
175 } // namespace SDRiming
176 
177 #endif
178 #endif
Physical constants and tuning parameters used only by the moisture and cloud-physics code.
constexpr amrex::Real tmelt
Definition: ERF_MicrophysicsConstants.H:122
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real erf_esati(amrex::Real t)
Definition: ERF_MicrophysicsUtils.H:67
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void erf_qsati(amrex::Real t, amrex::Real p, amrex::Real &qsati)
Definition: ERF_MicrophysicsUtils.H:254
Dimensionless numeric literals and pure mathematical constants.
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:31
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:30
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:29
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:34
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ mu
Definition: ERF_AdvanceMorrison.cpp:92