Skip to content

Commit c47b818

Browse files
authored
Merge branch 'main' into he/refactor-2M-tests
2 parents 9a15044 + 505fe8d commit c47b818

File tree

5 files changed

+177
-0
lines changed

5 files changed

+177
-0
lines changed

docs/src/API.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ Microphysics2M.rain_evaporation
7171
Microphysics2M.conv_q_liq_to_q_rai
7272
```
7373

74+
## Distribution tools for 2-moment microphysics
75+
76+
```@docs
77+
DistributionTools
78+
DistributionTools.generalized_gamma_quantile
79+
DistributionTools.generalized_gamma_cdf
80+
DistributionTools.exponential_cdf
81+
DistributionTools.exponential_quantile
82+
```
83+
7484
# P3 scheme
7585

7686
```@docs

src/CloudMicrophysics.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ function conv_q_liq_to_q_rai end
1111
function accretion end
1212

1313
include("Common.jl")
14+
include("DistributionTools.jl")
1415
include("Microphysics0M.jl")
1516
include("Microphysics1M.jl")
1617
include("Microphysics2M.jl")

src/DistributionTools.jl

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"""
2+
DistributionTools
3+
4+
A module containing tools for working with size distributions.
5+
6+
Currently, it contains tools for working with the generalized gamma distribution
7+
and the exponential distribution.
8+
9+
Mostly used for the 2-moment microphysics.
10+
"""
11+
module DistributionTools
12+
13+
import SpecialFunctions as SF
14+
15+
"""
16+
generalized_gamma_quantile(ν, μ, B, Y)
17+
18+
Calculate the quantile (inverse cumulative distribution function) for a
19+
generalized gamma distribution parameterized in the form:
20+
21+
g(x) = A ⋅ x^ν ⋅ exp(-B ⋅ x^μ)
22+
23+
# Arguments
24+
- `ν, μ, B`: The PDF parameters
25+
- `Y`: The probability level (0 ≤ Y ≤ 1) for which to compute the quantile
26+
27+
# Returns
28+
- `x`: The value x such that P(X ≤ x) = Y
29+
"""
30+
function generalized_gamma_quantile(ν, μ, B, Y)
31+
# Compute the inverse of the regularized incomplete gamma function
32+
z = SF.gamma_inc_inv((ν + 1) / μ, Y, 1 - Y)
33+
return (z / B)^(1 / μ)
34+
end
35+
36+
"""
37+
generalized_gamma_cdf(ν, μ, B, x)
38+
39+
Calculate the cumulative distribution function (CDF) for a generalized gamma distribution
40+
parameterized in the form:
41+
42+
g(x) = A * x^ν * exp(-B * x^μ)
43+
44+
The CDF gives the probability P(X ≤ x).
45+
46+
# Arguments
47+
- `ν, μ, B`: The PDF parameters
48+
- `x`: The value at which to evaluate the CDF
49+
50+
# Returns
51+
- `p`: The probability P(X ≤ x)
52+
"""
53+
function generalized_gamma_cdf(ν, μ, B, x)
54+
# Check input validity
55+
μ > 0 || throw(DomainError(μ, "Parameter μ must be positive"))
56+
B > 0 || throw(DomainError(B, "Parameter B must be positive"))
57+
# Handle edge cases
58+
x 0 && return zero(x)
59+
60+
# Compute the regularized incomplete gamma function
61+
p, _ = SF.gamma_inc((ν + 1) / μ, (B * x)^μ)
62+
return p
63+
end
64+
65+
"""
66+
exponential_cdf(D_mean, D)
67+
68+
Calculate the cumulative distribution function (CDF) for an exponential distribution
69+
parameterized in the form:
70+
71+
n(D) = N₀ * exp(-D / D_mean)
72+
73+
where N₀ is a normalizing constant such that the total probability is 1.
74+
75+
# Arguments
76+
- `D_mean`: The mean value of the distribution
77+
- `D`: The point at which to evaluate the CDF (must be ≥ 0)
78+
79+
# Returns
80+
- `p`: The probability P(X ≤ D)
81+
"""
82+
function exponential_cdf(D_mean, D)
83+
# Check input validity
84+
D_mean > 0 || throw(DomainError(D_mean, "Mean parameter must be positive"))
85+
# Handle edge cases
86+
D < 0 && return zero(D)
87+
# Calculate CDF: P(X ≤ D) = 1 - exp(-D/D_mean)
88+
return 1 - exp(-D / D_mean)
89+
end
90+
91+
"""
92+
exponential_quantile(D_mean, Y)
93+
94+
Calculate the quantile (inverse cumulative distribution function) for an exponential distribution
95+
parameterized in the form:
96+
97+
n(D) = N₀ * exp(-D / D_mean)
98+
99+
where N₀ is a normalizing constant such that the total probability is 1.
100+
101+
# Arguments
102+
- `Y`: The probability level (0 ≤ Y ≤ 1) for which to compute the quantile
103+
- `D_mean`: The mean value of the distribution
104+
105+
# Returns
106+
- `D`: The value D such that P(X ≤ D) = Y
107+
"""
108+
function exponential_quantile(D_mean, Y)
109+
# Check input validity
110+
(0 Y 1) || throw(DomainError(Y, "Probability Y must be in [0,1]"))
111+
D_mean > 0 || throw(DomainError(D_mean, "Mean parameter must be positive"))
112+
# Calculate quantile: x = -D_mean * ln(1-Y)
113+
return -D_mean * log(1 - Y)
114+
end
115+
116+
end # module DistributionTools

