Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "CloudMicrophysics"
uuid = "6a9e3e04-43cd-43ba-94b9-e8782df3c71b"
authors = ["Climate Modeling Alliance"]
version = "0.31.12"
version = "0.32"

[deps]
ClimaParams = "5c42b081-d73a-476f-9059-fd94b934656c"
Expand Down
30 changes: 4 additions & 26 deletions src/BulkMicrophysicsTendencies.jl
Original file line number Diff line number Diff line change
Expand Up @@ -567,31 +567,13 @@ derivatives; snow and cloud formation derivatives are zero for now.
end

# --- 0-Moment Microphysics ---

"""
_precip_energy(tps, T, q_lcl, q_icl)

Internal energy of removed condensate, weighted by the liquid fraction.
Shared helper for the two 0-moment `bulk_microphysics_tendencies` methods.
"""
@inline function _precip_energy(tps, T, q_lcl, q_icl)
λ = TDI.liquid_fraction(tps, T, q_lcl, q_icl)
I_liq = TDI.internal_energy_liquid(tps, T)
I_ice = TDI.internal_energy_ice(tps, T)
return λ * I_liq + (1 - λ) * I_ice
end

"""
bulk_microphysics_tendencies(::Microphysics0Moment, mp, tps, T, q_lcl, q_icl)
bulk_microphysics_tendencies(::Microphysics0Moment, mp, tps, T, q_lcl, q_icl, q_vap_sat)

Compute 0-moment microphysics tendencies in one fused call.

Returns a NamedTuple with:
- `dq_tot_dt`: Total water tendency from precipitation removal [kg/kg/s]
- `e_int_precip`: Internal energy of removed condensate [J/kg]

Caller adds geopotential Φ for energy tendency: `e_tot = dq_tot_dt * (e_int_precip + Φ)`
Returns the total water tendency `dq_tot_dt` (a scalar, in kg/kg/s) from precipitation removal.

The first form uses the fixed condensate threshold `qc_0`;
the second form uses the supersaturation threshold `S_0 * q_vap_sat`.
Expand All @@ -606,7 +588,6 @@ the second form uses the supersaturation threshold `S_0 * q_vap_sat`.

# Notes
- Does NOT apply limiters (caller applies based on timestep)
- Does NOT include geopotential (caller adds Φ for energy tendency)
"""
@inline function bulk_microphysics_tendencies(
::Microphysics0Moment,
Expand All @@ -619,10 +600,8 @@ the second form uses the supersaturation threshold `S_0 * q_vap_sat`.
q_lcl = UT.clamp_to_nonneg(q_lcl)
q_icl = UT.clamp_to_nonneg(q_icl)
dq_tot_dt = CM0.remove_precipitation(mp.precip, q_lcl, q_icl)
e_int_precip = _precip_energy(tps, T, q_lcl, q_icl)
return (; dq_tot_dt, e_int_precip)
return dq_tot_dt
end

@inline function bulk_microphysics_tendencies(
::Microphysics0Moment,
mp::CMP.Microphysics0MParams,
Expand All @@ -635,8 +614,7 @@ end
q_lcl = UT.clamp_to_nonneg(q_lcl)
q_icl = UT.clamp_to_nonneg(q_icl)
dq_tot_dt = CM0.remove_precipitation(mp.precip, q_lcl, q_icl, q_vap_sat)
e_int_precip = _precip_energy(tps, T, q_lcl, q_icl)
return (; dq_tot_dt, e_int_precip)
return dq_tot_dt
end

# --- 2-Moment Microphysics Helper Functions ---
Expand Down Expand Up @@ -826,7 +804,7 @@ to be non-Nothing, eliminating runtime type checks and dynamic dispatch.
- `tps`: Thermodynamics parameters
- `ρ`: Air density (kg/m³)
- `T`: Temperature (K)
- `q_tot`: Total water specific content (kg/kg)
- `q_tot`: Total water specific content (kg/kg)
- `q_lcl`: Cloud liquid specific content (kg/kg)
- `n_lcl`: Cloud droplet number per kg air (1/kg)
- `q_rai`: Rain specific content (kg/kg)
Expand Down
27 changes: 12 additions & 15 deletions test/bulk_tendencies_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,41 +31,39 @@ function test_bulk_microphysics_0m_tendencies(FT)
q_lcl = FT(2e-3) # Above threshold
q_icl = FT(0)

tendencies = BMT.bulk_microphysics_tendencies(
dq_tot_dt = BMT.bulk_microphysics_tendencies(
BMT.Microphysics0Moment(),
mp, tps, T, q_lcl, q_icl,
)

# Should be negative (removing condensate)
@test tendencies.dq_tot_dt < FT(0)
# Internal energy should be finite
@test isfinite(tendencies.e_int_precip)
@test dq_tot_dt < FT(0)
end

@testset "BulkMicrophysicsTendencies 0M - Below threshold" begin
T = T_freeze + FT(10)
q_lcl = FT(1e-6) # Below threshold
q_icl = FT(0)

tendencies = BMT.bulk_microphysics_tendencies(
dq_tot_dt = BMT.bulk_microphysics_tendencies(
BMT.Microphysics0Moment(),
mp, tps, T, q_lcl, q_icl,
)

# No precipitation when below threshold
@test tendencies.dq_tot_dt == FT(0)
@test dq_tot_dt == FT(0)
end

@testset "BulkMicrophysicsTendencies 0M - Type stability" begin
T = T_freeze + FT(5)
q_lcl = FT(1e-3)
q_icl = FT(5e-4)

tendencies = @inferred BMT.bulk_microphysics_tendencies(
dq_tot_dt = @inferred BMT.bulk_microphysics_tendencies(
BMT.Microphysics0Moment(),
mp, tps, T, q_lcl, q_icl,
)
@test tendencies isa NamedTuple{(:dq_tot_dt, :e_int_precip), NTuple{2, FT}}
@test dq_tot_dt isa FT
end

@testset "BulkMicrophysicsTendencies 0M - S_0 precipitation removal" begin
Expand All @@ -75,14 +73,13 @@ function test_bulk_microphysics_0m_tendencies(FT)
q_icl = FT(0)
q_vap_sat = TDI.saturation_vapor_specific_content_over_liquid(tps, T, ρ)

tendencies = BMT.bulk_microphysics_tendencies(
dq_tot_dt = BMT.bulk_microphysics_tendencies(
BMT.Microphysics0Moment(),
mp, tps, T, q_lcl, q_icl, q_vap_sat,
)

# Should be negative (removing condensate above S_0 * q_vap_sat)
@test tendencies.dq_tot_dt < FT(0)
@test isfinite(tendencies.e_int_precip)
@test dq_tot_dt < FT(0)
end

@testset "BulkMicrophysicsTendencies 0M - S_0 below threshold" begin
Expand All @@ -93,12 +90,12 @@ function test_bulk_microphysics_0m_tendencies(FT)
q_lcl = FT(1e-8)
q_icl = FT(0)

tendencies = BMT.bulk_microphysics_tendencies(
dq_tot_dt = BMT.bulk_microphysics_tendencies(
BMT.Microphysics0Moment(),
mp, tps, T, q_lcl, q_icl, q_vap_sat,
)

@test tendencies.dq_tot_dt == FT(0)
@test dq_tot_dt == FT(0)
end

@testset "BulkMicrophysicsTendencies 0M - S_0 type stability" begin
Expand All @@ -108,11 +105,11 @@ function test_bulk_microphysics_0m_tendencies(FT)
q_icl = FT(5e-4)
q_vap_sat = TDI.saturation_vapor_specific_content_over_liquid(tps, T, ρ)

tendencies = @inferred BMT.bulk_microphysics_tendencies(
dq_tot_dt = @inferred BMT.bulk_microphysics_tendencies(
BMT.Microphysics0Moment(),
mp, tps, T, q_lcl, q_icl, q_vap_sat,
)
@test tendencies isa NamedTuple{(:dq_tot_dt, :e_int_precip), NTuple{2, FT}}
@test dq_tot_dt isa FT
end
end

Expand Down
19 changes: 9 additions & 10 deletions test/gpu_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1001,10 +1001,9 @@ function test_gpu(FT)
end # TT.@testset "Modal nucleation kernels"

TT.@testset "Bulk microphysics tendencies kernels" begin
# 0M tests (returns dq_tot_dt, e_int_precip)
# 0M tests (returns dq_tot_dt scalar only)
ndrange = 10
DT = @NamedTuple{dq_tot_dt::FT, e_int_precip::FT}
(; output) = setup_output(ndrange, DT)
(; output) = setup_output(ndrange, FT)
liquid_frac = constant_data(FT(0.5); ndrange)
qc = constant_data(FT(1e-3); ndrange)
T = constant_data(FT(280.0); ndrange)
Expand All @@ -1013,21 +1012,21 @@ function test_gpu(FT)
TT.@testset "0M" begin
kernel!(mp_0m, tps, output, liquid_frac, qc, T; ndrange)
TT.@test allequal(Array(output))
tendencies = Array(output)[1]
TT.@test tendencies.dq_tot_dt ≤ 0 # Precipitation removal (dq_tot_dt) always negative or zero
TT.@test isfinite(tendencies.e_int_precip)
dq_tot_dt = Array(output)[1]
TT.@test dq_tot_dt ≤ 0 # Precipitation removal (dq_tot_dt) always negative or zero
TT.@test isfinite(dq_tot_dt)
end

# 0M S_0 tests (with q_vap_sat)
(; output) = setup_output(ndrange, DT)
(; output) = setup_output(ndrange, FT)
q_vap_sat = constant_data(FT(0.01); ndrange)
kernel_s0! = test_bulk_tendencies_0m_S0_kernel!(backend, work_groups)
TT.@testset "0M S_0" begin
kernel_s0!(mp_0m, tps, output, liquid_frac, qc, T, q_vap_sat; ndrange)
TT.@test allequal(Array(output))
tendencies = Array(output)[1]
TT.@test tendencies.dq_tot_dt ≤ 0
TT.@test isfinite(tendencies.e_int_precip)
dq_tot_dt = Array(output)[1]
TT.@test dq_tot_dt ≤ 0
TT.@test isfinite(dq_tot_dt)
end


Expand Down
15 changes: 6 additions & 9 deletions test/type_stability_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,10 @@ function run_type_stability_tests()
)

@test tendencies_0M isa Vector
@test eltype(tendencies_0M) <: NamedTuple
# strict check
@test eltype(tendencies_0M) === FT
val0 = tendencies_0M[1]
for k in keys(val0)
@test getproperty(val0, k) isa FT
end
@test val0 isa FT
@test isfinite(val0)

# --- 0-Moment (S_0 mode) ---
ρ_0M = fill(FT(1.2), N)
Expand All @@ -55,11 +53,10 @@ function run_type_stability_tests()
)

@test tendencies_0M_S0 isa Vector
@test eltype(tendencies_0M_S0) <: NamedTuple
@test eltype(tendencies_0M_S0) === FT
val0s = tendencies_0M_S0[1]
for k in keys(val0s)
@test getproperty(val0s, k) isa FT
end
@test val0s isa FT
@test isfinite(val0s)

# --- 1-Moment ---
mp1 = CMP.Microphysics1MParams(FT)
Expand Down
Loading