@@ -98,6 +98,49 @@ function ice_melt(
9898 return (; dNdt, dLdt)
9999end
100100
101+ function collision_cross_section_ice_liquid (state, Dᵢ, Dₗ)
102+ rᵢ_eff (Dᵢ) = √ (ice_area (state, Dᵢ) / π)
103+ return π * (rᵢ_eff (Dᵢ) + Dₗ / 2 )^ 2 # collision cross section
104+ end
105+
106+ """
107+ volumetric_collision_rate_integrand(state, velocity_params, ρₐ)
108+
109+ Returns a function that computes the volumetric collision rate integrand for ice-liquid collisions [m³/s].
110+ The returned function takes ice and liquid particle diameters as arguments.
111+
112+ # Arguments
113+ - `state`: [`P3State`](@ref)
114+ - `velocity_params`: velocity parameterization, e.g. [`CMP.Chen2022VelType`](@ref)
115+ - `ρₐ`: air density
116+
117+ # Returns
118+ A function `(D_ice, D_liq) -> E * K * |vᵢ - vₗ|` where:
119+ - `D_ice` and `D_liq` are the (maximum) diameters of the ice and liquid particles
120+ - `E` is the collision efficiency
121+ - `K` is the collision cross section
122+ - `vᵢ` and `vₗ` are the terminal velocities of ice and liquid particles
123+
124+ Note that `E`, `K`, `vᵢ` and `vₗ` are all, in general, functions of `D_ice` and `D_liq`.
125+
126+ This function is a component of integrals like
127+
128+ ```math
129+ ∫ ∫ E * K * |vᵢ - vₗ| * N'_i * N'_l dD_i dD_l
130+ ```
131+ """
132+ function volumetric_collision_rate_integrand (velocity_params, ρₐ, state)
133+ v_ice = ice_particle_terminal_velocity (velocity_params, ρₐ, state)
134+ v_liq = CO. particle_terminal_velocity (velocity_params. rain, ρₐ)
135+ function integrand (D_ice:: FT , D_liq:: FT ) where {FT}
136+ E = FT (1 ) # TODO - Make collision efficiency a function of Dᵢ and Dₗ
137+ K = collision_cross_section_ice_liquid (state, D_ice, D_liq)
138+ return E * K * abs (v_ice (D_ice) - v_liq (D_liq))
139+ end
140+
141+ return integrand
142+ end
143+
101144"""
102145 compute_max_freeze_rate(aps, tps, velocity_params, ρₐ, Tₐ, state)
103146
@@ -199,3 +242,259 @@ function compute_local_rime_density(velocity_params, ρₐ, T, state)
199242 end
200243 return ρ′_rim
201244end
245+
246+ """
247+ get_liquid_integrals(n, ∂ₜV, m_liq, ρ′_rim, liq_bounds; ∫kwargs...)
248+
249+ Returns a function `liquid_integrals(Dᵢ)` that computes the liquid particle integrals
250+ for a given ice particle diameter `Dᵢ`.
251+
252+ # Arguments
253+ - `n`: liquid particle size distribution function `n(D)`
254+ - `∂ₜV`: volumetric collision rate integrand function `∂ₜV(Dᵢ, D)`
255+ - `m_liq`: liquid particle mass function `m_liq(D)`
256+ - `ρ′_rim`: local rime density function `ρ′_rim(Dᵢ, D)`
257+ - `liq_bounds`: integration bounds for liquid particles
258+
259+ # Keyword arguments
260+ - `∫kwargs...`: Additional keyword arguments passed to `QuadGK.quadgk`
261+
262+ # Notes
263+ The function `liquid_integrals(Dᵢ)` returns a tuple `(∂ₜN_col, ∂ₜM_col, ∂ₜB_col)`
264+ of collision rates at `Dᵢ`, where:
265+ - `∂ₜN_col`: number collision rate [1/s]
266+ - `∂ₜM_col`: mass collision rate [kg/s]
267+ - `∂ₜB_col`: rime volume collision rate [m³/s]
268+ """
269+ function get_liquid_integrals (n, ∂ₜV, m_liq, ρ′_rim, liq_bounds; ∫kwargs... )
270+ function liquid_integrals (Dᵢ)
271+ ((∂ₜN_col, ∂ₜM_col, ∂ₜB_col), _) =
272+ QGK. quadgk (liq_bounds... ; ∫kwargs... ) do D
273+ return SA. SVector (
274+ # ∂ₜN_col = ∫ ∂ₜV ⋅ n ⋅ dD
275+ ∂ₜV (Dᵢ, D) * n (D),
276+ # ∂ₜM_col = ∫ ∂ₜV ⋅ n ⋅ m_liq ⋅ dD
277+ ∂ₜV (Dᵢ, D) * n (D) * m_liq (D),
278+ # ∂ₜB_col = ∫ ∂ₜV ⋅ n ⋅ m_liq / ρ′_rim ⋅ dD
279+ ∂ₜV (Dᵢ, D) * n (D) * m_liq (D) / ρ′_rim (Dᵢ, D),
280+ )
281+ end
282+ return ∂ₜN_col, ∂ₜM_col, ∂ₜB_col
283+ end
284+ return liquid_integrals
285+ end
286+
287+ """
288+ ∫liquid_ice_collisions(
289+ n_i, ∂ₜM_max, cloud_integrals, rain_integrals, ice_bounds; ∫kwargs...
290+ )
291+
292+ Computes the bulk collision rate integrands between ice and liquid particles.
293+
294+ # Arguments
295+ - `n_i`: ice particle size distribution function n_i(D)
296+ - `∂ₜM_max`: maximum freezing rate function ∂ₜM_max(Dᵢ)
297+ - `cloud_integrals`: an instance of [`get_liquid_integrals`](@ref) for cloud particles
298+ - `rain_integrals`: an instance of [`get_liquid_integrals`](@ref) for rain particles
299+ - `ice_bounds`: integration bounds for ice particles, from [`integral_bounds`](@ref)
300+
301+ # Keyword arguments
302+ - `∫kwargs...`: Additional keyword arguments passed to `QuadGK.quadgk`
303+
304+ # Returns
305+ A tuple of 8 integrands, see [`∫liquid_ice_collisions`](@ref) for details.
306+ """
307+ function ∫liquid_ice_collisions (n_i, ∂ₜM_max, cloud_integrals, rain_integrals, ice_bounds; ∫kwargs... )
308+ function liquid_ice_collisions_integrands (Dᵢ)
309+ # Inner integrals over liquid particle diameters
310+ ∂ₜN_c_col, ∂ₜM_c_col, ∂ₜB_c_col = cloud_integrals (Dᵢ)
311+ ∂ₜN_r_col, ∂ₜM_r_col, ∂ₜB_r_col = rain_integrals (Dᵢ)
312+
313+ # Partition the mass collisions between freezing and shedding
314+ ∂ₜM_col = ∂ₜM_c_col + ∂ₜM_r_col # [kg / s]
315+
316+ ∂ₜM_frz = min (∂ₜM_col, ∂ₜM_max (Dᵢ))
317+ f_frz = iszero (∂ₜM_col) ? zero (∂ₜM_frz) : ∂ₜM_frz / ∂ₜM_col
318+ 𝟙_wet = ∂ₜM_col > ∂ₜM_frz # Used for wet densification
319+
320+ n = n_i (Dᵢ)
321+ # Integrating over `Dᵢ` gives another unit of `[m]`, so `[X / s / m]` --> `[X / s]`
322+ # ∂ₜX = ∫ ∂ₜX(Dᵢ) nᵢ(Dᵢ) dDᵢ
323+ return SA. SVector (
324+ n * ∂ₜM_c_col * f_frz, # QCFRZ
325+ n * ∂ₜM_c_col * (1 - f_frz), # QCSHD
326+ n * ∂ₜN_c_col, # NCCOL
327+ n * ∂ₜM_r_col * f_frz, # QRFRZ
328+ n * ∂ₜM_r_col * (1 - f_frz), # QRSHD
329+ n * ∂ₜN_r_col, # NRCOL
330+ n * ∂ₜM_col, # ∫M_col, total collision rate
331+ n * ∂ₜB_c_col * f_frz, # BCCOL, ∂ₜB_rim source
332+ n * ∂ₜB_r_col * f_frz, # BRCOL, ∂ₜB_rim source
333+ n * 𝟙_wet * ∂ₜM_col, # ∫𝟙_wet_M_col, wet growth indicator
334+ )
335+ end
336+
337+ (rates, _) = QGK. quadgk (liquid_ice_collisions_integrands, ice_bounds... ; ∫kwargs... )
338+ return rates
339+ end
340+
341+ """
342+ ∫liquid_ice_collisions(
343+ state, logλ, psd_c, psd_r, L_c, N_c, L_r, N_r,
344+ aps, tps, vel, ρₐ, T, m_liq
345+ )
346+
347+ Compute key liquid-ice collision rates and quantities. Used by [`bulk_liquid_ice_collision_sources`](@ref).
348+
349+ # Arguments
350+ - `state`: [`P3State`](@ref)
351+ - `logλ`: the log of the slope parameter [log(1/m)]
352+ - `psd_c`: [`CMP.CloudParticlePDF_SB2006`](@ref)
353+ - `psd_r`: [`CMP.RainParticlePDF_SB2006`](@ref)
354+ - `L_c`: cloud liquid water content [kg/m³]
355+ - `N_c`: cloud liquid water number concentration [1/m³]
356+ - `L_r`: rain water content [kg/m³]
357+ - `N_r`: rain number concentration [1/m³]
358+ - `aps`: [`CMP.AirProperties`](@ref)
359+ - `tps`: `TDP.ThermodynamicsParameters`
360+ - `vel`: velocity parameterization, e.g. [`CMP.Chen2022VelType`](@ref)
361+ - `ρₐ`: air density [kg/m³]
362+ - `T`: temperature [K]
363+ - `m_liq`: liquid particle mass function `m_liq(D)`
364+
365+ # Returns
366+ A tuple `(QCFRZ, QCSHD, NCCOL, QRFRZ, QRSHD, NRCOL, ∫M_col, BCCOL, BRCOL, ∫𝟙_wet_M_col)`, where:
367+ 1. `QCFRZ` - Cloud mass collision rate due to freezing [kg/s]
368+ 2. `QCSHD` - Cloud mass collision rate due to shedding [kg/s]
369+ 3. `NCCOL` - Cloud number collision rate [1/s]
370+ 4. `QRFRZ` - Rain mass collision rate due to freezing [kg/s]
371+ 5. `QRSHD` - Rain mass collision rate due to shedding [kg/s]
372+ 4. `NRCOL` - Rain number collision rate [1/s]
373+ 5. `∫M_col` - Total collision rate [kg/s]
374+ 6. `BCCOL` - Cloud rime volume source [m³/m³/s]
375+ 7. `BRCOL` - Rain rime volume source [m³/m³/s]
376+ 8. `∫𝟙_wet_M_col` - Wet growth indicator [kg/s]
377+ """
378+ function ∫liquid_ice_collisions (
379+ state, logλ,
380+ psd_c, psd_r, L_c, N_c, L_r, N_r,
381+ aps, tps, vel, ρₐ, T, m_liq,
382+ )
383+ FT = eltype (state)
384+
385+ # Particle size distributions
386+ n_c = DT. size_distribution (psd_c, L_c / ρₐ, ρₐ, N_c) # n_c(Dₗ)
387+ n_r = DT. size_distribution (psd_r, L_r / ρₐ, ρₐ, N_r) # n_r(Dₗ)
388+ n_i = DT. size_distribution (state, logλ) # n_i(Dᵢ)
389+
390+ # Initialize integration buffers by evaluating a representative integral
391+ ice_bounds = integral_bounds (state, logλ; p = 0.00001 )
392+ mm = FT (1e-3 )
393+ bounds_c = FT[0 ; 0.01 mm; 0.1 mm; 1 mm; 10 mm; 100 mm; 1 ] # TODO : Replace by quantiles method
394+ bounds_r = FT[0 ; 0.01 mm; 0.1 mm; 1 mm; 10 mm; 100 mm; 1 ] # TODO : Replace by quantiles method
395+ segbuf_c = QGK. quadgk_segbuf (n_c, bounds_c... )[3 ]
396+ segbuf_r = QGK. quadgk_segbuf (n_r, bounds_r... )[3 ]
397+ segbuf_ice = QGK. quadgk_segbuf (n_i, ice_bounds... )[3 ]
398+
399+ # Integrand components
400+ # NOTE: We assume collision efficiency, shape (spherical), and terminal velocity is the
401+ # same for cloud and precipitating liquid particles ⟹ same volumetric collision rate, ∂ₜV
402+ ∂ₜV = volumetric_collision_rate_integrand (vel, ρₐ, state) # ∂ₜV(Dᵢ, Dₗ)
403+ ρ′_rim = compute_local_rime_density (vel, ρₐ, T, state) # ρ′_rim(Dᵢ, Dₗ)
404+ ∂ₜM_max = compute_max_freeze_rate (aps, tps, vel, ρₐ, T, state) # ∂ₜM_max(Dᵢ)
405+
406+ cloud_integrals = get_liquid_integrals (n_c, ∂ₜV, m_liq, ρ′_rim, bounds_c; eval_segbuf = segbuf_c) # (∂ₜN_c_col, ∂ₜM_c_col, ∂ₜB_c_col)
407+ rain_integrals = get_liquid_integrals (n_r, ∂ₜV, m_liq, ρ′_rim, bounds_r; eval_segbuf = segbuf_r) # (∂ₜN_r_col, ∂ₜM_r_col, ∂ₜB_r_col)
408+
409+ return ∫liquid_ice_collisions (
410+ n_i, ∂ₜM_max, cloud_integrals, rain_integrals, ice_bounds; eval_segbuf = segbuf_ice,
411+ )
412+ end
413+
414+ """
415+ bulk_liquid_ice_collision_sources(
416+ params, logλ, L_ice, F_rim, ρ_rim,
417+ psd_c, psd_r, L_c, N_c, L_r, N_r,
418+ aps, tps, vel, ρₐ, T,
419+ )
420+
421+ Computes the bulk rates for ice and liquid particle collisions.
422+
423+ # Arguments
424+ - `params`: the [`CMP.ParametersP3`](@ref)
425+ - `logλ`: the log of the slope parameter [log(1/m)]
426+ - `L_ice`: ice water content [kg/m³]
427+ - `F_rim`: riming fraction
428+ - `ρ_rim`: rime density [kg/m³]
429+ - `psd_c`: a [`CMP.CloudParticlePDF_SB2006`](@ref)
430+ - `psd_r`: a [`CMP.RainParticlePDF_SB2006`](@ref)
431+ - `L_c`: cloud liquid water content [kg/m³]
432+ - `N_c`: cloud liquid water number concentration [1/m³]
433+ - `L_r`: rain water content [kg/m³]
434+ - `N_r`: rain number concentration [1/m³]
435+ - `aps`: [`CMP.AirProperties`](@ref)
436+ - `tps`: thermodynamics parameters
437+ - `vel`: the velocity parameterization, e.g. [`CMP.Chen2022VelType`](@ref)
438+ - `ρₐ`: air density [kg/m³]
439+ - `T`: temperature [K]
440+
441+ # Returns
442+ A `NamedTuple` of `(; ∂ₜq_c, ∂ₜq_r, ∂ₜN_c, ∂ₜN_r, ∂ₜL_rim, ∂ₜL_ice, ∂ₜB_rim)`, where:
443+ 1. `∂ₜq_c`: cloud liquid water content tendency [kg/kg/s]
444+ 2. `∂ₜq_r`: rain water content tendency [kg/kg/s]
445+ 3. `∂ₜN_c`: cloud number concentration tendency [1/m³/s]
446+ 4. `∂ₜN_r`: rain number concentration tendency [1/m³/s]
447+ 5. `∂ₜL_rim`: riming mass tendency [kg/m³/s]
448+ 6. `∂ₜL_ice`: ice water content tendency [kg/m³/s]
449+ 7. `∂ₜB_rim`: rime volume tendency [m³/m³/s]
450+ """
451+ function bulk_liquid_ice_collision_sources (
452+ params, logλ, L_ice, F_rim, ρ_rim,
453+ psd_c, psd_r, L_c, N_c, L_r, N_r,
454+ aps, tps, vel, ρₐ, T,
455+ )
456+ FT = eltype (params)
457+ (; τ_wet) = params
458+ D_shd = FT (1e-3 ) # 1mm # TODO : Externalize this parameter
459+
460+ ρw = psd_c. ρw
461+ @assert ρw == psd_r. ρw " Cloud and rain should have the same liquid water density"
462+ m_liq (Dₗ) = ρw * CO. volume_sphere_D (Dₗ)
463+
464+ state = get_state (params; L_ice, F_rim, ρ_rim)
465+
466+ (QCFRZ, QCSHD, NCCOL, QRFRZ, QRSHD, NRCOL, ∫∂ₜM_col, BCCOL, BRCOL, ∫𝟙_wet_M_col) = ∫liquid_ice_collisions (
467+ state, logλ,
468+ psd_c, psd_r, L_c, N_c, L_r, N_r,
469+ aps, tps, vel, ρₐ, T, m_liq,
470+ )
471+
472+ # Bulk wet growth fraction
473+ f_wet = ∫𝟙_wet_M_col / ∫∂ₜM_col
474+
475+ # Shedding of rain
476+ # QRSHD = ∫∂ₜM_col - (QCFRZ + QRFRZ)
477+ NRSHD = QRSHD / m_liq (D_shd)
478+ # NCSHD = QCSHD / m_liq(D_shd)
479+
480+ # Densification of rime
481+ (; L_ice, F_rim, ρ_rim) = state
482+ B_rim = (L_ice * F_rim) / ρ_rim # from: ρ_rim = L_rim / B_rim
483+ QIWET = f_wet * L_ice * (1 - F_rim) / τ_wet # densification of rime mass
484+ BIWET = f_wet * (L_ice / ρ⭒ - B_rim) / τ_wet # densification of rime volume
485+
486+ # Bulk rates
487+ # # Liquid phase
488+ ∂ₜq_c = (- QCFRZ - QCSHD) / ρₐ
489+ ∂ₜq_r = (- QRFRZ + QCSHD) / ρₐ
490+ ∂ₜN_c = - NCCOL
491+ ∂ₜN_r = - NRCOL + NRSHD
492+ # # Ice phase
493+ ∂ₜL_rim = QCFRZ + QRFRZ + QIWET
494+ ∂ₜL_ice = QCFRZ + QRFRZ
495+ # ∂ₜN_ice = 0
496+ ∂ₜB_rim = BCCOL + BRCOL + BIWET
497+
498+ return (; ∂ₜq_c, ∂ₜq_r, ∂ₜN_c, ∂ₜN_r, ∂ₜL_rim, ∂ₜL_ice, ∂ₜB_rim)
499+
500+ end
0 commit comments