test/DistributionTools_tests.jl

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
using Test
2+
import CloudMicrophysics.DistributionTools as DT
3+
4+
@testset "DistributionTools" begin
5+
@testset "Generalized Gamma Distribution" begin
6+
# Test parameters
7+
ν = 2.0
8+
μ = 3.0
9+
B = 1.0
10+
11+
# Test CDF and quantile correspondence
12+
for Y in [0.1, 0.25, 0.5, 0.75, 0.9]
13+
x = DT.generalized_gamma_quantile(ν, μ, B, Y)
14+
p = DT.generalized_gamma_cdf(ν, μ, B, x)
15+
@test p Y rtol = 1e-10
16+
end
17+
18+
# Test edge cases
19+
@test DT.generalized_gamma_cdf(ν, μ, B, 0) == 0
20+
@test DT.generalized_gamma_cdf(ν, μ, B, -1) == 0
21+
22+
# Test error handling
23+
@test_throws DomainError DT.generalized_gamma_cdf(ν, -1, B, 1) # μ < 0
24+
@test_throws DomainError DT.generalized_gamma_cdf(ν, μ, -1, 1) # B < 0
25+
end
26+
27+
@testset "Exponential Distribution" begin
28+
# Test parameters
29+
D_mean = 2.0
30+
31+
# Test CDF and quantile correspondence
32+
for Y in [0.1, 0.25, 0.5, 0.75, 0.9]
33+
D = DT.exponential_quantile(D_mean, Y)
34+
p = DT.exponential_cdf(D_mean, D)
35+
@test p Y rtol = 1e-10
36+
end
37+
38+
# Test edge cases
39+
@test DT.exponential_cdf(D_mean, 0) == 0
40+
@test DT.exponential_cdf(D_mean, -1) == 0
41+
@test DT.exponential_cdf(D_mean, Inf) 1 rtol = 1e-10
42+
43+
# Test error handling
44+
@test_throws DomainError DT.exponential_cdf(-1, 1) # D_mean < 0
45+
@test_throws DomainError DT.exponential_quantile(D_mean, -0.1) # Y < 0
46+
@test_throws DomainError DT.exponential_quantile(D_mean, 1.1) # Y > 1
47+
@test_throws DomainError DT.exponential_quantile(-1, 0.5) # D_mean < 0
48+
end
49+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ include("performance_tests.jl")
1818
include("aerosol_activation_calibration.jl")
1919
include("ice_nucleation_calibration.jl")
2020
include("ventilation_tests.jl")
21+
include("DistributionTools_tests.jl")

0 commit comments

Comments
 (0)