Skip to content

Commit e0496e3

Browse files
committed
fix(2M): add cond/evap to 2M, fix 2M tendencies
- rename rain_evaporation return fields - add condensation/evaporation (+sublimation/deposition) - rates for 2M - formatting - docstrings
1 parent 00b7133 commit e0496e3

File tree

10 files changed

+247
-251
lines changed

10 files changed

+247
-251
lines changed

docs/src/plots/RainEvapoartionSB2006.jl

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,49 +7,58 @@ import CloudMicrophysics.ThermodynamicsInterface as TDI
77
import CloudMicrophysics.Parameters as CMP
88
import CloudMicrophysics.Common as CO
99
import CloudMicrophysics.Microphysics2M as CM2
10+
import CloudMicrophysics.Utilities as UT
1011

1112
FT = Float64
1213

13-
const tps = TDI.TD.Parameters.ThermodynamicsParameters(FT)
14-
const aps = CMP.AirProperties(FT)
15-
const SB2006 = CMP.SB2006(FT)
14+
tps = TDI.TD.Parameters.ThermodynamicsParameters(FT)
15+
aps = CMP.AirProperties(FT)
16+
SB2006 = CMP.SB2006(FT)
1617

17-
function rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, Nᵣ, T)
18+
function rain_evaporation_CPU(
19+
(; pdf_r, evap)::CMP.SB2006, aps::CMP.AirProperties, tps::TDI.PS,
20+
q_tot, q_lcl, q_icl, q_rai, q_sno, ρ, N_rai, T,
21+
)
22+
FT = eltype(q_tot)
23+
ϵₘ = UT.ϵ_numerics_2M_M(FT)
24+
ϵₙ = UT.ϵ_numerics_2M_N(FT)
25+
26+
∂ₜρn_rai = FT(0)
27+
∂ₜq_rai = FT(0)
28+
S = TDI.supersaturation_over_liquid(tps, q_tot, q_lcl + q_rai, q_icl + q_sno, ρ, T)
1829

19-
evap_rate_0 = FT(0)
20-
evap_rate_1 = FT(0)
21-
S = TDI.supersaturation_over_liquid(tps, qₜ, qₗ + qᵣ, qᵢ + qₛ, ρ, T)
30+
# If there are no raindrops or conditions are supersaturated, no evaporation occurs
31+
(N_rai ϵₙ || S 0) && return (; ∂ₜρn_rai, ∂ₜq_rai)
2232

23-
if (qᵣ > FT(0) && S < FT(0))
33+
(; ν_air, D_vapor) = aps
34+
(; av, bv, α, β, ρ0) = evap
35+
ρw = pdf_r.ρw
36+
x_star = pdf_r.xr_min
37+
G = CO.G_func_liquid(aps, tps, T)
2438

