ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_SuperDropletPCCoalescence.H
Go to the documentation of this file.
1 #ifndef SUPERDROPLET_PC_COALESCENCE_H_
2 #define SUPERDROPLET_PC_COALESCENCE_H_
3 
4 #include <algorithm>
5 #include "ERF_Constants.H"
7 
8 #ifdef ERF_USE_PARTICLES
9 
10 /*! \brief Field indices for 2-field interpolation (ice-water collision efficiency) */
11 AMREX_ENUM(InterpFieldsTR,
12  temperature,
13  moist_density,
14  NUM_FIELDS
15 );
16 
17 /*! \brief Field indices for 4-field interpolation (riming) */
18 AMREX_ENUM(InterpFieldsFull,
19  temperature,
20  pressure,
21  moist_density,
22  qv,
23  NUM_FIELDS
24 );
25 
26 /*! \brief Coalescence kernels
27 
28  Reference for Long's and Hall's kernels:
29  + Shima et al., 2009, "The super-droplet method for the numerical simulation of clouds and
30  precipitation: A particle-based and probabilistic microphysics model coupled with a non-
31  hydrostatic model." Quart. J. Roy. Meteorol. Soc., 135: 1307-1320
32  + https://github.com/Shima-Lab/SCALE-SDM_BOMEX_Sato2018/blob/master/contrib/SDM/sdm_motion.f90
33 */
34 template<typename RT, int SPACEDIM>
35 struct CollisionKernel
36 {
37  const RT lambda_mfp = RT(6.62e-8); // mean free path of air
38  const RT para_Asc = RT(2.51);
39  const RT para_Bsc = RT(0.8);
40  const RT para_Csc = RT(0.55);
41  const RT k_coeff = RT(1.0);
42 
43  /*! \brief Golovin kernel */
44  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
45  RT golovin ( const RT a_r1, /*!< radius of droplet 1 */
46  const RT a_r2 /*!< radius of droplet 2 */ ) const noexcept
47  {
48  RT b = RT(1.5e03);
49  auto X_1 = RT(four_thirds_pi) * a_r1*a_r1*a_r1;
50  auto X_2 = RT(four_thirds_pi) * a_r2*a_r2*a_r2;
51  return b * (X_1 + X_2);
52  }
53 
54  /* The following code is adapted from SCALE-SDM:
55  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
56  /*! \brief Calculate collision kernel for gravitational sedimentation
57  *
58  * This function computes the collision kernel for collisions driven by
59  * differential gravitational settling. It uses the terminal velocities
60  * of particles to determine collision probability.
61  *
62  * \param[in] a_r1 Radius of particle 1 (m)
63  * \param[in] a_r2 Radius of particle 2 (m)
64  * \param[in] a_v1 Pointer to velocity vector of particle 1 (m/s)
65  * \param[in] a_v2 Pointer to velocity vector of particle 2 (m/s)
66  * \param[in] a_x1 Pointer to position vector of particle 1 (m)
67  * \param[in] a_x2 Pointer to position vector of particle 2 (m)
68  * \param[in] a_vel_type Kernel relative velocity type
69  * \return Collision kernel value (m³/s)
70  */
71  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
72  RT sedimentation ( const RT a_r1,
73  const RT a_r2,
74  const RT* const a_v1,
75  const RT* const a_v2,
76  const RT* const a_x1,
77  const RT* const a_x2,
78  const SDKernelRelativeVelocityType a_vel_type ) const noexcept
79  {
80  auto p = std::min(a_r1,a_r2) / std::max(a_r1,a_r2);
81  auto E = RT(0.5)*p*p / ((RT(1.0)+p)*(RT(1.0)+p));
82 
83  RT dv = 0;
84  if (a_vel_type == SDKernelRelativeVelocityType::radial_velocity) {
85  RT dr = 0, dv2 = 0;
86  for (int d = 0; d < SPACEDIM; d++) {
87  const RT ddv = a_v1[d]-a_v2[d];
88  const RT ddx = a_x1[d]-a_x2[d];
89  dv += ddv*ddx;
90  dr += ddx*ddx;
91  dv2 += ddv*ddv;
92  }
93  // radial component; fall back to the full relative speed for coincident pairs
94  dv = (dr > RT(0)) ? std::abs(dv) / std::sqrt(dr) : std::sqrt(dv2);
95  } else {
96  for (int d = 0; d < SPACEDIM; d++) {
97  dv += (a_v1[d]-a_v2[d])*(a_v1[d]-a_v2[d]);
98  }
99  dv = std::sqrt(dv);
100  }
101 
102  return PI*(a_r1+a_r2)*(a_r1+a_r2)*E*dv;
103  }
104 
105  /* The following code is adapted from SCALE-SDM:
106  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
107  /*! \brief Calculate collision kernel using Long's formulation
108  *
109  * Long's kernel is an empirical formulation for cloud droplet collision-coalescence
110  * that accounts for different collision rates based on droplet size. It is particularly
111  * effective for smaller cloud droplets where hydrodynamic effects are important.
112  *
113  * Reference: Long, A. B., 1974: Solutions to the droplet collection equation
114  * for polynomial kernels. J. Atmos. Sci., 31, 1040-1052.
115  *
116  * \param[in] a_r1 Radius of particle 1 (m)
117  * \param[in] a_r2 Radius of particle 2 (m)
118  * \param[in] a_v1 Pointer to velocity vector of particle 1 (m/s)
119  * \param[in] a_v2 Pointer to velocity vector of particle 2 (m/s)
120  * \param[in] a_x1 Pointer to position vector of particle 1 (m)
121  * \param[in] a_x2 Pointer to position vector of particle 2 (m)
122  * \param[in] a_vel_type Kernel relative velocity type
123  * \return Collision kernel value (m³/s)
124  */
125  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
126  RT Longs ( const RT a_r1,
127  const RT a_r2,
128  const RT* const a_v1,
129  const RT* const a_v2,
130  const RT* const a_x1,
131  const RT* const a_x2,
132  const SDKernelRelativeVelocityType a_vel_type ) const noexcept
133  {
134  auto r1 = std::max( a_r1, a_r2 ); // large
135  auto r2 = std::min( a_r1, a_r2 ); // small
136  auto sumr = r1 + r2;
137 
138  RT c_rate = RT(1.0);
139  if( r1 <= RT(5.E-5) ) {
140  c_rate = RT(4.5E+8) * ( r1*r1 ) * ( RT(1.0) - RT(3.E-6)/(std::max(RT(3.01E-6),r1)) );
141  }
142  c_rate *= (PI*sumr*sumr);
143 
144  RT dv = 0;
145  if (a_vel_type == SDKernelRelativeVelocityType::radial_velocity) {
146  RT dr = 0, dv2 = 0;
147  for (int d = 0; d < SPACEDIM; d++) {
148  const RT ddv = a_v1[d]-a_v2[d];
149  const RT ddx = a_x1[d]-a_x2[d];
150  dv += ddv*ddx;
151  dr += ddx*ddx;
152  dv2 += ddv*ddv;
153  }
154  // radial component; fall back to the full relative speed for coincident pairs
155  dv = (dr > RT(0)) ? std::abs(dv) / std::sqrt(dr) : std::sqrt(dv2);
156  } else {
157  for (int d = 0; d < SPACEDIM; d++) {
158  dv += (a_v1[d]-a_v2[d])*(a_v1[d]-a_v2[d]);
159  }
160  dv = std::sqrt(dv);
161  }
162 
163  return c_rate*dv;
164  }
165 
166  /* The following code is adapted from SCALE-SDM:
167  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
168  /*! \brief r0col data for Hall's kernel */
169  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
170  RT Halls_data_r0col (const int a_i /*!< index */) const noexcept
171  {
172  static const RT vec[15] = { RT(6.0),
173  RT(8.0),
174  RT(10.0),
175  RT(15.0),
176  RT(20.0),
177  RT(25.0),
178  RT(30.0),
179  RT(40.0),
180  RT(50.0),
181  RT(60.0),
182  RT(70.0),
183  RT(100.0),
184  RT(150.0),
185  RT(200.0),
186  RT(300.0)};
187  return vec[a_i];
188  }
189 
190  /* The following code is adapted from SCALE-SDM:
191  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
192  /*! \brief r0col data for Hall's kernel */
193  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
194  int Halls_data_r0col ( const RT a_r /*!< radius */) const noexcept
195  {
196  auto radius_microns = a_r*RT(1.0e6); // [m] -> [mu-m]
197  int i;
198  for (i = 0; i < 15; i++) {
199  if (radius_microns <= Halls_data_r0col(i)) { return i; }
200  }
201  return i;
202  }
203 
204  /* The following code is adapted from SCALE-SDM:
205  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
206  /*! \brief ratcol data for Hall's kernel */
207  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
208  RT Halls_data_ratcol ( const int a_i /*!< index */) const noexcept
209  {
210  static const RT vec[21] = {RT(0.00),
211  RT(0.05),
212  RT(0.10),
213  RT(0.15),
214  RT(0.20),
215  fourth,
216  RT(0.30),
217  RT(0.35),
218  RT(0.40),
219  RT(0.45),
220  RT(0.50),
221  RT(0.55),
222  RT(0.60),
223  RT(0.65),
224  RT(0.70),
225  RT(0.75),
226  RT(0.80),
227  RT(0.85),
228  RT(0.90),
229  RT(0.95),
230  RT(1.00)};
231  return vec[a_i];
232  }
233 
234  /* The following code is adapted from SCALE-SDM:
235  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
236  /*! \brief ratcol data for Hall's kernel */
237  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
238  int Halls_data_ratcol ( const RT a_r /*!< ratio */) const noexcept
239  {
240  int i;
241  for (i = 1; i < 20; i++) {
242  if (a_r <= Halls_data_ratcol(i)) { return i; }
243  }
244  return i;
245  }
246 
247  /* The following code is adapted from SCALE-SDM:
248  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
249  /*! \brief ecoll data for Hall's kernel */
250  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
251  RT Halls_data_ecoll ( const int a_i, /*!< row index */
252  const int a_j /*!< column index */) const noexcept
253  {
254  const RT ecoll[21][15] =
255  { {RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010),RT(0.0010)},
256  {RT(0.0030),RT(0.0030),RT(0.0030),RT(0.0040),RT(0.0050),RT(0.0050),RT(0.0050),RT(0.0100),RT(0.1000),RT(0.0500),RT(0.2000),RT(0.5000),RT(0.7700),RT(0.8700),RT(0.9700)},
257  {RT(0.0070),RT(0.0070),RT(0.0070),RT(0.0080),RT(0.0090),RT(0.0100),RT(0.0100),RT(0.0700),RT(0.4000),RT(0.4300),RT(0.5800),RT(0.7900),RT(0.9300),RT(0.9600),RT(1.0000)},
258  {RT(0.0090),RT(0.0090),RT(0.0090),RT(0.0120),RT(0.0150),RT(0.0100),RT(0.0200),RT(0.2800),RT(0.6000),RT(0.6400),RT(0.7500),RT(0.9100),RT(0.9700),RT(0.9800),RT(1.0000)},
259  {RT(0.0140),RT(0.0140),RT(0.0140),RT(0.0150),RT(0.0160),RT(0.0300),RT(0.0600),RT(0.5000),RT(0.7000),RT(0.7700),RT(0.8400),RT(0.9500),RT(0.9700),RT(1.0000),RT(1.0000)},
260  {RT(0.0170),RT(0.0170),RT(0.0170),RT(0.0200),RT(0.0220),RT(0.0600),RT(0.1000),RT(0.6200),RT(0.7800),RT(0.8400),RT(0.8800),RT(0.9500),RT(1.0000),RT(1.0000),RT(1.0000)},
261  {RT(0.0300),RT(0.0300),RT(0.0240),RT(0.0220),RT(0.0320),RT(0.0620),RT(0.2000),RT(0.6800),RT(0.8300),RT(0.8700),RT(0.9000),RT(0.9500),RT(1.0000),RT(1.0000),RT(1.0000)},
262  {RT(0.0250),RT(0.0250),RT(0.0250),RT(0.0360),RT(0.0430),RT(0.1300),RT(0.2700),RT(0.7400),RT(0.8600),RT(0.8900),RT(0.9200),RT(1.0000),RT(1.0000),RT(1.0000),RT(1.0000)},
263  {RT(0.0270),RT(0.0270),RT(0.0270),RT(0.0400),RT(0.0520),RT(0.2000),RT(0.4000),RT(0.7800),RT(0.8800),RT(0.9000),RT(0.9400),RT(1.0000),RT(1.0000),RT(1.0000),RT(1.0000)},
264  {RT(0.0300),RT(0.0300),RT(0.0300),RT(0.0470),RT(0.0640),RT(0.2500),RT(0.5000),RT(0.8000),RT(0.9000),RT(0.9100),RT(0.9500),RT(1.0000),RT(1.0000),RT(1.0000),RT(1.0000)},
265  {RT(0.0400),RT(0.0400),RT(0.0330),RT(0.0370),RT(0.0680),RT(0.2400),RT(0.5500),RT(0.8000),RT(0.9000),RT(0.9100),RT(0.9500),RT(1.0000),RT(1.0000),RT(1.0000),RT(1.0000)},
266  {RT(0.0350),RT(0.0350),RT(0.0350),RT(0.0550),RT(0.0790),RT(0.2900),RT(0.5800),RT(0.8000),RT(0.9000),RT(0.9100),RT(0.9500),RT(1.0000),RT(1.0000),RT(1.0000),RT(1.0000)},
267  {RT(0.0370),RT(0.0370),RT(0.0370),RT(0.0620),RT(0.0820),RT(0.2900),RT(0.5900),RT(0.7800),RT(0.9000),RT(0.9100),RT(0.9500),RT(1.0000),RT(1.0000),RT(1.0000),RT(1.0000)},
268  {RT(0.0370),RT(0.0370),RT(0.0370),RT(0.0600),RT(0.0800),RT(0.2900),RT(0.5800),RT(0.7700),RT(0.8900),RT(0.9100),RT(0.9500),RT(1.0000),RT(1.0000),RT(1.0000),RT(1.0000)},
269  {RT(0.0370),RT(0.0370),RT(0.0370),RT(0.0410),RT(0.0750),RT(0.2500),RT(0.5400),RT(0.7600),RT(0.8800),RT(0.9200),RT(0.9500),RT(1.0000),RT(1.0000),RT(1.0000),RT(1.0000)},
270  {RT(0.0370),RT(0.0370),RT(0.0370),RT(0.0520),RT(0.0670),RT(0.2500),RT(0.5100),RT(0.7700),RT(0.8800),RT(0.9300),RT(0.9700),RT(1.0000),RT(1.0000),RT(1.0000),RT(1.0000)},
271  {RT(0.0370),RT(0.0370),RT(0.0370),RT(0.0470),RT(0.0570),RT(0.2500),RT(0.4900),RT(0.7700),RT(0.8900),RT(0.9500),RT(1.0000),RT(1.0000),RT(1.0000),RT(1.0000),RT(1.0000)},
272  {RT(0.0360),RT(0.0360),RT(0.0360),RT(0.0420),RT(0.0480),RT(0.2300),RT(0.4700),RT(0.7800),RT(0.9200),RT(1.0000),RT(1.0200),RT(1.0200),RT(1.0200),RT(1.0200),RT(1.0200)},
273  {RT(0.0400),RT(0.0400),RT(0.0350),RT(0.0330),RT(0.0400),RT(0.1120),RT(0.4500),RT(0.7900),RT(1.0100),RT(1.0300),RT(1.0400),RT(1.0400),RT(1.0400),RT(1.0400),RT(1.0400)},
274  {RT(0.0330),RT(0.0330),RT(0.0330),RT(0.0330),RT(0.0330),RT(0.1190),RT(0.4700),RT(0.9500),RT(1.3000),RT(1.7000),RT(2.3000),RT(2.3000),RT(2.3000),RT(2.3000),RT(2.3000)},
275  {RT(0.0270),RT(0.0270),RT(0.0270),RT(0.0270),RT(0.0270),RT(0.1250),RT(0.5200),RT(1.4000),RT(2.3000),RT(3.0000),RT(4.0000),RT(4.0000),RT(4.0000),RT(4.0000),RT(4.0000)} };
276  return ecoll[a_i][a_j];
277  }
278 
279 
280  /* The following code is adapted from SCALE-SDM:
281  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
282  /*! \brief Hall's kernel */
283  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
284  RT Halls ( const RT a_r1, /*!< radius of particle 1 */
285  const RT a_r2, /*!< radius of particle 2 */
286  const RT* const a_v1, /*!< velocity of particle 1 */
287  const RT* const a_v2, /*!< velocity of particle 2 */
288  const RT* const a_x1, /*!< position of particle 1 */
289  const RT* const a_x2, /*!< position of particle 2 */
290  const SDKernelRelativeVelocityType a_vel_type /*!< velocity type */) const noexcept
291  {
292  auto r1 = std::max( a_r1, a_r2 ); // large
293  auto r2 = std::min( a_r1, a_r2 ); // small
294  auto ratio = r2 / r1;
295  auto sumr = r1 + r2;
296 
297  int irr = Halls_data_r0col(r1);
298  int iqq = Halls_data_ratcol(ratio);
299 
300  RT c_rate = RT(1.0);
301 
302  if( irr >= 15 ) {
303 
304  RT q = ( ratio - Halls_data_ratcol(iqq-1) )
305  / ( Halls_data_ratcol(iqq) - Halls_data_ratcol(iqq-1) );
306 
307  RT ek = (RT(1.0)-q) * Halls_data_ecoll(iqq-1,14)
308  + q * Halls_data_ecoll(iqq ,14);
309 
310  c_rate = std::min( ek, RT(1.0) );
311 
312  } else if ((irr >= 1) && (irr < 15)) {
313 
314  RT p = ( r1*RT(1.0e6) - Halls_data_r0col(irr-1) )
315  / ( Halls_data_r0col(irr) - Halls_data_r0col(irr-1) );
316 
317  RT q = ( ratio - Halls_data_ratcol(iqq-1) )
318  / ( Halls_data_ratcol(iqq) - Halls_data_ratcol(iqq-1) );
319 
320  c_rate = (RT(1.0)-p) * (RT(1.0)-q) * Halls_data_ecoll(iqq-1,irr-1)
321  + p * (RT(1.0)-q) * Halls_data_ecoll(iqq-1,irr )
322  + (RT(1.0)-p) * q * Halls_data_ecoll(iqq ,irr-1)
323  + p * q * Halls_data_ecoll(iqq ,irr );
324 
325  } else {
326 
327  RT q = ( ratio - Halls_data_ratcol(iqq-1) )
328  / ( Halls_data_ratcol(iqq) - Halls_data_ratcol(iqq-1) );
329 
330  c_rate = (RT(1.0)-q) * Halls_data_ecoll(iqq-1,0)
331  + q * Halls_data_ecoll(iqq ,0);
332 
333  }
334  c_rate *= (PI*sumr*sumr);
335 
336  RT dv = 0;
337  if (a_vel_type == SDKernelRelativeVelocityType::radial_velocity) {
338  RT dr = 0, dv2 = 0;
339  for (int d = 0; d < SPACEDIM; d++) {
340  const RT ddv = a_v1[d]-a_v2[d];
341  const RT ddx = a_x1[d]-a_x2[d];
342  dv += ddv*ddx;
343  dr += ddx*ddx;
344  dv2 += ddv*ddv;
345  }
346  // radial component; fall back to the full relative speed for coincident pairs
347  dv = (dr > RT(0)) ? std::abs(dv) / std::sqrt(dr) : std::sqrt(dv2);
348  } else {
349  for (int d = 0; d < SPACEDIM; d++) {
350  dv += (a_v1[d]-a_v2[d])*(a_v1[d]-a_v2[d]);
351  }
352  dv = std::sqrt(dv);
353  }
354 
355  return c_rate*dv;
356  }
357 
358  /* The following code is adapted from SCALE-SDM:
359  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
360  /*! \brief Brownian coagulation kernel from Seinfeld and Pandis
361  *
362  * Computes the Brownian coagulation coefficient K12 [m³/s] for two particles
363  * using the interpolation formula that accounts for both continuum and
364  * free-molecular regimes.
365  *
366  * Reference: Seinfeld, J. H., and S. N. Pandis, 2016: Atmospheric Chemistry
367  * and Physics: From Air Pollution to Climate Change, 3rd ed. Wiley, 1152 pp.
368  * Chapter 13, Eq. 13.56 (Fuchs interpolation formula)
369  *
370  * Dynamic viscosity from Pruppacher & Klett (1997), Eq. 3-11.
371  * Slip correction coefficients: 1.257, 0.40, 0.55 (Table 13.1)
372  */
373  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
374  RT Brownian_SeinfeldPandis ( const RT a_r1, /*!< radius of particle 1 */
375  const RT a_r2, /*!< radius of particle 2 */
376  const RT a_mass_1, /*!< total mass of particle 1 */
377  const RT a_mass_2, /*!< total mass of particle 2 */
378  const RT a_pressure, /*!< pressure */
379  const RT a_temperature /*!< temperature */ ) const noexcept
380  {
381  // diameter of droplets
382  RT diameter_1 = 2*a_r1;
383  RT diameter_2 = 2*a_r2;
384 
385  RT p_crs = a_pressure; // [Pa]
386  RT t_crs = a_temperature; // [K]
387 
388  // dynamic viscosity [Pa*s] (Pruppacher & Klett,1997)
389  RT T_degC = t_crs - RT(273.15); // [K] => [degC]
390  RT vis_crs = RT(0.0);
391  if( T_degC >= RT(0.0) ) {
392  vis_crs = ( RT(1.7180) + RT(4.9E-3)*T_degC ) * RT(1.E-5);
393  } else {
394  vis_crs = ( RT(1.7180) + RT(4.9E-3)*T_degC -RT(1.2E-5)*T_degC*T_degC ) * RT(1.E-5);
395  }
396 
397  //air mean free path [m]
398  RT lambda_crs = (two*vis_crs) / (p_crs*std::sqrt(RT(8.0)*mwdair/(PI*boltz*avogadro*t_crs)));
399 
400  // temporary var
401  RT dtmp = RT(0.0);
402 
403  // slip correction of droplets [-]
404  dtmp = RT(1.2570) + RT(0.40) * exp(-RT(0.550)*diameter_1/lambda_crs);
405  RT slip_corr_1 = RT(1.0) + (RT(2.0)*lambda_crs*dtmp)/(diameter_1);
406  dtmp = RT(1.2570) + RT(0.40) * exp(-RT(0.550)*diameter_2/lambda_crs);
407  RT slip_corr_2 = RT(1.0) + (RT(2.0)*lambda_crs*dtmp)/(diameter_2);
408 
409  // diffusion term [m*m/s]
410  dtmp = (boltz*t_crs)/(RT(3.0)*PI*vis_crs);
411  RT diff_term_1 = dtmp * (slip_corr_1/diameter_1);
412  RT diff_term_2 = dtmp * (slip_corr_2/diameter_2);
413 
414  // velocity term [m/s]
415  dtmp = (RT(8.0)*boltz*t_crs)/PI;
416  RT vel_term_1 = std::sqrt(dtmp/a_mass_1);
417  RT vel_term_2 = std::sqrt(dtmp/a_mass_2);
418 
419  // mean free path of droplets [m]
420  dtmp = RT(8.0)/PI;
421  RT lambda_1 = dtmp * (diff_term_1/vel_term_1);
422  RT lambda_2 = dtmp * (diff_term_2/vel_term_2);
423 
424  //length term [m]
425  RT d1l1_sq = diameter_1*diameter_1+lambda_1*lambda_1;
426  dtmp = (diameter_1+lambda_1)*(diameter_1+lambda_1)*(diameter_1+lambda_1)
427  - d1l1_sq*std::sqrt(d1l1_sq); // x^1.5 = x*sqrt(x)
428  RT length_term_1 = dtmp/(RT(3.0)*diameter_1*lambda_1) - diameter_1;
429  RT d2l2_sq = diameter_2*diameter_2+lambda_2*lambda_2;
430  dtmp = (diameter_2+lambda_2)*(diameter_2+lambda_2)*(diameter_2+lambda_2)
431  - d2l2_sq*std::sqrt(d2l2_sq); // x^1.5 = x*sqrt(x)
432  RT length_term_2 = dtmp/(RT(3.0)*diameter_2*lambda_2) - diameter_2;
433 
434  // Brownian Coagulation Coefficient K12 [m3/s]
435  RT sumdia = diameter_1 + diameter_2;
436  RT sumd = diff_term_1 + diff_term_2;
437  RT sumc = std::sqrt( vel_term_1*vel_term_1 + vel_term_2*vel_term_2 );
438  RT sumg = std::sqrt( RT(2.0)*length_term_1*length_term_1 + RT(2.0)*length_term_2*length_term_2 );
439 
440  dtmp = sumdia/(sumdia+RT(2.0)*sumg) + (RT(8.0)*sumd)/(sumdia*sumc);
441  RT k12 = RT(2.0)*PI * sumdia*sumd/dtmp;
442 
443  return k12;
444  }
445 
446  /* The following code is adapted from SCALE-SDM:
447  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
448  /*! \brief Collision efficiency for riming from Beard and Grover (1974)
449  *
450  * Computes the collision efficiency between ice particles and cloud droplets
451  * based on Reynolds number, Froude number, and size ratio.
452  *
453  * Reference: Beard, K. V., and S. N. Grover, 1974: Numerical collision
454  * efficiencies for small raindrops colliding with micron size particles.
455  * J. Atmos. Sci., 31, 543-550.
456  * https://doi.org/10.1175/1520-0469(1974)031<0543:NCEFSR>2.0.CO;2
457  *
458  * Coefficients:
459  * - K0 fit: A0=-0.1007, A1=-0.358, A2=0.0261 (Re ≤ 400), K0=0.21 (Re > 400)
460  * - yc0 fit: B0=0.1465, B1=1.302, B2=-0.607, B3=0.293
461  * - E = ((yc0+p)/(1+p))² where p = size ratio
462  */
463  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
464  RT BeardGrover1974 ( const RT a_sr, /*!< Size ratio (r_droplet/r_ice) */
465  const RT a_Re, /*!< Reynolds number of ice particle */
466  const RT a_Fr /*!< Froude number */ ) const noexcept
467  {
468  // riming efficiency of Beard and Grover (1974)
469  const RT A0 = -RT(0.1007);
470  const RT A1 = -RT(0.358);
471  const RT A2 = RT(0.0261);
472  const RT B0 = RT(0.1465);
473  const RT B1 = RT(1.302);
474  const RT B2 = -RT(0.607);
475  const RT B3 = RT(0.293);
476 
477  RT K0 = RT(0.0);
478  if ( a_Re > RT(400.0)) {
479  K0 = RT(0.21);
480  } else {
481  auto F = std::log(a_Re);
482  auto G = A0 + A1*F + A2*F*F;
483  K0 = std::exp(G);
484  }
485 
486  auto z = std::log(a_Fr/K0);
487  auto H = std::max(B0 + B1*z + B2*z*z + B3*z*z*z,RT(0.0));
488  auto yc0 = (RT(2.0)/PI)*std::atan(H);
489 
490  auto retval = (yc0+a_sr)*(yc0+a_sr) / ((RT(1.0)+a_sr)*(RT(1.0)+a_sr));
491  return retval;
492  }
493 
494  /* The following code is adapted from SCALE-SDM:
495  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
496  /*! \brief Collision efficiency for plate-like ice crystals from Erfani and Mitchell (2017)
497  *
498  * Computes the collision efficiency between plate-like ice crystals and
499  * cloud droplets based on Reynolds and Froude numbers.
500  *
501  * Reference: Erfani, E., and D. L. Mitchell, 2017: Growth of ice particle
502  * mass and projected area during riming. Atmos. Chem. Phys., 17, 1241-1257.
503  * https://doi.org/10.5194/acp-17-1241-2017
504  *
505  * Valid for Re ≥ 1, with polynomial fits for K_t (transition Froude number)
506  * and K_c (critical Froude number). Three regimes based on Fr vs K_t.
507  */
508  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
509  RT ErfaniMitchell2017_Plate ( const RT a_Re, /*!< Reynolds number of ice crystal */
510  const RT a_Fr /*!< Froude number */ ) const noexcept
511  {
512  if (a_Re < RT(1.0)) {
513  return RT(0.0);
514  } else {
515 
516  auto Re_l = std::min(a_Re,RT(120.0));
517  auto Fr_l = std::min(a_Fr,RT(35.0));
518  auto Re_l_2 = Re_l*Re_l;
519  auto Re_l_3 = Re_l_2*Re_l;
520  auto Re_l_4 = Re_l_2*Re_l_2;
521  auto Re_l_5 = Re_l_4*Re_l;
522  auto K_t = -RT(5.07e-10)*Re_l_5
523  +RT(1.73e-7) *Re_l_4
524  -RT(2.17e-5) *Re_l_3
525  +RT(0.0013) *Re_l_2
526  -RT(0.037) *Re_l
527  +RT(0.8355);
528 
529  RT K_c = RT(0.0);
530  if (Re_l <= RT(10.0)) { K_c = RT(1.250) * std::pow(Re_l, RT(-0.350)); }
531  else if (Re_l <= RT(40.0)) { K_c = RT(1.072) * std::pow(Re_l, RT(-0.301)); }
532  else if (Re_l <= RT(120.0)) { K_c = RT(0.356) * std::pow(Re_l, RT(-0.003)); }
533 
534  auto retval = RT(0.0);
535  if (Fr_l <= RT(0.35)) {
536  retval = RT(0.787) * std::pow(Fr_l, RT(0.988)) * std::max(RT(0.263)*std::log(Re_l)-RT(0.264), RT(0.0));
537  } else if(Fr_l <= K_t) {
538  retval = (RT(0.7475)*std::log10(Fr_l)+RT(0.620)) * std::max(RT(0.263)*std::log(Re_l)-RT(0.264), RT(0.0));
539  } else {
540  auto E_1 = (RT(0.7475)*std::log10(K_t)+RT(0.620)) * std::max(RT(0.263)*std::log(Re_l)-RT(0.264), RT(0.0));
541  auto E_2 = std::sqrt( std::max(RT(1.0) - RT(0.2)*(std::log10(Fr_l/K_c)-std::sqrt(RT(5.0)))
542  *(std::log10(Fr_l/K_c)-std::sqrt(RT(5.0))),
543  RT(0.0)) );
544  retval = std::max(E_1,E_2);
545  }
546  return retval;
547  }
548  }
549 
550  /* The following code is adapted from SCALE-SDM:
551  * Copyright (c) 2012-2014, Team SCALE, All rights reserved. */
552  /*! \brief Collision efficiency for columnar ice crystals from Erfani and Mitchell (2017)
553  *
554  * Computes the collision efficiency between columnar ice crystals and
555  * cloud droplets based on Reynolds and Froude numbers.
556  *
557  * Reference: Erfani, E., and D. L. Mitchell, 2017: Growth of ice particle
558  * mass and projected area during riming. Atmos. Chem. Phys., 17, 1241-1257.
559  * https://doi.org/10.5194/acp-17-1241-2017
560  *
561  * Valid for Re ≥ 0.2. Uses different polynomial fits than the plate version
562  * for K_t (transition Froude) and K_c (critical Froude). Two regimes based on Fr vs K_t.
563  */
564  AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
565  RT ErfaniMitchell2017_Column ( const RT a_Re, /*!< Reynolds number of ice crystal */
566  const RT a_Fr /*!< Froude number */ ) const noexcept
567  {
568  if (a_Re < RT(0.2)) {
569  return RT(0.0);
570  } else {
571 
572  auto Re_l = std::min(a_Re,RT(20.0));
573  auto Fr_l = std::min(a_Fr,RT(20.0));
574 
575  auto K_t = RT(0.0);
576  if (Re_l <= RT(2.0)) {
577  K_t = RT(0.0251)*Re_l*Re_l
578  - RT(0.0144)*Re_l
579  + RT(0.811);
580  } else {
581  K_t = -RT(0.0003)*Re_l*Re_l*Re_l
582  +RT(0.0124)*Re_l*Re_l
583  -RT(0.1634)*Re_l
584  +RT(1.0075);
585  }
586 
587  auto para_r = RT(0.0), K_c = RT(0.0);
588  if (Re_l <= RT(1.7)) {
589  para_r = RT(0.7422) * std::pow(Re_l, RT(0.2111));
590  K_c = RT(0.7797) * std::pow(Re_l, RT(-0.009));
591  } else {
592  para_r = RT(0.8025) * std::pow(Re_l, RT(0.0604));
593  K_c = RT(1.0916) * std::pow(Re_l, RT(-0.635));
594  }
595 
596  auto retval = RT(0.0);
597  if (Fr_l <= K_t) {
598  if (Re_l <= RT(3.0)) {
599  retval= RT(0.787) * std::pow(Fr_l, RT(0.988)) * (-RT(0.0121)*Re_l*Re_l + RT(0.1297)*Re_l + RT(0.0598));
600  } else {
601  retval = RT(0.787) * std::pow(Fr_l, RT(0.988)) * (-RT(0.0005)*Re_l*Re_l + RT(0.1028)*Re_l + RT(0.0359));
602  }
603  } else {
604  auto tmp = std::log10(Fr_l/K_c)-std::sqrt(RT(3.5));
605  retval = para_r * std::sqrt( std::max(RT(1.0)-tmp*tmp/RT(3.5), RT(0.0)) );
606  }
607  return retval;
608  }
609  }
610 
611 };
612 
613 #endif
614 #endif
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real avogadro
Definition: ERF_Constants.H:106
constexpr amrex::Real fourth
Definition: ERF_Constants.H:14
constexpr amrex::Real mwdair
Definition: ERF_Constants.H:107
constexpr amrex::Real boltz
Definition: ERF_Constants.H:105
constexpr amrex::Real PI
Definition: ERF_Constants.H:42
constexpr amrex::Real four_thirds_pi
Definition: ERF_Constants.H:44
AMREX_ENUM(InitType, None, Input_Sounding, NCFile, WRFInput, Metgrid, Uniform, ConstantDensity, ConstantDensityLinearTheta, Isentropic, MoistBaseState, HindCast)
Initial-condition source used to populate the ERF state.
Real H
Definition: ERF_InitCustomPert_MovingTerrain.H:7
@ qv
Definition: ERF_Kessler.H:30
@ q
Definition: ERF_WSM6.H:184
@ p
Definition: ERF_WSM6.H:191
@ tmp
Definition: ERF_AdvanceWSM6.cpp:114