@@ -32,7 +32,9 @@ import ..Microphysics1M as CM1
3232import .. Microphysics2M as CM2
3333import .. MicrophysicsNonEq as CMNonEq
3434import .. P3Scheme as CMP3
35+ import .. HetIceNucleation as CM_HetIce
3536import ... ThermodynamicsInterface as TDI
37+ import .. Common as CO
3638
3739export MicrophysicsScheme,
3840 Microphysics0Moment,
@@ -804,6 +806,9 @@ to be non-Nothing, eliminating runtime type checks and dynamic dispatch.
804806 ρ, T, q_tot, q_lcl, n_lcl, q_rai, n_rai,
805807 q_ice = zero (ρ), n_ice = zero (ρ), q_rim = zero (ρ), b_rim = zero (ρ), logλ = zero (ρ),
806808) where {WR, ICE <: CMP.P3IceParams }
809+ ϵₘ = UT. ϵ_numerics_2M_M (FT)
810+ ϵₙ = UT. ϵ_numerics_2M_M (FT)
811+ ϵB = UT. ϵ_numerics_P3_B (FT)
807812 # Clamp negative inputs to zero (robustness against numerical errors)
808813 ρ = UT. clamp_to_nonneg (ρ)
809814 q_tot = UT. clamp_to_nonneg (q_tot)
@@ -816,13 +821,26 @@ to be non-Nothing, eliminating runtime type checks and dynamic dispatch.
816821 q_rim = UT. clamp_to_nonneg (q_rim)
817822 b_rim = UT. clamp_to_nonneg (b_rim)
818823
824+ # Convert to volumetric quantities for P3 functions
825+ L_lcl = q_lcl * ρ # [kg lcl / m³ air]
826+ L_rai = q_rai * ρ # [kg rai / m³ air]
827+ N_lcl = n_lcl * ρ # [1 / m³ air]
828+ N_rai = n_rai * ρ # [1 / m³ air]
829+ L_ice = q_ice * ρ # [kg ice / m³ air]
830+ N_ice = n_ice * ρ # [1 / m³ air]
831+ L_rim = q_rim * ρ # [kg rim / m³ air]
832+ B_rim = b_rim * ρ # [m³ rim / m³ air]
833+ # Compute rime fraction and density
834+ F_rim = ifelse (q_ice > ϵₘ, L_rim / L_ice, zero (L_rim))
835+ ρ_rim = ifelse (b_rim > ϵB, L_rim / B_rim, zero (L_rim)) # [kg rim / m³ rim]
836+
819837 # Unpack warm rain parameters (always present)
820838 aps = mp. warm_rain. air_properties
839+ subdep = mp. warm_rain. subdep
821840
822841 # Initialize ice-related tendencies
823842 dq_ice_dt = zero (ρ)
824- # TODO : When ice number concentration becomes prognostic, add:
825- # dn_ice_dt = zero(ρ) # Ice number tendency (changes due to melting, aggregation)
843+ dn_ice_dt = zero (ρ) # Ice number tendency (changes due to melting, aggregation)
826844 dq_rim_dt = zero (ρ)
827845 db_rim_dt = zero (ρ)
828846
@@ -833,51 +851,24 @@ to be non-Nothing, eliminating runtime type checks and dynamic dispatch.
833851 dq_rai_dt = warm. dq_rai_dt
834852 dn_rai_dt = warm. dn_rai_dt
835853
836- # Convert to number densities for remaining functions
837- N_lcl = ρ * n_lcl
838- N_rai = ρ * n_rai
839-
840854 # --- P3 Ice Processes ---
841855 p3 = mp. ice. scheme
842856 vel = mp. ice. terminal_velocity
843857 pdf_c = mp. ice. cloud_pdf
844858 pdf_r = mp. ice. rain_pdf
859+ heterogeneous = mp. ice. heterogeneous
860+ deposition_condfreeze = mp. ice. deposition_condfreeze
861+
845862
846863 # Only compute ice processes if there is ice mass/number present
847- if (q_ice > zero (q_ice) || n_ice > zero (n_ice))
848- # Convert to volumetric quantities for P3 functions
849- L_ice = q_ice * ρ # [kg/m³]
850- N_ice = n_ice * ρ # [1/m³]
851- L_lcl = q_lcl * ρ # [kg/m³]
852- N_lcl = n_lcl * ρ # [1/m³]
853- L_rai = q_rai * ρ # [kg/m³]
854- N_rai = n_rai * ρ # [1/m³]
855-
856- # Compute rime fraction and density
857- F_rim = ifelse (q_ice > zero (q_ice), q_rim / q_ice, zero (q_rim))
858- ρ_rim = ifelse (b_rim > zero (b_rim), q_rim * ρ / (b_rim * ρ), ρ) # [kg/m³]
864+ if q_ice > ϵₘ && n_ice > ϵₙ
859865
860866 # Liquid-ice collision sources (core P3 process)
861867 # Only compute if there is ice present
862868 if L_ice > zero (L_ice) && N_ice > zero (N_ice)
863869 coll = CMP3. bulk_liquid_ice_collision_sources (
864- p3,
865- logλ,
866- L_ice,
867- N_ice,
868- F_rim,
869- ρ_rim,
870- pdf_c,
871- pdf_r,
872- L_lcl,
873- N_lcl,
874- L_rai,
875- N_rai,
876- aps,
877- tps,
878- vel,
879- ρ,
880- T,
870+ p3, logλ, L_ice, N_ice, F_rim, ρ_rim, pdf_c, pdf_r,
871+ L_lcl, N_lcl, L_rai, N_rai, aps, tps, vel, ρ, T,
881872 )
882873
883874 # Add collision tendencies
@@ -907,9 +898,69 @@ to be non-Nothing, eliminating runtime type checks and dynamic dispatch.
907898 end
908899 end
909900
910- # TODO : When ice number concentration is tracked, add dn_ice_dt to return tuple:
911- # return (; dq_lcl_dt, dn_lcl_dt, dq_rai_dt, dn_rai_dt, dq_ice_dt, dn_ice_dt, dq_rim_dt, db_rim_dt)
912- return (; dq_lcl_dt, dn_lcl_dt, dq_rai_dt, dn_rai_dt, dq_ice_dt, dq_rim_dt, db_rim_dt)
901+ # --- -------------------- ---
902+ # --- Ice Nucleation Modes ---
903+ # --- -------------------- ---
904+
905+ # --- Ice Nucleation (Deposition) ---
906+ # Assume nucleated particles have diameter 1μm --> nucleated mass (per particle)
907+ D_nuc_ice = FT (1e-6 ) # 1μm
908+ m_nuc = p3. ρ_i * CO. volume_sphere_D (D_nuc_ice) # [kg]
909+
910+ # TODO : The parameterixation should return a rate, `∂N/∂t`, not number changes `ΔN`
911+ n_dep = CM_HetIce. P3_deposition_N_i (deposition_condfreeze, T) / ρ # [particles / kg air]
912+ m_dep = n_dep * m_nuc # [kg ice / kg air]
913+ dn_ice_dt += n_dep
914+ dq_ice_dt += m_dep
915+ dq_rim_dt += m_dep
916+ db_rim_dt += m_dep / 900
917+
918+ # --- Heterogeneous Ice Nucleation (Immersion freezing) ---
919+ # Assume mass loss is mean condensate mass
920+ m_lcl = ifelse (n_lcl > ϵₙ, q_lcl / n_lcl, zero (q_lcl)) # mean liquid mass
921+
922+ # TODO : The parameterixation should return a rate, `∂N/∂t`, not number changes `ΔN`
923+ inpc = CM_HetIce. INP_concentration_mean (heterogeneous, T) / ρ # [particles / kg air]
924+ n_het = max (0 , inpc - n_ice) # [particles / kg air]
925+ q_het = n_het * m_lcl # [kg ice / kg air]
926+
927+ dn_ice_dt += n_het
928+ dq_ice_dt += q_het
929+ dq_rim_dt += q_het
930+ db_rim_dt += q_het / 900 # = ρ^* TODO : make this a parameter?
931+ dn_lcl_dt -= n_het
932+ dq_lcl_dt -= q_het
933+
934+ # --- Cloud Droplet Condensation Freezing ---
935+ # ref: `homogeneous_freezing` in `parcel/ParcelTendencies.jl`
936+ # get mean diameter of cloud droplets, then convert to volume
937+
938+ # --- Ice Sublimation / Deposition ---
939+ # Deposition/sublimation of cloud ice
940+ ∂ₜq_ice_dep = CMNonEq. conv_q_vap_to_q_lcl_icl_MM2015 (subdep, tps, q_tot, q_lcl, q_ice, q_rai, zero (q_ice), ρ, T)
941+ # No ice deposition above freezing (lack of INPs)
942+ ∂ₜq_ice_dep = ifelse (T > tps. T_freeze, min (∂ₜq_ice_dep, zero (T)), ∂ₜq_ice_dep)
943+ # During sublimation, the number of ice particles decreases in proportion to the mean ice mass
944+ # During deposition, the number of ice particles remain unchanged
945+ ∂ₜn_ice_dep = ifelse (∂ₜq_ice_dep < 0 , (n_ice / q_ice) * ∂ₜq_ice_dep, zero (∂ₜq_ice_dep))
946+ dq_ice_dt += ∂ₜq_ice_dep
947+ dn_ice_dt += ∂ₜn_ice_dep
948+
949+ # --- Ice Self-collection (Aggregation) ---
950+ if q_ice > ϵₘ && n_ice > ϵₙ
951+ state = CMP3. P3State (p3, L_ice, N_ice, F_rim, ρ_rim)
952+ S_ice_agg = CMP3. ice_self_collection (state, logλ, aps, tps, vel, ρ, T)
953+ dn_ice_dt -= S_ice_agg. dNdt / ρ
954+ end
955+
956+ # --- Rain Heterogeneous Freezing ---
957+ # TODO : Implement heterogeneous freezing of rain
958+ # This process is currently missing in P3_processes.jl
959+ # S_rai_frz = ...
960+ # dq_rai_dt -= S_rai_frz
961+ # dq_ice_dt += S_rai_frz
962+
963+ return (; dq_lcl_dt, dn_lcl_dt, dq_rai_dt, dn_rai_dt, dq_ice_dt, dn_ice_dt, dq_rim_dt, db_rim_dt)
913964end
914965
915966end # module BulkMicrophysicsTendencies
0 commit comments