25-
(; ν_air, D_vapor) = aps
26-
(; av, bv, α, β, ρ0) = SB2006.evap
27-
ρw = SB2006.pdf_r.ρw
28-
x_star = SB2006.pdf_r.xr_min
29-
G = CO.G_func_liquid(aps, tps, T)
39+
(; xr_mean) = CM2.pdf_rain_parameters(pdf_r, q_rai, ρ, N_rai)
40+
Dr = cbrt(6 * xr_mean /* ρw))
3041

31-
(; xr_mean) = CM2.pdf_rain_parameters(SB2006.pdf_r, qᵣ, ρ, Nᵣ)
32-
Dr = (FT(6) / FT(π) / ρw)^FT(1 / 3) * xr_mean^FT(1 / 3)
42+
t_star = cbrt(6 * x_star / xr_mean)
43+
a_vent_0 = av * SF.gamma(FT(-1), t_star) / FT(6)^(-2 // 3)
44+
b_vent_0 = bv * SF.gamma(-1 // 2 + 3 // 2 * β, t_star) / FT(6)^/ 2 - 1 // 2)
3345

34-
t_star = (FT(6) * x_star / xr_mean)^FT(1 / 3)
35-
a_vent_0 = av * FT(SF.gamma(-1, t_star)) / FT(6)^FT(-2 / 3)
36-
b_vent_0 =
37-
bv * FT(SF.gamma((-1 / 2) + (3 / 2) * β, t_star)) /
38-
FT(6)^FT/ 2 - 1 / 2)
46+
a_vent_1 = av * SF.gamma(FT(2)) / cbrt(FT(6))
47+
b_vent_1 = bv * SF.gamma(5 // 2 + 3 // 2 * β) / FT(6)^/ 2 + 1 // 2)
3948

40-
a_vent_1 = av * SF.gamma(FT(2)) / FT(6)^FT(1 / 3)
41-
b_vent_1 =
42-
bv * SF.gamma(FT(5 / 2) + FT(3 / 2) * β) / FT(6)^FT/ 2 + 1 / 2)
49+
N_Re = α * xr_mean^β * sqrt(ρ0 / ρ) * Dr / ν_air
50+
Fv0 = a_vent_0 + b_vent_0 * cbrt(ν_air / D_vapor) * sqrt(N_Re)
51+
Fv1 = a_vent_1 + b_vent_1 * cbrt(ν_air / D_vapor) * sqrt(N_Re)
4352

44-
N_Re = α * xr_mean^β * sqrt(ρ0 / ρ) * Dr / ν_air
45-
Fv0 = a_vent_0 + b_vent_0 * (ν_air / D_vapor)^FT(1 / 3) * sqrt(N_Re)
46-
Fv1 = a_vent_1 + b_vent_1 * (ν_air / D_vapor)^FT(1 / 3) * sqrt(N_Re)
53+
∂ₜρn_rai = min(0, 2 * FT(π) * G * S * N_rai * Dr * Fv0 / xr_mean)
54+
∂ₜq_rai = min(0, 2 * FT(π) * G * S * N_rai * Dr * Fv1 / ρ)
4755

48-
evap_rate_0 = min(FT(0), FT(2) * FT(π) * G * S * Nᵣ * Dr * Fv0 / xr_mean)
49-
evap_rate_1 = min(FT(0), FT(2) * FT(π) * G * S * Nᵣ * Dr * Fv1 / ρ)
50-
end
56+
# When xr = 0 ∂ₜρn_rai becomes NaN. We replace NaN with 0 which is the limit of
57+
# ∂ₜρn_rai for xr -> 0.
58+
∂ₜρn_rai = ifelse(xr_mean / x_star < eps(FT), FT(0), ∂ₜρn_rai)
59+
∂ₜq_rai = ifelse(q_rai < ϵₘ, FT(0), ∂ₜq_rai)
5160

52-
return (; evap_rate_0, evap_rate_1)
61+
return (; ∂ₜρn_rai, ∂ₜq_rai)
5362
end
5463

5564
qᵥ = FT(1e-2)
@@ -67,21 +76,21 @@ Nᵣ_range = range(1e6, stop = 1e9, length = 1000)
6776
T_range = range(273.15, stop = 273.15 + 50, length = 1000)
6877

6978
#! format: off
70-
evap_qᵣ_0 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, _qᵣ, qₛ, ρ, Nᵣ, T).evap_rate_0 for _qᵣ in qᵣ_range]
71-
evap_Nᵣ_0 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, _Nᵣ, T).evap_rate_0 for _Nᵣ in Nᵣ_range]
72-
evap_T_0 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, Nᵣ, _T).evap_rate_0 for _T in T_range]
79+
evap_qᵣ_0 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, _qᵣ, qₛ, ρ, Nᵣ, T).∂ₜρn_rai for _qᵣ in qᵣ_range]
80+
evap_Nᵣ_0 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, _Nᵣ, T).∂ₜρn_rai for _Nᵣ in Nᵣ_range]
81+
evap_T_0 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, Nᵣ, _T).∂ₜρn_rai for _T in T_range]
7382

74-
evap_qᵣ_0n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, _qᵣ, qₛ, ρ, Nᵣ, T).evap_rate_0 for _qᵣ in qᵣ_range]
75-
evap_Nᵣ_0n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, _Nᵣ, T).evap_rate_0 for _Nᵣ in Nᵣ_range]
76-
evap_T_0n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, Nᵣ, _T).evap_rate_0 for _T in T_range]
83+
evap_qᵣ_0n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, _qᵣ, qₛ, ρ, Nᵣ, T).∂ₜρn_rai for _qᵣ in qᵣ_range]
84+
evap_Nᵣ_0n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, _Nᵣ, T).∂ₜρn_rai for _Nᵣ in Nᵣ_range]
85+
evap_T_0n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, Nᵣ, _T).∂ₜρn_rai for _T in T_range]
7786

78-
evap_qᵣ_3 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, _qᵣ, qₛ, ρ, Nᵣ, T).evap_rate_1 for _qᵣ in qᵣ_range]
79-
evap_Nᵣ_3 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, _Nᵣ, T).evap_rate_1 for _Nᵣ in Nᵣ_range]
80-
evap_T_3 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, Nᵣ, _T).evap_rate_1 for _T in T_range]
87+
evap_qᵣ_3 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, _qᵣ, qₛ, ρ, Nᵣ, T).∂ₜq_rai for _qᵣ in qᵣ_range]
88+
evap_Nᵣ_3 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, _Nᵣ, T).∂ₜq_rai for _Nᵣ in Nᵣ_range]
89+
evap_T_3 = [rain_evaporation_CPU(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, Nᵣ, _T).∂ₜq_rai for _T in T_range]
8190

82-
evap_qᵣ_3n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, _qᵣ, qₛ, ρ, Nᵣ, T).evap_rate_1 for _qᵣ in qᵣ_range]
83-
evap_Nᵣ_3n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, _Nᵣ, T).evap_rate_1 for _Nᵣ in Nᵣ_range]
84-
evap_T_3n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, Nᵣ, _T).evap_rate_1 for _T in T_range]
91+
evap_qᵣ_3n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, _qᵣ, qₛ, ρ, Nᵣ, T).∂ₜq_rai for _qᵣ in qᵣ_range]
92+
evap_Nᵣ_3n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, _Nᵣ, T).∂ₜq_rai for _Nᵣ in Nᵣ_range]
93+
evap_T_3n = [CM2.rain_evaporation(SB2006, aps, tps, qₜ, qₗ, qᵢ, qᵣ, qₛ, ρ, Nᵣ, _T).∂ₜq_rai for _T in T_range]
8594

8695
fig = MK.Figure(resolution = (800, 600))
8796

src/BulkMicrophysicsTendencies.jl

Lines changed: 46 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,8 @@ end
101101

102102
"""
103103
bulk_microphysics_tendencies(
104-
::Microphysics1Moment,
105-
mp,
106-
tps,
107-
ρ,
108-
T,
109-
q_tot,
110-
q_lcl,
111-
q_icl,
112-
q_rai,
113-
q_sno,
104+
::Microphysics1Moment, mp, tps,
105+
ρ, T, q_tot, q_lcl, q_icl, q_rai, q_sno,
114106
)
115107
116108
Compute all 1-moment microphysics tendencies in one fused call.
@@ -157,17 +149,8 @@ This is a pure function of local thermodynamic state, suitable for:
157149
Limiters depend on timestep `dt` and should be applied by the caller after computing tendencies.
158150
"""
159151
@inline function bulk_microphysics_tendencies(
160-
::Microphysics1Moment,
161-
mp::CMP.Microphysics1MParams,
162-
tps,
163-
ρ,
164-
T,
165-
q_tot,
166-
q_lcl,
167-
q_icl,
168-
q_rai,
169-
q_sno,
170-
N_lcl = zero(ρ),
152+
::Microphysics1Moment, mp::CMP.Microphysics1MParams, tps,
153+
ρ, T, q_tot, q_lcl, q_icl, q_rai, q_sno, N_lcl = zero(ρ),
171154
)
172155
# Clamp negative inputs to zero (robustness against numerical errors)
173156
ρ = UT.clamp_to_nonneg(ρ)
@@ -590,12 +573,8 @@ the second form uses the supersaturation threshold `S_0 * q_vap_sat`.
590573
- Does NOT apply limiters (caller applies based on timestep)
591574
"""
592575
@inline function bulk_microphysics_tendencies(
593-
::Microphysics0Moment,
594-
mp::CMP.Microphysics0MParams,
595-
tps,
596-
T,
597-
q_lcl,
598-
q_icl,
576+
::Microphysics0Moment, mp::CMP.Microphysics0MParams, tps,
577+
T, q_lcl, q_icl,
599578
)
600579
q_lcl = UT.clamp_to_nonneg(q_lcl)
601580
q_icl = UT.clamp_to_nonneg(q_icl)
@@ -642,7 +621,13 @@ Used by both warm-only and warm+ice dispatch methods to reduce code duplication.
642621
- `dn_lcl_dt`: Cloud number tendency (1/kg/s)
643622
- `dn_rai_dt`: Rain number tendency (1/kg/s)
644623
"""
645-
@inline function warm_rain_tendencies_2m(sb, q_lcl, q_rai, ρ, n_lcl, n_rai)
624+
@inline function warm_rain_tendencies_2m(warm_rain, tps, T, q_tot, q_lcl, q_rai, q_ice, ρ, n_lcl, n_rai)
625+
626+
# Unpack parameters
627+
sb = warm_rain.seifert_beheng
628+
aps = warm_rain.air_properties
629+
condevap = warm_rain.condevap
630+
646631
# Convert to number densities for CM2 functions
647632
N_lcl = ρ * n_lcl
648633
N_rai = ρ * n_rai
@@ -654,6 +639,17 @@ Used by both warm-only and warm+ice dispatch methods to reduce code duplication.
654639
dn_lcl_dt = zero(FT)
655640
dn_rai_dt = zero(FT)
656641

642+
# --- Condensation of vapor / evaporation of cloud liquid water ---
643+
∂ₜq_lcl_cond = CMNonEq.conv_q_vap_to_q_lcl_icl_MM2015(condevap, tps, q_tot, q_lcl, q_ice, q_rai, zero(q_ice), ρ, T)
644+
∂ₜn_lcl_cond = zero(∂ₜq_lcl_cond) # neglect number change from condensation/evaporation
645+
dq_lcl_dt += ∂ₜq_lcl_cond
646+
dn_lcl_dt += ∂ₜn_lcl_cond
647+
648+
# --- Evaporation of rain ---
649+
evap = CM2.rain_evaporation(sb, aps, tps, q_tot, q_lcl, q_ice, q_rai, zero(q_ice), ρ, N_rai, T)
650+
dq_rai_dt += evap.∂ₜq_rai
651+
dn_rai_dt += evap.∂ₜρn_rai / ρ
652+
657653
# --- Autoconversion ---
658654
acnv = CM2.autoconversion(sb.acnv, sb.pdf_c, q_lcl, q_rai, ρ, N_lcl)
659655
dq_lcl_dt += acnv.dq_lcl_dt
@@ -679,6 +675,17 @@ Used by both warm-only and warm+ice dispatch methods to reduce code duplication.
679675
dn_rai_br = CM2.rain_breakup(sb.pdf_r, sb.brek, q_rai, ρ, N_rai, dn_rai_sc)
680676
dn_rai_dt += dn_rai_br / ρ
681677

678+
# --- Number adjustment for mass limits ---
679+
# Cloud liquid
680+
dn_lcl_inc = CM2.number_increase_for_mass_limit(sb.numadj, sb.pdf_c.xc_max, q_lcl, ρ, N_lcl)
681+
dn_lcl_dec = CM2.number_decrease_for_mass_limit(sb.numadj, sb.pdf_c.xc_min, q_lcl, ρ, N_lcl)
682+
dn_lcl_dt += (dn_lcl_inc + dn_lcl_dec) / ρ
683+
684+
# Rain
685+
dn_rai_inc = CM2.number_increase_for_mass_limit(sb.numadj, sb.pdf_r.xr_max, q_rai, ρ, N_rai)
686+
dn_rai_dec = CM2.number_decrease_for_mass_limit(sb.numadj, sb.pdf_r.xr_min, q_rai, ρ, N_rai)
687+
dn_rai_dt += (dn_rai_inc + dn_rai_dec) / ρ
688+
682689
return (; dq_lcl_dt, dq_rai_dt, dn_lcl_dt, dn_rai_dt)
683690
end
684691

@@ -689,7 +696,7 @@ end
689696
bulk_microphysics_tendencies(
690697
::Microphysics2Moment,
691698
mp::Microphysics2MParams{WR, Nothing},
692-
...
699+
ρ, T, q_tot, q_lcl, n_lcl, q_rai, n_rai,
693700
)
694701
695702
Compute 2-moment **warm rain only** microphysics tendencies (Seifert-Beheng 2006).
@@ -719,21 +726,9 @@ For warm rain + P3 ice, see the method that accepts `Microphysics2MParams{FT, WR
719726
- `db_rim_dt`: Rime volume tendency (always zero for warm-only)
720727
"""
721728
@inline function bulk_microphysics_tendencies(
722-
::Microphysics2Moment,
723-
mp::CMP.Microphysics2MParams{WR, Nothing},
724-
tps,
725-
ρ,
726-
T,
727-
q_tot,
728-
q_lcl,
729-
n_lcl,
730-
q_rai,
731-
n_rai,
732-
q_ice = zero(ρ),
733-
n_ice = zero(ρ),
734-
q_rim = zero(ρ),
735-
b_rim = zero(ρ),
736-
logλ = zero(ρ),
729+
::Microphysics2Moment, mp::CMP.Microphysics2MParams{WR, Nothing}, tps,
730+
ρ, T, q_tot, q_lcl, n_lcl, q_rai, n_rai,
731+
q_ice = zero(ρ), n_ice = zero(ρ), q_rim = zero(ρ), b_rim = zero(ρ), logλ = zero(ρ),
737732
) where {WR}
738733
# Clamp negative inputs to zero (robustness against numerical errors)
739734
ρ = UT.clamp_to_nonneg(ρ)
@@ -747,50 +742,27 @@ For warm rain + P3 ice, see the method that accepts `Microphysics2MParams{FT, WR
747742
q_rim = UT.clamp_to_nonneg(q_rim)
748743
b_rim = UT.clamp_to_nonneg(b_rim)
749744

750-
# Unpack warm rain parameters
751-
sb = mp.warm_rain.seifert_beheng
752-
aps = mp.warm_rain.air_properties
753-
754745
# Initialize ice-related tendencies (always zero for warm-only)
755746
dq_ice_dt = zero(ρ)
756747
dq_rim_dt = zero(ρ)
757748
db_rim_dt = zero(ρ)
758749

759750
# --- Core Warm Rain Processes (shared helper) ---
760-
warm = warm_rain_tendencies_2m(sb, q_lcl, q_rai, ρ, n_lcl, n_rai)
751+
warm = warm_rain_tendencies_2m(mp.warm_rain, tps, T, q_tot, q_lcl, q_rai, q_ice, ρ, n_lcl, n_rai)
761752
dq_lcl_dt = warm.dq_lcl_dt
762753
dn_lcl_dt = warm.dn_lcl_dt
763754
dq_rai_dt = warm.dq_rai_dt
764755
dn_rai_dt = warm.dn_rai_dt
765756

766-
# Convert to number densities for remaining functions
767-
N_lcl = ρ * n_lcl
768-
N_rai = ρ * n_rai
769-
770-
# --- Rain evaporation ---
771-
evap = CM2.rain_evaporation(sb, aps, tps, zero(ρ), q_lcl, zero(ρ), q_rai, zero(ρ), ρ, N_rai, T)
772-
dq_rai_dt += evap.evap_rate_1
773-
dn_rai_dt += evap.evap_rate_0 / ρ
774-
775-
# --- Number adjustment for mass limits ---
776-
# Cloud liquid
777-
dn_lcl_inc = CM2.number_increase_for_mass_limit(sb.numadj, sb.pdf_c.xc_max, q_lcl, ρ, N_lcl)
778-
dn_lcl_dec = CM2.number_decrease_for_mass_limit(sb.numadj, sb.pdf_c.xc_min, q_lcl, ρ, N_lcl)
779-
dn_lcl_dt += (dn_lcl_inc + dn_lcl_dec) / ρ
780-
781-
# Rain
782-
dn_rai_inc = CM2.number_increase_for_mass_limit(sb.numadj, sb.pdf_r.xr_max, q_rai, ρ, N_rai)
783-
dn_rai_dec = CM2.number_decrease_for_mass_limit(sb.numadj, sb.pdf_r.xr_min, q_rai, ρ, N_rai)
784-
dn_rai_dt += (dn_rai_inc + dn_rai_dec) / ρ
785-
786757
return (; dq_lcl_dt, dn_lcl_dt, dq_rai_dt, dn_rai_dt, dq_ice_dt, dq_rim_dt, db_rim_dt)
787758
end
788759

789760
"""
790761
bulk_microphysics_tendencies(
791762
::Microphysics2Moment,
792763
mp::Microphysics2MParams{FT, WR, <:P3IceParams},
793-
...
764+
ρ, T, q_tot, q_lcl, n_lcl, q_rai, n_rai,
765+
q_ice, n_ice, q_rim, b_rim, logλ,
794766
)
795767
796768
Compute 2-moment **warm rain + P3 ice** microphysics tendencies.
@@ -828,21 +800,9 @@ to be non-Nothing, eliminating runtime type checks and dynamic dispatch.
828800
- `db_rim_dt`: Rime volume tendency (m³/kg/s)
829801
"""
830802
@inline function bulk_microphysics_tendencies(
831-
::Microphysics2Moment,
832-
mp::CMP.Microphysics2MParams{WR, ICE},
833-
tps,
834-
ρ,
835-
T,
836-
q_tot,
837-
q_lcl,
838-
n_lcl,
839-
q_rai,
840-
n_rai,
841-
q_ice = zero(ρ),
842-
n_ice = zero(ρ),
843-
q_rim = zero(ρ),
844-
b_rim = zero(ρ),
845-
logλ = zero(ρ),
803+
::Microphysics2Moment, mp::CMP.Microphysics2MParams{WR, ICE}, tps,
804+
ρ, T, q_tot, q_lcl, n_lcl, q_rai, n_rai,
805+
q_ice = zero(ρ), n_ice = zero(ρ), q_rim = zero(ρ), b_rim = zero(ρ), logλ = zero(ρ),
846806
) where {WR, ICE <: CMP.P3IceParams}
847807
# Clamp negative inputs to zero (robustness against numerical errors)
848808
ρ = UT.clamp_to_nonneg(ρ)
@@ -857,7 +817,6 @@ to be non-Nothing, eliminating runtime type checks and dynamic dispatch.
857817
b_rim = UT.clamp_to_nonneg(b_rim)
858818

859819
# Unpack warm rain parameters (always present)
860-
sb = mp.warm_rain.seifert_beheng
861820
aps = mp.warm_rain.air_properties
862821

863822
# Initialize ice-related tendencies
@@ -868,7 +827,7 @@ to be non-Nothing, eliminating runtime type checks and dynamic dispatch.
868827
db_rim_dt = zero(ρ)
869828

870829
# --- Core Warm Rain Processes (shared helper) ---
871-
warm = warm_rain_tendencies_2m(sb, q_lcl, q_rai, ρ, n_lcl, n_rai)
830+
warm = warm_rain_tendencies_2m(mp.warm_rain, tps, T, q_tot, q_lcl, q_rai, q_ice, ρ, n_lcl, n_rai)
872831
dq_lcl_dt = warm.dq_lcl_dt
873832
dn_lcl_dt = warm.dn_lcl_dt
874833
dq_rai_dt = warm.dq_rai_dt
@@ -878,26 +837,7 @@ to be non-Nothing, eliminating runtime type checks and dynamic dispatch.
878837
N_lcl = ρ * n_lcl
879838
N_rai = ρ * n_rai
880839

881-
# --- Rain evaporation ---
882-
evap = CM2.rain_evaporation(sb, aps, tps, zero(ρ), q_lcl, zero(ρ), q_rai, zero(ρ), ρ, N_rai, T)
883-
dq_rai_dt += evap.evap_rate_1
884-
dn_rai_dt += evap.evap_rate_0 / ρ
885-
886-
# --- Number adjustment for mass limits ---
887-
# Cloud liquid
888-
dn_lcl_inc = CM2.number_increase_for_mass_limit(sb.numadj, sb.pdf_c.xc_max, q_lcl, ρ, N_lcl)
889-
dn_lcl_dec = CM2.number_decrease_for_mass_limit(sb.numadj, sb.pdf_c.xc_min, q_lcl, ρ, N_lcl)
890-
dn_lcl_dt += (dn_lcl_inc + dn_lcl_dec) / ρ
891-
892-
# Rain
893-
dn_rai_inc = CM2.number_increase_for_mass_limit(sb.numadj, sb.pdf_r.xr_max, q_rai, ρ, N_rai)
894-
dn_rai_dec = CM2.number_decrease_for_mass_limit(sb.numadj, sb.pdf_r.xr_min, q_rai, ρ, N_rai)
895-
dn_rai_dt += (dn_rai_inc + dn_rai_dec) / ρ
896-
897840
# --- P3 Ice Processes ---
898-
# NOTE: P3 uses gamma_inc_inv from SpecialFunctions which is NOT GPU-compatible
899-
# (it uses string formatting for errors). We must keep if-branches to skip
900-
# P3 code when there is no ice, otherwise GPU compilation fails.
901841
p3 = mp.ice.scheme
902842
vel = mp.ice.terminal_velocity
903843
pdf_c = mp.ice.cloud_pdf

0 commit comments

Comments
 (0)