From 08fa7cf4dd54bd1dd275017259be97f3407e8451 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Wed, 21 Feb 2024 15:02:57 -0800 Subject: [PATCH 01/33] terminal velocity start to implement --- docs/src/P3Scheme.md | 24 +++- docs/src/plots/P3LambdaErrorPlots.jl | 159 +++++++++++++++++++++++++++ docs/src/plots/P3ShapeSolverPlots.jl | 56 ++++++++++ src/P3Scheme.jl | 123 ++++++++++++++++++++- test/p3_tests.jl | 8 +- 5 files changed, 359 insertions(+), 11 deletions(-) create mode 100644 docs/src/plots/P3LambdaErrorPlots.jl create mode 100644 docs/src/plots/P3ShapeSolverPlots.jl diff --git a/docs/src/P3Scheme.md b/docs/src/P3Scheme.md index 0c8ffbbe2..405199ef7 100644 --- a/docs/src/P3Scheme.md +++ b/docs/src/P3Scheme.md @@ -125,14 +125,34 @@ As a result ``q\_{ice}`` can be expressed as a sum of inclomplete gamma function | condition(s) | ``q_{ice} = \int \! m(D) N'(D) \mathrm{d}D`` | gamma representation | |:---------------------------------------------|:-----------------------------------------------------------------------------------------|:---------------------------------------------| | ``D < D_{th}`` | ``\int_{0}^{D_{th}} \! \frac{\pi}{6} \rho_i \ D^3 N'(D) \mathrm{d}D`` | ``\frac{\pi}{6} \rho_i N_0 \lambda \,^{-(\mu \, + 4)} (\Gamma \,(\mu \, + 4) - \Gamma \,(\mu \, + 4, \lambda \,D_{th}))``| -| ``q_{rim} = 0`` and ``D > D_{th}`` | ``\int_{D_{th}}^{\infty} \! \alpha_{va} \ D^{\beta_{va}} N'(D) \mathrm{d}D`` | ``\alpha_{va} \ N_0 \lambda \,^{-(\mu \, + \beta_{va} \, + 1)} (\Gamma \,(\mu \, + \beta_{va} \, + 1) + \Gamma \,(\mu \, + \beta_{va} \, + 1, \lambda \,D_{th}) - (\mu \, + \beta_{va} \,)\Gamma \,(\mu \, + \beta_{va} \,))`` | +| ``q_{rim} = 0`` and ``D > D_{th}`` | ``\int_{D_{th}}^{\infty} \! \alpha_{va} \ D^{\beta_{va}} N'(D) \mathrm{d}D`` | ``\alpha_{va} \ N_0 \lambda \,^{-(\mu \, + \beta_{va} \, + 1)} (\Gamma \,(\mu \, + \beta_{va} \, + 1, \lambda \,D_{th}))`` | | ``q_{rim} > 0`` and ``D_{gr} > D > D_{th}`` | ``\int_{D_{th}}^{D_{gr}} \! \alpha_{va} \ D^{\beta_{va}} N'(D) \mathrm{d}D`` | ``\alpha_{va} \ N_0 \lambda \,^{-(\mu \, + \beta_{va} \, + 1)} (\Gamma \,(\mu \, + \beta_{va} \, + 1, \lambda \,D_{th}) - \Gamma \,(\mu \, + \beta_{va} \, + 1, \lambda \,D_{gr}))`` | | ``q_{rim} > 0`` and ``D_{cr} > D > D_{gr}`` | ``\int_{D_{gr}}^{D_{cr}} \! \frac{\pi}{6} \rho_g \ D^3 N'(D) \mathrm{d}D`` | ``\frac{\pi}{6} \rho_g N_0 \lambda \,^{-(\mu \, + 4)} (\Gamma \,(\mu \, + 4, \lambda \,D_{gr}) - \Gamma \,(\mu \, + 4, \lambda \,D_{cr}))`` | -| ``q_{rim} > 0`` and ``D > D_{cr}`` | ``\int_{D_{cr}}^{\infty} \! \frac{\alpha_{va}}{1-F_r} D^{\beta_{va}} N'(D) \mathrm{d}D`` | ``\frac{\alpha_{va}}{1-F_r} N_0 \lambda \,^{-(\mu \, + \beta_{va} \, + 1)} (\Gamma \,(\mu \, + \beta_{va} \, + 1) + \Gamma \,(\mu \, + \beta_{va} \, + 1, \lambda \,D_{cr}) - (\mu \, + \beta_{va} \,)\Gamma \,(\mu \, + \beta_{va} \,))`` | +| ``q_{rim} > 0`` and ``D > D_{cr}`` | ``\int_{D_{cr}}^{\infty} \! \frac{\alpha_{va}}{1-F_r} D^{\beta_{va}} N'(D) \mathrm{d}D`` | ``\frac{\alpha_{va}}{1-F_r} N_0 \lambda \,^{-(\mu \, + \beta_{va} \, + 1)} (\Gamma \,(\mu \, + \beta_{va} \, + 1, \lambda \,D_{cr}))`` | where ``\Gamma \,(a, z) = \int_{z}^{\infty} \! t^{a - 1} e^{-t} \mathrm{d}D`` and ``\Gamma \,(a) = \Gamma \,(a, 0)`` for simplicity. +An initial guess for the non-linear solver is found by approximating the gamma functions as a simple power function. + +```@example +include("plots/P3ShapeSolverPlots.jl") +``` +![](SolverInitialGuess.svg) + +This equation is given by ``(log(q_{approx}) - log(q1)) = slope (log(\lambda) - log(p1))``. Solving for ``q_{approx}`` we get `` q_{approx} = q_1 \frac{\lambda}{p_1} ^{slope}`` where `` slope = \frac{log(q1) - log(q2)}{log(p1) - log(p2)}``, ``p1`` and ``p2`` are defining ``\lambda`` values of the estimated line (we use p1 = 1e2, p2 = 1e6), ``q1 = q(p1)`` and ``q2 = q(p2)`` are the corresponding calculated q values for the given ``F_r`` and ``\rho_r`` values. + +We use this approximation to calculate a ``\lambda_{guess}`` value which will set our initial guess. Solving for ``\lambda`` in the power function we get ``\lambda_{guess} = p1 (\frac{q}{q1})^{(\frac{log(q1)-log(q2)}{log(p1)-log(p2)})}``. Thus, given any q we can calculate a ``\lambda`` around which to expect the true solved ``\lambda`` value. + +For small values of ``\lambda_{guess}`` it was found to be more efficient to use constant initial guesses. + +Using this approach we get the following relative errors in our solved ``\lambda`` vs the expected ``lambda`` within the solver. + +```@example +include("plots/P3LambdaErrorPlots.jl") +``` +![](P3LambdaHeatmap.svg) + ## Example figures Below we show the m(D) regime, replicating Figures 1 (a) and (b) from [MorrisonMilbrandt2015](@cite). diff --git a/docs/src/plots/P3LambdaErrorPlots.jl b/docs/src/plots/P3LambdaErrorPlots.jl new file mode 100644 index 000000000..8dc2c3c76 --- /dev/null +++ b/docs/src/plots/P3LambdaErrorPlots.jl @@ -0,0 +1,159 @@ +import CloudMicrophysics as CM +import CloudMicrophysics.P3Scheme as P3 +import CloudMicrophysics.Parameters as CMP +import CLIMAParameters as CP +import SpecialFunctions as SF +import RootSolvers as RS +import CairoMakie as Plt + +FT = Float64 + +const PSP3 = CMP.ParametersP3 +p3 = CMP.ParametersP3(FT) + +function λ_diff(F_r::FT, ρ_r::FT, N::FT, λ_ex::FT, p3::PSP3) where {FT} + + # Find the P3 scheme thresholds + th = P3.thresholds(p3, ρ_r, F_r) + # Convert λ to ensure it remains positive + x = log(λ_ex) + # Compute mass density based on input shape parameters + q_calc = P3.q_gamma(p3, F_r, N, x, th) + + (λ_calculated,) = P3.distribution_parameter_solver(p3, q_calc, N, ρ_r, F_r) + return abs(λ_ex - λ_calculated) +end + +function get_errors( + p3::PSP3, + λ_min::FT, + λ_max::FT, + F_r_min::FT, + F_r_max::FT, + ρ_r::FT, + N::FT, + λSteps::Int, + F_rSteps::Int, +) where {FT} + λs = range(FT(λ_min), stop = λ_max, length = λSteps) + F_rs = range(F_r_min, stop = F_r_max, length = F_rSteps) + E = zeros(λSteps, F_rSteps) + min = Inf + max = -Inf + + for i in 1:λSteps + for j in 1:F_rSteps + λ = λs[i] + F_r = F_rs[j] + + diff = λ_diff(F_r, ρ_r, N, λ, p3) + er = log(diff / λ) + + E[i, j] = er + + if er > max && er < Inf + max = er + end + if er < min && er > -Inf + min = er + end + + end + end + return (λs = λs, F_rs = F_rs, E = E, min = min, max = max) +end + +function plot_relerrors( + N::FT, + λ_min::FT, + λ_max::FT, + F_r_min::FT, + F_r_max::FT, + ρ_r_min::FT, + ρ_r_max::FT, + λSteps::Int, + F_rSteps::Int, + numPlots::Int, + p3::PSP3, +) where {FT} + + ρ_rs = range(ρ_r_min, stop = ρ_r_max, length = numPlots) + + f = Plt.Figure() + + x = 1 + y = 1 + for i in 1:numPlots + + ρ = ρ_rs[i] + + Plt.Axis( + f[x, y], + xlabel = "λ", + ylabel = "F_r", + title = string( + "log(relative error calculated λ) for ρ_r = ", + string(ρ), + ), + width = 400, + height = 300, + ) + + (λs, F_rs, E, min, max) = get_errors( + p3, + λ_min, + λ_max, + F_r_min, + F_r_max, + ρ, + N, + λSteps, + F_rSteps, + ) + + Plt.heatmap!(λs, F_rs, E) + Plt.Colorbar( + f[x, y + 1], + limits = (min, max), + colormap = :viridis, + flipaxis = false, + ) + + y = y + 2 + if (y > 6) + x = x + 1 + y = 1 + end + end + + Plt.resize_to_layout!(f) + Plt.save("P3LambdaHeatmap.svg", f) +end + +# Define variables for heatmap relative error plots: + +λ_min = FT(1e2) +λ_max = FT(1e6) +F_r_min = FT(0) +F_r_max = FT(1 - eps(FT)) +ρ_r_min = FT(100) +ρ_r_max = FT(900) +N = FT(1e8) + +λ_Steps = 100 +F_r_Steps = 100 +NumPlots = 9 + +plot_relerrors( + N, + λ_min, + λ_max, + F_r_min, + F_r_max, + ρ_r_min, + ρ_r_max, + λ_Steps, + F_r_Steps, + NumPlots, + p3, +) diff --git a/docs/src/plots/P3ShapeSolverPlots.jl b/docs/src/plots/P3ShapeSolverPlots.jl new file mode 100644 index 000000000..878b4b47b --- /dev/null +++ b/docs/src/plots/P3ShapeSolverPlots.jl @@ -0,0 +1,56 @@ +import CairoMakie as Plt +import CloudMicrophysics as CM +import CLIMAParameters as CP +import CloudMicrophysics.Parameters as CMP +import CloudMicrophysics.P3Scheme as P3 + +FT = Float64 + +const PSP3 = CMP.ParametersP3 +p3 = CMP.ParametersP3(FT) + +function guess_value(λ::FT, p1::FT, p2::FT, q1::FT, q2::FT) + return q1 * (λ / p1)^((log(q1) - log(q2)) / (log(p1) - log(p2))) +end + +function lambda_guess_plot(F_r::FT, ρ_r::FT) where {FT} + N = FT(1e8) + + λs = FT(1e2):FT(1e2):FT(1e6 + 1) + th = P3.thresholds(p3, ρ_r, F_r) + qs = [P3.q_gamma(p3, F_r, N, log(λ), th) for λ in λs] + + guesses = [guess_value(λ, λs[1], last(λs), qs[1], last(qs)) for λ in λs] + + f = Plt.Figure() + Plt.Axis( + f[1, 1], + xscale = log, + yscale = log, + xticks = [10^2, 10^3, 10^4, 10^5, 10^6], + yticks = [10^3, 1, 10^-3, 10^-6], + xlabel = "λ", + ylabel = "q", + title = "q vs λ", + height = 300, + width = 400, + ) + + l1 = Plt.lines!(λs, qs, linewidth = 3, color = "Black", label = "q") + l2 = Plt.lines!( + λs, + guesses, + linewidth = 2, + linestyle = :dash, + color = "Red", + label = "q_approximated", + ) + + Plt.axislegend("Legend", position = :lb) + + Plt.resize_to_layout!(f) + Plt.save("SolverInitialGuess.svg", f) + +end + +lambda_guess_plot(FT(0.5), FT(200)) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index da3605413..dcea1f34d 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -228,7 +228,7 @@ Returns the shape parameter μ for a given λ value Eq. 3 in Morrison and Milbrandt (2015). """ function DSD_μ(p3::PSP3, λ::FT) where {FT} - @assert λ > FT(0) + #@assert λ > FT(0) return min(p3.μ_max, max(FT(0), p3.a * λ^p3.b - p3.c)) end @@ -269,7 +269,8 @@ end # q_rim = 0 and D_min = D_th, D_max = inf function q_rz(p3::PSP3, N_0::FT, λ::FT, D_min::FT) where {FT} x = DSD_μ(p3, λ) + p3.β_va + 1 - return α_va_si(p3) * N_0 / λ^x * (Γ(x, λ * D_min)) + return α_va_si(p3) * N_0 / λ^x * + (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) end # q_rim > 0 and D_min = D_th and D_max = D_gr function q_n(p3::PSP3, N_0::FT, λ::FT, D_min::FT, D_max::FT) where {FT} @@ -280,7 +281,8 @@ end # q_rim > 0 and D_min = D_cr, D_max = inf function q_r(p3::PSP3, F_r::FT, N_0::FT, λ::FT, D_min::FT) where {FT} x = DSD_μ(p3, λ) + p3.β_va + 1 - return α_va_si(p3) * N_0 / (1 - F_r) / λ^x * (Γ(x, λ * D_min)) + return α_va_si(p3) * N_0 / (1 - F_r) / λ^x * + (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) end """ @@ -317,6 +319,49 @@ function q_gamma( ) end +""" + get_bounds(N, N̂, q, F_r, p3, th) + + - N - ice number concentration [1/m3] + - N̂ - normalization as set in distribution_parameter_solver() + - q - mass mixing ratio + - F_r -rime mass fraction [q_rim/q_i] + - p3 - a struct with P3 scheme parameters + - th - thresholds() nonlinear solve output tuple (D_cr, D_gr, ρ_g, ρ_d) + + Returns estimated guess for λ from q to be used in distribution_parameter_solver() +""" +function get_bounds( + N::FT, + N̂::FT, + q::FT, + F_r::FT, + p3::PSP3, + th = (; D_cr = FT(0), D_gr = FT(0), ρ_g = FT(0), ρ_d = FT(0)), +) where {FT} + + left = FT(1e2) + right = FT(1e6) + radius = FT(0.8) + + ql = q_gamma(p3, F_r, N / N̂, log(left), th) + qr = q_gamma(p3, F_r, N / N̂, log(right), th) + + guess = + left * (q / (N̂ * ql))^((log(right) - log(left)) / (log(qr) - log(ql))) + + max = log(guess * exp(radius)) + min = log(guess) + + # Use constant bounds for small λ + if guess < FT(2.5 * 1e4) || isequal(guess, NaN) + min = log(FT(20000)) + max = log(FT(50000)) + end + + return (; min, max) +end + """ distrbution_parameter_solver() @@ -344,14 +389,17 @@ function distribution_parameter_solver( # To ensure that λ is positive solve for x such that λ = exp(x) # We divide by N̂ to deal with large N₀ values for Float32 - N̂ = FT(1e30) + N̂ = FT(1e20) shape_problem(x) = q / N̂ - q_gamma(p3, F_r, N / N̂, x, th) + # Get intial guess for solver + (; min, max) = get_bounds(N, N̂, q, F_r, p3, th) + # Find slope parameter x = RS.find_zero( shape_problem, - RS.SecantMethod(FT(log(20000)), FT(log(50000))), + RS.SecantMethod(min, max), RS.CompactSolution(), RS.RelativeSolutionTolerance(eps(FT)), 10, @@ -360,4 +408,69 @@ function distribution_parameter_solver( return (; λ = exp(x), N_0 = DSD_N₀(p3, N, exp(x))) end +""" + integrate(a, b, c1, c2, c3) + + - a - lower bound + - b - upper bound + - c1, c2, c3 - respective constants + + Integrates the function c1 * D ^ (c2) * exp(-c3 * D) dD from a to b + Returns the result +""" +function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where{FT} + if b == Inf + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3)) + elseif a == 0 + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2) - Γ(1 + c2, b * c3)) + else + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3) - Γ(1 + c2, b * c3)) + end +end + +""" +""" +function terminal_velocity_mass(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where{FT} + + # Get the thresholds for different particles regimes + th = thresholds(p3, ρ_r, F_r) + D_th = D_th_helper(p3) + + # Get the shape parameters + (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) + μ = DSD_μ(p3, λ) + + # Get the ai, bi, ci constants for velocity calculations + + + # Redefine α_va to be in si units + α_va = α_va_si(p3) + + # STILL NEED TO BE SUMMED OVER RESPECTIVE ai bi and ci + if F_r == 0 + term1 = integrate(0, D_th, π / 6 * p3.ρ_i * ai * N_0, bi + μ + 3, ci + λ) + term2 = integrate(D_th, Inf, α_va * ai * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci + λ) + else + term1 = integrate(0, D_th, π / 6 * p3.ρ_i * ai * N_0, bi + μ + 3, ci + λ) + term2 = integrate(D_th, th.D_gr, α_va * ai * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci + λ) + term3 = integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * ai * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi + 3 + μ, ci + λ) + term4 = ( + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi + p3.β_va + μ + κ(3 * p3.σ), ci + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi + p3.β_va + μ + κ(2 + 2 * p3.σ), ci + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi + p3.β_va + μ + κ(4 + p3.σ), ci + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi + p3.β_va + μ + κ(6), ci + λ) + ) + end + + # Get total sums (top) + return top / q + +end + +""" +""" +function terminal_velocity_number(p3::PSP3, N::FT, F_r::FT, ρ_r::FT) + +end + end diff --git a/test/p3_tests.jl b/test/p3_tests.jl index b5ddd2aa7..36aa1c78e 100644 --- a/test/p3_tests.jl +++ b/test/p3_tests.jl @@ -157,11 +157,11 @@ function test_p3_shape_solver(FT) TT.@testset "shape parameters (nonlinear solver function)" begin # initialize test values: - ep = 1e3 * eps(FT) - N_test = (FT(1e8)) # N values + ep = 1e4 * eps(FT) + N_test = (FT(1e7), FT(1e8), FT(1e9), FT(1e10)) # N values λ_test = (FT(15000), FT(20000)) # test λ values in range - ρ_r_test = (FT(200)) #, FT(1)) #, FT(100)) # representative ρ_r values - F_r_test = (FT(0.5), FT(0.8), FT(0.95)) # representative F_r values + ρ_r_test = (FT(200), FT(400), FT(600), FT(800)) # representative ρ_r values + F_r_test = (FT(0), FT(0.5), FT(0.8), FT(0.95)) # representative F_r values # check that the shape solution solves to give correct values for N in N_test From f128383aa20d7140a1059d7623093c92fc0e857d Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Thu, 22 Feb 2024 13:43:10 -0800 Subject: [PATCH 02/33] Update P3Scheme to use integrate() --- src/P3Scheme.jl | 60 ++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index dcea1f34d..5dc16e82f 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -218,6 +218,26 @@ end Γ(a::FT, z::FT) where {FT <: Real} = FT(SF.gamma(a, z)) Γ(a::FT) where {FT <: Real} = FT(SF.gamma(a)) +""" + integrate(a, b, c1, c2, c3) + + - a - lower bound + - b - upper bound + - c1, c2, c3 - respective constants + + Integrates the function c1 * D ^ (c2) * exp(-c3 * D) dD from a to b + Returns the result +""" +function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where{FT} + if b == Inf + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3)) + elseif a == 0 + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2) - Γ(1 + c2, b * c3)) + else + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3) - Γ(1 + c2, b * c3)) + end +end + """ DSD_μ(p3, λ) @@ -264,25 +284,29 @@ end # q_rim > 0 and D_min = D_gr, D_max = D_cr, ρ = ρ_g function q_s(p3::PSP3, ρ::FT, N_0::FT, λ::FT, D_min::FT, D_max::FT) where {FT} x = DSD_μ(p3, λ) + 4 - return FT(π) / 6 * ρ * N_0 / λ^x * (Γ(x, λ * D_min) - Γ(x, λ * D_max)) + return integrate(D_min, D_max, FT(π) / 6 * ρ * N_0, DSD_μ(p3, λ) + 3, λ) + #return FT(π) / 6 * ρ * N_0 / λ^x * (Γ(x, λ * D_min) - Γ(x, λ * D_max)) end # q_rim = 0 and D_min = D_th, D_max = inf function q_rz(p3::PSP3, N_0::FT, λ::FT, D_min::FT) where {FT} x = DSD_μ(p3, λ) + p3.β_va + 1 - return α_va_si(p3) * N_0 / λ^x * - (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) + return integrate(D_min, FT(Inf), α_va_si(p3) * N_0, DSD_μ(p3, λ) + p3.β_va, λ) + #return α_va_si(p3) * N_0 / λ^x * + # (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) end # q_rim > 0 and D_min = D_th and D_max = D_gr function q_n(p3::PSP3, N_0::FT, λ::FT, D_min::FT, D_max::FT) where {FT} x = DSD_μ(p3, λ) + p3.β_va + 1 - return α_va_si(p3) * N_0 / λ^x * (Γ(x, λ * D_min) - Γ(x, λ * D_max)) + return integrate(D_min, D_max, α_va_si(p3) * N_0, DSD_μ(p3, λ) + p3.β_va, λ) + #return α_va_si(p3) * N_0 / λ^x * (Γ(x, λ * D_min) - Γ(x, λ * D_max)) end # partially rimed ice or large unrimed ice (upper bound on D is infinity) # q_rim > 0 and D_min = D_cr, D_max = inf function q_r(p3::PSP3, F_r::FT, N_0::FT, λ::FT, D_min::FT) where {FT} x = DSD_μ(p3, λ) + p3.β_va + 1 - return α_va_si(p3) * N_0 / (1 - F_r) / λ^x * - (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) + return integrate(D_min, FT(Inf), α_va_si(p3) * N_0 / (1 - F_r), DSD_μ(p3, λ) + p3.β_va, λ) + #return α_va_si(p3) * N_0 / (1 - F_r) / λ^x * + # (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) end """ @@ -408,27 +432,7 @@ function distribution_parameter_solver( return (; λ = exp(x), N_0 = DSD_N₀(p3, N, exp(x))) end -""" - integrate(a, b, c1, c2, c3) - - - a - lower bound - - b - upper bound - - c1, c2, c3 - respective constants - - Integrates the function c1 * D ^ (c2) * exp(-c3 * D) dD from a to b - Returns the result -""" -function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where{FT} - if b == Inf - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3)) - elseif a == 0 - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2) - Γ(1 + c2, b * c3)) - else - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3) - Γ(1 + c2, b * c3)) - end -end - -""" +#= """ """ function terminal_velocity_mass(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where{FT} @@ -471,6 +475,6 @@ end """ function terminal_velocity_number(p3::PSP3, N::FT, F_r::FT, ρ_r::FT) -end +end =# end From 532cedfb6b567c81bfc934d40946124492bb9b9c Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Thu, 22 Feb 2024 13:52:04 -0800 Subject: [PATCH 03/33] moved p3_mass back to the plots --- docs/src/API.md | 1 - docs/src/plots/P3SchemePlots.jl | 72 +++++++++++++++++-- src/P3Scheme.jl | 120 ++++++++++---------------------- test/p3_tests.jl | 67 ------------------ 4 files changed, 103 insertions(+), 157 deletions(-) diff --git a/docs/src/API.md b/docs/src/API.md index 6b7e209f9..dbe116f6a 100644 --- a/docs/src/API.md +++ b/docs/src/API.md @@ -58,7 +58,6 @@ Microphysics2M.conv_q_liq_to_q_rai ```@docs P3Scheme P3Scheme.thresholds -P3Scheme.p3_mass P3Scheme.distribution_parameter_solver ``` diff --git a/docs/src/plots/P3SchemePlots.jl b/docs/src/plots/P3SchemePlots.jl index a904615f6..659284018 100644 --- a/docs/src/plots/P3SchemePlots.jl +++ b/docs/src/plots/P3SchemePlots.jl @@ -9,6 +9,66 @@ FT = Float64 const PSP3 = CMP.ParametersP3 p3 = CMP.ParametersP3(FT) +""" + mass_(p3, D, ρ, F_r) + + - p3 - a struct with P3 scheme parameters + - D - maximum particle dimension [m] + - ρ - bulk ice density (ρ_i for small ice, ρ_g for graupel) [kg/m3] + - F_r - rime mass fraction [q_rim/q_i] + +Returns mass as a function of size for differen particle regimes [kg] +""" +# for spherical ice (small ice or completely rimed ice) +mass_s(D::FT, ρ::FT) where {FT <: Real} = FT(π) / 6 * ρ * D^3 +# for large nonspherical ice (used for unrimed and dense types) +mass_nl(p3::PSP3, D::FT) where {FT <: Real} = P3.α_va_si(p3) * D^p3.β_va +# for partially rimed ice +mass_r(p3::PSP3, D::FT, F_r::FT) where {FT <: Real} = + P3.α_va_si(p3) / (1 - F_r) * D^p3.β_va + +""" + p3_mass(p3, D, F_r, th) + + - p3 - a struct with P3 scheme parameters + - D - maximum particle dimension + - F_r - rime mass fraction (q_rim/q_i) + - th - P3Scheme nonlinear solve output tuple (D_cr, D_gr, ρ_g, ρ_d) + +Returns mass(D) regime, used to create figures for the docs page. +""" +function p3_mass( + p3::PSP3, + D::FT, + F_r::FT, + th = (; D_cr = FT(0), D_gr = FT(0), ρ_g = FT(0), ρ_d = FT(0)), +) where {FT <: Real} + D_th = P3.D_th_helper(p3) + if D_th > D + return mass_s(D, p3.ρ_i) # small spherical ice + end + if F_r == 0 + return mass_nl(p3, D) # large nonspherical unrimed ice + end + if th.D_gr > D >= D_th + return mass_nl(p3, D) # dense nonspherical ice + end + if th.D_cr > D >= th.D_gr + return mass_s(D, th.ρ_g) # graupel + end + if D >= th.D_cr + return mass_r(p3, D, F_r) # partially rimed ice + end + + # TODO - would something like this be better? + #return ifelse(D_th_helper(p3) > D, mass_s(D, p3.ρ_i), + # ifelse(F_r == 0, mass_nl(p3, D), + # ifelse(th.D_gr > D >= D_th_helper(p3), mass_nl(p3, D), + # ifelse(th.D_cr > D >= th.D_gr, mass_s(D, th.ρ_g), + # mass_r(p3, D, F_r))))) + +end + """ A_(p3, D) @@ -88,9 +148,9 @@ function p3_mass_plot() sol_8 = P3.thresholds(p3, 400.0, 0.8) #! format: off - fig1_a_0 = Plt.lines!(ax1_a, D_range * 1e3, [P3.p3_mass(p3, D, 0.0 ) for D in D_range], color = cl[1], linewidth = lw) - fig1_a_5 = Plt.lines!(ax1_a, D_range * 1e3, [P3.p3_mass(p3, D, 0.5, sol_5) for D in D_range], color = cl[2], linewidth = lw) - fig1_a_8 = Plt.lines!(ax1_a, D_range * 1e3, [P3.p3_mass(p3, D, 0.8, sol_8) for D in D_range], color = cl[3], linewidth = lw) + fig1_a_0 = Plt.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.0 ) for D in D_range], color = cl[1], linewidth = lw) + fig1_a_5 = Plt.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.5, sol_5) for D in D_range], color = cl[2], linewidth = lw) + fig1_a_8 = Plt.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.8, sol_8) for D in D_range], color = cl[3], linewidth = lw) d_tha = Plt.vlines!(ax1_a, P3.D_th_helper(p3) * 1e3, linestyle = :dash, color = cl[4], linewidth = lw) d_cr_5 = Plt.vlines!(ax1_a, sol_5[1] * 1e3, linestyle = :dot, color = cl[2], linewidth = lw) @@ -128,9 +188,9 @@ function p3_mass_plot() sol_8 = P3.thresholds(p3, 800.0, 0.95) #! format: off - fig1_b200 = Plt.lines!(ax1_b, D_range * 1e3, [P3.p3_mass(p3, D, 0.95, sol_2) for D in D_range], color = cl[1], linewidth = lw) - fig1_b400 = Plt.lines!(ax1_b, D_range * 1e3, [P3.p3_mass(p3, D, 0.95, sol_4) for D in D_range], color = cl[2], linewidth = lw) - fig1_b800 = Plt.lines!(ax1_b, D_range * 1e3, [P3.p3_mass(p3, D, 0.95, sol_8) for D in D_range], color = cl[3], linewidth = lw) + fig1_b200 = Plt.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_2) for D in D_range], color = cl[1], linewidth = lw) + fig1_b400 = Plt.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_4) for D in D_range], color = cl[2], linewidth = lw) + fig1_b800 = Plt.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_8) for D in D_range], color = cl[3], linewidth = lw) d_thb = Plt.vlines!(ax1_b, P3.D_th_helper(p3) * 1e3, linestyle = :dash, color = cl[4], linewidth = lw) d_cr_200 = Plt.vlines!(ax1_b, sol_2[1] * 1e3, linestyle = :dot, color = cl[1], linewidth = lw) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 5dc16e82f..e0ce4aec4 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -19,7 +19,7 @@ import CloudMicrophysics.Parameters as CMP const PSP3 = CMP.ParametersP3 -export thresholds, p3_mass, distribution_parameter_solver +export thresholds, distribution_parameter_solver """ α_va_si(p3) @@ -154,65 +154,6 @@ function thresholds(p3::PSP3{FT}, ρ_r::FT, F_r::FT) where {FT} end end -""" - mass_(p3, D, ρ, F_r) - - - p3 - a struct with P3 scheme parameters - - D - maximum particle dimension [m] - - ρ - bulk ice density (ρ_i for small ice, ρ_g for graupel) [kg/m3] - - F_r - rime mass fraction [q_rim/q_i] - -Returns mass as a function of size for differen particle regimes [kg] -""" -# for spherical ice (small ice or completely rimed ice) -mass_s(D::FT, ρ::FT) where {FT <: Real} = FT(π) / 6 * ρ * D^3 -# for large nonspherical ice (used for unrimed and dense types) -mass_nl(p3::PSP3, D::FT) where {FT <: Real} = α_va_si(p3) * D^p3.β_va -# for partially rimed ice -mass_r(p3::PSP3, D::FT, F_r::FT) where {FT <: Real} = - α_va_si(p3) / (1 - F_r) * D^p3.β_va - -""" - p3_mass(p3, D, F_r, th) - - - p3 - a struct with P3 scheme parameters - - D - maximum particle dimension - - F_r - rime mass fraction (q_rim/q_i) - - th - P3Scheme nonlinear solve output tuple (D_cr, D_gr, ρ_g, ρ_d) - -Returns mass(D) regime, used to create figures for the docs page. -""" -function p3_mass( - p3::PSP3, - D::FT, - F_r::FT, - th = (; D_cr = FT(0), D_gr = FT(0), ρ_g = FT(0), ρ_d = FT(0)), -) where {FT <: Real} - if D_th_helper(p3) > D - return mass_s(D, p3.ρ_i) # small spherical ice - end - if F_r == 0 - return mass_nl(p3, D) # large nonspherical unrimed ice - end - if th.D_gr > D >= D_th_helper(p3) - return mass_nl(p3, D) # dense nonspherical ice - end - if th.D_cr > D >= th.D_gr - return mass_s(D, th.ρ_g) # graupel - end - if D >= th.D_cr - return mass_r(p3, D, F_r) # partially rimed ice - end - - # TODO - would something like this be better? - #return ifelse(D_th_helper(p3) > D, mass_s(D, p3.ρ_i), - # ifelse(F_r == 0, mass_nl(p3, D), - # ifelse(th.D_gr > D >= D_th_helper(p3), mass_nl(p3, D), - # ifelse(th.D_cr > D >= th.D_gr, mass_s(D, th.ρ_g), - # mass_r(p3, D, F_r))))) - -end - # Some wrappers to cast types from SF.gamma # (which returns Float64 even when the input is Float32) Γ(a::FT, z::FT) where {FT <: Real} = FT(SF.gamma(a, z)) @@ -432,9 +373,18 @@ function distribution_parameter_solver( return (; λ = exp(x), N_0 = DSD_N₀(p3, N, exp(x))) end -#= """ """ -function terminal_velocity_mass(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where{FT} + terminal_velocity_mass(p3, Chen2022, q, N, ρ_r, F_r) + + - p3 - + - Chen 2022 - + - q - + - N - + - ρ_r - + - F_r - + +""" +function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_r::FT, F_r::FT) where{FT} # Get the thresholds for different particles regimes th = thresholds(p3, ρ_r, F_r) @@ -444,37 +394,41 @@ function terminal_velocity_mass(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) μ = DSD_μ(p3, λ) - # Get the ai, bi, ci constants for velocity calculations - + # Get the ai, bi, ci constants (in si units) for velocity calculations + (; As, Bs, Cs, Es, Fs, Gs) = Chen2022.snow_ice + + bi = [Bs + ρ * Cs, Bs + ρ * Cs] + ai = [Es * ρ^As * 10^(3 * bi[1]), Fs * ρ^As * 10^(3 * bi[2])] + ci = [0, Gs * 10^3] # Redefine α_va to be in si units α_va = α_va_si(p3) - # STILL NEED TO BE SUMMED OVER RESPECTIVE ai bi and ci - if F_r == 0 - term1 = integrate(0, D_th, π / 6 * p3.ρ_i * ai * N_0, bi + μ + 3, ci + λ) - term2 = integrate(D_th, Inf, α_va * ai * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci + λ) - else - term1 = integrate(0, D_th, π / 6 * p3.ρ_i * ai * N_0, bi + μ + 3, ci + λ) - term2 = integrate(D_th, th.D_gr, α_va * ai * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci + λ) - term3 = integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * ai * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi + 3 + μ, ci + λ) - term4 = ( - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi + p3.β_va + μ + κ(3 * p3.σ), ci + λ) + - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi + p3.β_va + μ + κ(2 + 2 * p3.σ), ci + λ) + - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi + p3.β_va + μ + κ(4 + p3.σ), ci + λ) + - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi + p3.β_va + μ + κ(6), ci + λ) - ) + v = 0 + for i in 1:2 + if F_r == 0 + v += integrate(0, D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) + v += integrate(D_th, Inf, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + else + v += integrate(0, D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) + v += integrate(D_th, D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + v += integrate(D_gr, D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + 3 + μ, ci[i] + λ) + v += ( + integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi[i] + p3.β_va + μ + κ(3 * p3.σ), ci[i] + λ) + + integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi[i] + p3.β_va + μ + κ(2 + 2 * p3.σ), ci[i] + λ) + + integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi[i] + p3.β_va + μ + κ(4 + p3.σ), ci[i] + λ) + + integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi[i] + p3.β_va + μ + κ(6), ci[i] + λ) + ) + end end - # Get total sums (top) - return top / q - + return v / q end """ """ -function terminal_velocity_number(p3::PSP3, N::FT, F_r::FT, ρ_r::FT) +function terminal_velocity_number(p3::PSP3, N::FT, F_r::FT, ρ_r::FT) where{FT} -end =# +end end diff --git a/test/p3_tests.jl b/test/p3_tests.jl index 36aa1c78e..3d0e5727b 100644 --- a/test/p3_tests.jl +++ b/test/p3_tests.jl @@ -85,71 +85,6 @@ function test_p3_thresholds(FT) end end -function test_p3_mass(FT) - - p3 = CMP.ParametersP3(FT) - - TT.@testset "p3 mass_ functions" begin - - # Initialize test values - Ds = (FT(1e-5), FT(1e-4), FT(1e-3)) - ρs = (FT(400), FT(600), FT(800)) - F_rs = (FT(0.0), FT(0.5), FT(0.8)) - - # Test to see that the mass_ functions give positive, not NaN values - for D in Ds - for ρ in ρs - for F_r in F_rs - TT.@test P3.mass_s(D, ρ) >= 0 - TT.@test P3.mass_nl(p3, D) >= 0 - TT.@test P3.mass_r(p3, D, F_r) >= 0 - end - end - end - end - - # Test to see that p3_mass gives correct mass - TT.@testset "p3_mass() accurate values" begin - - # Initialize test values - Ds = (FT(1e-5), FT(1e-4), FT(1e-3)) - ρs = (FT(400), FT(600), FT(800)) - F_rs = (FT(0.0), FT(0.5), FT(0.8)) - eps = FT(1e-3) #TODO - this is very large for eps - - for ρ in ρs - for F_r in F_rs - D_th = P3.D_th_helper(p3) - D1 = D_th / 2 - th = P3.thresholds(p3, ρ, F_r) - - if (F_r > 0) - th = P3.thresholds(p3, ρ, F_r) - - D2 = (th.D_gr + D_th) / 2 - D3 = (th.D_cr + th.D_gr) / 2 - D4 = th.D_cr + eps - - TT.@test P3.p3_mass(p3, D1, F_r, th) == - P3.mass_s(D1, p3.ρ_i) - TT.@test P3.p3_mass(p3, D2, F_r, th) == P3.mass_nl(p3, D2) - TT.@test P3.p3_mass(p3, D3, F_r, th) == - P3.mass_s(D3, th.ρ_g) - TT.@test P3.p3_mass(p3, D4, F_r, th) == - P3.mass_r(p3, D4, F_r) - else - D2 = D1 + eps - TT.@test P3.p3_mass(p3, D1, F_r, th) == - P3.mass_s(D1, p3.ρ_i) - TT.@test P3.p3_mass(p3, D2, F_r, th) == P3.mass_nl(p3, D2) - end - - end - end - end - -end - function test_p3_shape_solver(FT) p3 = CMP.ParametersP3(FT) @@ -200,12 +135,10 @@ end println("Testing Float32") test_p3_thresholds(Float32) -test_p3_mass(Float32) #TODO - only works for Float64 now. We should switch the units inside the solver # from SI base to something more managable test_p3_shape_solver(Float32) println("Testing Float64") test_p3_thresholds(Float64) -test_p3_mass(Float64) test_p3_shape_solver(Float64) From 3954a6978d36db29fcf8f7144d74dd64ef0d7315 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Fri, 23 Feb 2024 12:27:31 -0800 Subject: [PATCH 04/33] terminal velocities can calculate --- src/P3Scheme.jl | 95 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 19 deletions(-) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index e0ce4aec4..abfcae30c 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -376,15 +376,18 @@ end """ terminal_velocity_mass(p3, Chen2022, q, N, ρ_r, F_r) - - p3 - - - Chen 2022 - - - q - - - N - - - ρ_r - - - F_r - + - p3 - a struct with P3 scheme parameters + - Chen 2022 - a struch with terminal velocity parameters as in Chen(2022) + - q - mass mixing ratio + - N - number mixing ratio + - ρ_r - rime density (q_rim/B_rim) [kg/m^3] + - F_r - rime mass fraction (q_rim/q_i) + - ρ_a - density of air + Returns the mass (total)-weighted fall speed + Eq C10 of Morrison and Milbrandt (2015) """ -function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_r::FT, F_r::FT) where{FT} +function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_r::FT, F_r::FT, ρ_a::FT) where{FT} # Get the thresholds for different particles regimes th = thresholds(p3, ρ_r, F_r) @@ -395,29 +398,32 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, μ = DSD_μ(p3, λ) # Get the ai, bi, ci constants (in si units) for velocity calculations - (; As, Bs, Cs, Es, Fs, Gs) = Chen2022.snow_ice + (; As, Bs, Cs, Es, Fs, Gs) = Chen2022 - bi = [Bs + ρ * Cs, Bs + ρ * Cs] - ai = [Es * ρ^As * 10^(3 * bi[1]), Fs * ρ^As * 10^(3 * bi[2])] + bi = [Bs + ρ_a * Cs, Bs + ρ_a * Cs] + ai = [Es * ρ_a^As * 10^(3 * bi[1]), Fs * ρ_a^As * 10^(3 * bi[2])] ci = [0, Gs * 10^3] + κ = FT(1/3) + # Redefine α_va to be in si units α_va = α_va_si(p3) + # TODO Update the velocity to use a different formulation for D > 0.625 mm v = 0 for i in 1:2 if F_r == 0 - v += integrate(0, D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) + v += integrate(FT(0), D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) v += integrate(D_th, Inf, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) else - v += integrate(0, D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) - v += integrate(D_th, D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) - v += integrate(D_gr, D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + 3 + μ, ci[i] + λ) + v += integrate(FT(0), D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) + v += integrate(D_th, th.D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + v += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + 3 + μ, ci[i] + λ) v += ( - integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi[i] + p3.β_va + μ + κ(3 * p3.σ), ci[i] + λ) + - integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi[i] + p3.β_va + μ + κ(2 + 2 * p3.σ), ci[i] + λ) + - integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi[i] + p3.β_va + μ + κ(4 + p3.σ), ci[i] + λ) + - integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi[i] + p3.β_va + μ + κ(6), ci[i] + λ) + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi[i] + p3.β_va + μ + κ*(3 * p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi[i] + p3.β_va + μ + κ*(2 + 2 * p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi[i] + p3.β_va + μ + κ*(4 + p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi[i] + p3.β_va + μ + 6 * κ, ci[i] + λ) ) end end @@ -426,9 +432,60 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, end """ + terminal_velocity_number(p3, Chen2022, q, N, ρ_r, F_r) + + - p3 - a struct with P3 scheme parameters + - Chen 2022 - a struch with terminal velocity parameters as in Chen(2022) + - q - mass mixing ratio + - N - number mixing ratio + - ρ_r - rime density (q_rim/B_rim) [kg/m^3] + - F_r - rime mass fraction (q_rim/q_i) + - ρ_a - density of air + + Returns the number (total)-weighted fall speed + Eq C11 of Morrison and Milbrandt (2015) """ -function terminal_velocity_number(p3::PSP3, N::FT, F_r::FT, ρ_r::FT) where{FT} +function terminal_velocity_number(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_r::FT, F_r::FT, ρ_a::FT) where{FT} + # Get the thresholds for different particles regimes + th = thresholds(p3, ρ_r, F_r) + D_th = D_th_helper(p3) + + # Get the shape parameters + (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) + μ = DSD_μ(p3, λ) + + # Get the ai, bi, ci constants (in si units) for velocity calculations + (; As, Bs, Cs, Es, Fs, Gs) = Chen2022 + + bi = [Bs + ρ_a * Cs, Bs + ρ_a * Cs] + ai = [Es * ρ_a^As * 10^(3 * bi[1]), Fs * ρ_a^As * 10^(3 * bi[2])] + ci = [0, Gs * 10^3] + + κ = FT(1/3) + + # Redefine α_va to be in si units + α_va = α_va_si(p3) + + # TODO Update the velocity to use a different formulation for D > 0.625 mm + v = 0 + for i in 1:2 + if F_r == 0 + v += integrate(FT(0), D_th, ai[i] * N_0, bi[i] + μ, ci[i] + λ) + v += integrate(D_th, Inf, ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + else + v += integrate(FT(0), D_th, ai[i] * N_0, bi[i] + μ, ci[i] + λ) + v += integrate(D_th, th.D_gr, ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + v += integrate(th.D_gr, th.D_cr, ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ, ci[i] + λ) + v += ( + integrate(th.D_cr, Inf, ai[i] * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi[i] + μ + κ*(3 * p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, ai[i] * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi[i] + μ + κ*(2 + 2 * p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, ai[i] * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi[i] + μ + κ*(4 + p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, ai[i] * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi[i] + μ + 6 * κ, ci[i] + λ) + ) + end + end + return v / N end end From 287f1867f11745f759b5cf109fa41505225e5ba7 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Fri, 23 Feb 2024 13:14:30 -0800 Subject: [PATCH 05/33] added D_m calculation --- src/P3Scheme.jl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index abfcae30c..5c01c3242 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -488,4 +488,44 @@ function terminal_velocity_number(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce return v / N end +""" + D_m (p3, q, N, ρ_r, F_r) + + - p3 - a struct with P3 scheme parameters + - q - mass mixing ratio + - N - number mixing ratio + - ρ_r - rime density (q_rim/B_rim) [kg/m^3] + - F_r - rime mass fraction (q_rim/q_i) + + Return the mass weighted mean particle size [m] +""" +function D_m(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} + # Get the thresholds for different particles regimes + th = thresholds(p3, ρ_r, F_r) + D_th = D_th_helper(p3) + + # Get the shape parameters + (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) + μ = DSD_μ(p3, λ) + + # Redefine α_va to be in si units + α_va = α_va_si(p3) + + # Calculate numerator + n = 0 + if F_r == 0 + n += integrate(FT(0), D_th, π / 6 * p3.ρ_i * N_0, μ + 4, λ) + n += integrate(D_th, Inf, α_va * N_0, μ + p3.β_va + 1, λ) + else + n += integrate(FT(0), D_th, π / 6 * p3.ρ_i * N_0, μ + 4, λ) + n += integrate(D_th, th.D_gr, α_va * N_0, μ + p3.β_va + 1, λ) + n += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * N_0, μ + 4, λ) + n += integrate(th.D_cr, Inf, α_va / (1 - F_r) * N_0, μ + p3.β_va + 1, λ) + end + + # Normalize by q + return n/q + +end + end From 2ab666a220377bb9ebf2d026f7b6839b9c44ea05 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Wed, 21 Feb 2024 15:02:57 -0800 Subject: [PATCH 06/33] terminal velocity start to implement --- src/P3Scheme.jl | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index b682ebfbc..dcea1f34d 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -408,4 +408,69 @@ function distribution_parameter_solver( return (; λ = exp(x), N_0 = DSD_N₀(p3, N, exp(x))) end +""" + integrate(a, b, c1, c2, c3) + + - a - lower bound + - b - upper bound + - c1, c2, c3 - respective constants + + Integrates the function c1 * D ^ (c2) * exp(-c3 * D) dD from a to b + Returns the result +""" +function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where{FT} + if b == Inf + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3)) + elseif a == 0 + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2) - Γ(1 + c2, b * c3)) + else + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3) - Γ(1 + c2, b * c3)) + end +end + +""" +""" +function terminal_velocity_mass(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where{FT} + + # Get the thresholds for different particles regimes + th = thresholds(p3, ρ_r, F_r) + D_th = D_th_helper(p3) + + # Get the shape parameters + (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) + μ = DSD_μ(p3, λ) + + # Get the ai, bi, ci constants for velocity calculations + + + # Redefine α_va to be in si units + α_va = α_va_si(p3) + + # STILL NEED TO BE SUMMED OVER RESPECTIVE ai bi and ci + if F_r == 0 + term1 = integrate(0, D_th, π / 6 * p3.ρ_i * ai * N_0, bi + μ + 3, ci + λ) + term2 = integrate(D_th, Inf, α_va * ai * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci + λ) + else + term1 = integrate(0, D_th, π / 6 * p3.ρ_i * ai * N_0, bi + μ + 3, ci + λ) + term2 = integrate(D_th, th.D_gr, α_va * ai * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci + λ) + term3 = integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * ai * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi + 3 + μ, ci + λ) + term4 = ( + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi + p3.β_va + μ + κ(3 * p3.σ), ci + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi + p3.β_va + μ + κ(2 + 2 * p3.σ), ci + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi + p3.β_va + μ + κ(4 + p3.σ), ci + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi + p3.β_va + μ + κ(6), ci + λ) + ) + end + + # Get total sums (top) + return top / q + +end + +""" +""" +function terminal_velocity_number(p3::PSP3, N::FT, F_r::FT, ρ_r::FT) + +end + end From 788e7b84c84f9dfd351909299f6208794d9c31ef Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Thu, 22 Feb 2024 13:43:10 -0800 Subject: [PATCH 07/33] Update P3Scheme to use integrate() --- src/P3Scheme.jl | 60 ++++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index dcea1f34d..5dc16e82f 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -218,6 +218,26 @@ end Γ(a::FT, z::FT) where {FT <: Real} = FT(SF.gamma(a, z)) Γ(a::FT) where {FT <: Real} = FT(SF.gamma(a)) +""" + integrate(a, b, c1, c2, c3) + + - a - lower bound + - b - upper bound + - c1, c2, c3 - respective constants + + Integrates the function c1 * D ^ (c2) * exp(-c3 * D) dD from a to b + Returns the result +""" +function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where{FT} + if b == Inf + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3)) + elseif a == 0 + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2) - Γ(1 + c2, b * c3)) + else + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3) - Γ(1 + c2, b * c3)) + end +end + """ DSD_μ(p3, λ) @@ -264,25 +284,29 @@ end # q_rim > 0 and D_min = D_gr, D_max = D_cr, ρ = ρ_g function q_s(p3::PSP3, ρ::FT, N_0::FT, λ::FT, D_min::FT, D_max::FT) where {FT} x = DSD_μ(p3, λ) + 4 - return FT(π) / 6 * ρ * N_0 / λ^x * (Γ(x, λ * D_min) - Γ(x, λ * D_max)) + return integrate(D_min, D_max, FT(π) / 6 * ρ * N_0, DSD_μ(p3, λ) + 3, λ) + #return FT(π) / 6 * ρ * N_0 / λ^x * (Γ(x, λ * D_min) - Γ(x, λ * D_max)) end # q_rim = 0 and D_min = D_th, D_max = inf function q_rz(p3::PSP3, N_0::FT, λ::FT, D_min::FT) where {FT} x = DSD_μ(p3, λ) + p3.β_va + 1 - return α_va_si(p3) * N_0 / λ^x * - (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) + return integrate(D_min, FT(Inf), α_va_si(p3) * N_0, DSD_μ(p3, λ) + p3.β_va, λ) + #return α_va_si(p3) * N_0 / λ^x * + # (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) end # q_rim > 0 and D_min = D_th and D_max = D_gr function q_n(p3::PSP3, N_0::FT, λ::FT, D_min::FT, D_max::FT) where {FT} x = DSD_μ(p3, λ) + p3.β_va + 1 - return α_va_si(p3) * N_0 / λ^x * (Γ(x, λ * D_min) - Γ(x, λ * D_max)) + return integrate(D_min, D_max, α_va_si(p3) * N_0, DSD_μ(p3, λ) + p3.β_va, λ) + #return α_va_si(p3) * N_0 / λ^x * (Γ(x, λ * D_min) - Γ(x, λ * D_max)) end # partially rimed ice or large unrimed ice (upper bound on D is infinity) # q_rim > 0 and D_min = D_cr, D_max = inf function q_r(p3::PSP3, F_r::FT, N_0::FT, λ::FT, D_min::FT) where {FT} x = DSD_μ(p3, λ) + p3.β_va + 1 - return α_va_si(p3) * N_0 / (1 - F_r) / λ^x * - (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) + return integrate(D_min, FT(Inf), α_va_si(p3) * N_0 / (1 - F_r), DSD_μ(p3, λ) + p3.β_va, λ) + #return α_va_si(p3) * N_0 / (1 - F_r) / λ^x * + # (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) end """ @@ -408,27 +432,7 @@ function distribution_parameter_solver( return (; λ = exp(x), N_0 = DSD_N₀(p3, N, exp(x))) end -""" - integrate(a, b, c1, c2, c3) - - - a - lower bound - - b - upper bound - - c1, c2, c3 - respective constants - - Integrates the function c1 * D ^ (c2) * exp(-c3 * D) dD from a to b - Returns the result -""" -function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where{FT} - if b == Inf - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3)) - elseif a == 0 - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2) - Γ(1 + c2, b * c3)) - else - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3) - Γ(1 + c2, b * c3)) - end -end - -""" +#= """ """ function terminal_velocity_mass(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where{FT} @@ -471,6 +475,6 @@ end """ function terminal_velocity_number(p3::PSP3, N::FT, F_r::FT, ρ_r::FT) -end +end =# end From d075c57dd2f9dc74246de73081ef41353ddd4b36 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Thu, 22 Feb 2024 13:52:04 -0800 Subject: [PATCH 08/33] moved p3_mass back to the plots --- docs/src/API.md | 1 - docs/src/plots/P3SchemePlots.jl | 72 +++++++++++++++++-- src/P3Scheme.jl | 120 ++++++++++---------------------- test/p3_tests.jl | 67 ------------------ 4 files changed, 103 insertions(+), 157 deletions(-) diff --git a/docs/src/API.md b/docs/src/API.md index 6b7e209f9..dbe116f6a 100644 --- a/docs/src/API.md +++ b/docs/src/API.md @@ -58,7 +58,6 @@ Microphysics2M.conv_q_liq_to_q_rai ```@docs P3Scheme P3Scheme.thresholds -P3Scheme.p3_mass P3Scheme.distribution_parameter_solver ``` diff --git a/docs/src/plots/P3SchemePlots.jl b/docs/src/plots/P3SchemePlots.jl index a904615f6..659284018 100644 --- a/docs/src/plots/P3SchemePlots.jl +++ b/docs/src/plots/P3SchemePlots.jl @@ -9,6 +9,66 @@ FT = Float64 const PSP3 = CMP.ParametersP3 p3 = CMP.ParametersP3(FT) +""" + mass_(p3, D, ρ, F_r) + + - p3 - a struct with P3 scheme parameters + - D - maximum particle dimension [m] + - ρ - bulk ice density (ρ_i for small ice, ρ_g for graupel) [kg/m3] + - F_r - rime mass fraction [q_rim/q_i] + +Returns mass as a function of size for differen particle regimes [kg] +""" +# for spherical ice (small ice or completely rimed ice) +mass_s(D::FT, ρ::FT) where {FT <: Real} = FT(π) / 6 * ρ * D^3 +# for large nonspherical ice (used for unrimed and dense types) +mass_nl(p3::PSP3, D::FT) where {FT <: Real} = P3.α_va_si(p3) * D^p3.β_va +# for partially rimed ice +mass_r(p3::PSP3, D::FT, F_r::FT) where {FT <: Real} = + P3.α_va_si(p3) / (1 - F_r) * D^p3.β_va + +""" + p3_mass(p3, D, F_r, th) + + - p3 - a struct with P3 scheme parameters + - D - maximum particle dimension + - F_r - rime mass fraction (q_rim/q_i) + - th - P3Scheme nonlinear solve output tuple (D_cr, D_gr, ρ_g, ρ_d) + +Returns mass(D) regime, used to create figures for the docs page. +""" +function p3_mass( + p3::PSP3, + D::FT, + F_r::FT, + th = (; D_cr = FT(0), D_gr = FT(0), ρ_g = FT(0), ρ_d = FT(0)), +) where {FT <: Real} + D_th = P3.D_th_helper(p3) + if D_th > D + return mass_s(D, p3.ρ_i) # small spherical ice + end + if F_r == 0 + return mass_nl(p3, D) # large nonspherical unrimed ice + end + if th.D_gr > D >= D_th + return mass_nl(p3, D) # dense nonspherical ice + end + if th.D_cr > D >= th.D_gr + return mass_s(D, th.ρ_g) # graupel + end + if D >= th.D_cr + return mass_r(p3, D, F_r) # partially rimed ice + end + + # TODO - would something like this be better? + #return ifelse(D_th_helper(p3) > D, mass_s(D, p3.ρ_i), + # ifelse(F_r == 0, mass_nl(p3, D), + # ifelse(th.D_gr > D >= D_th_helper(p3), mass_nl(p3, D), + # ifelse(th.D_cr > D >= th.D_gr, mass_s(D, th.ρ_g), + # mass_r(p3, D, F_r))))) + +end + """ A_(p3, D) @@ -88,9 +148,9 @@ function p3_mass_plot() sol_8 = P3.thresholds(p3, 400.0, 0.8) #! format: off - fig1_a_0 = Plt.lines!(ax1_a, D_range * 1e3, [P3.p3_mass(p3, D, 0.0 ) for D in D_range], color = cl[1], linewidth = lw) - fig1_a_5 = Plt.lines!(ax1_a, D_range * 1e3, [P3.p3_mass(p3, D, 0.5, sol_5) for D in D_range], color = cl[2], linewidth = lw) - fig1_a_8 = Plt.lines!(ax1_a, D_range * 1e3, [P3.p3_mass(p3, D, 0.8, sol_8) for D in D_range], color = cl[3], linewidth = lw) + fig1_a_0 = Plt.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.0 ) for D in D_range], color = cl[1], linewidth = lw) + fig1_a_5 = Plt.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.5, sol_5) for D in D_range], color = cl[2], linewidth = lw) + fig1_a_8 = Plt.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.8, sol_8) for D in D_range], color = cl[3], linewidth = lw) d_tha = Plt.vlines!(ax1_a, P3.D_th_helper(p3) * 1e3, linestyle = :dash, color = cl[4], linewidth = lw) d_cr_5 = Plt.vlines!(ax1_a, sol_5[1] * 1e3, linestyle = :dot, color = cl[2], linewidth = lw) @@ -128,9 +188,9 @@ function p3_mass_plot() sol_8 = P3.thresholds(p3, 800.0, 0.95) #! format: off - fig1_b200 = Plt.lines!(ax1_b, D_range * 1e3, [P3.p3_mass(p3, D, 0.95, sol_2) for D in D_range], color = cl[1], linewidth = lw) - fig1_b400 = Plt.lines!(ax1_b, D_range * 1e3, [P3.p3_mass(p3, D, 0.95, sol_4) for D in D_range], color = cl[2], linewidth = lw) - fig1_b800 = Plt.lines!(ax1_b, D_range * 1e3, [P3.p3_mass(p3, D, 0.95, sol_8) for D in D_range], color = cl[3], linewidth = lw) + fig1_b200 = Plt.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_2) for D in D_range], color = cl[1], linewidth = lw) + fig1_b400 = Plt.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_4) for D in D_range], color = cl[2], linewidth = lw) + fig1_b800 = Plt.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_8) for D in D_range], color = cl[3], linewidth = lw) d_thb = Plt.vlines!(ax1_b, P3.D_th_helper(p3) * 1e3, linestyle = :dash, color = cl[4], linewidth = lw) d_cr_200 = Plt.vlines!(ax1_b, sol_2[1] * 1e3, linestyle = :dot, color = cl[1], linewidth = lw) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 5dc16e82f..e0ce4aec4 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -19,7 +19,7 @@ import CloudMicrophysics.Parameters as CMP const PSP3 = CMP.ParametersP3 -export thresholds, p3_mass, distribution_parameter_solver +export thresholds, distribution_parameter_solver """ α_va_si(p3) @@ -154,65 +154,6 @@ function thresholds(p3::PSP3{FT}, ρ_r::FT, F_r::FT) where {FT} end end -""" - mass_(p3, D, ρ, F_r) - - - p3 - a struct with P3 scheme parameters - - D - maximum particle dimension [m] - - ρ - bulk ice density (ρ_i for small ice, ρ_g for graupel) [kg/m3] - - F_r - rime mass fraction [q_rim/q_i] - -Returns mass as a function of size for differen particle regimes [kg] -""" -# for spherical ice (small ice or completely rimed ice) -mass_s(D::FT, ρ::FT) where {FT <: Real} = FT(π) / 6 * ρ * D^3 -# for large nonspherical ice (used for unrimed and dense types) -mass_nl(p3::PSP3, D::FT) where {FT <: Real} = α_va_si(p3) * D^p3.β_va -# for partially rimed ice -mass_r(p3::PSP3, D::FT, F_r::FT) where {FT <: Real} = - α_va_si(p3) / (1 - F_r) * D^p3.β_va - -""" - p3_mass(p3, D, F_r, th) - - - p3 - a struct with P3 scheme parameters - - D - maximum particle dimension - - F_r - rime mass fraction (q_rim/q_i) - - th - P3Scheme nonlinear solve output tuple (D_cr, D_gr, ρ_g, ρ_d) - -Returns mass(D) regime, used to create figures for the docs page. -""" -function p3_mass( - p3::PSP3, - D::FT, - F_r::FT, - th = (; D_cr = FT(0), D_gr = FT(0), ρ_g = FT(0), ρ_d = FT(0)), -) where {FT <: Real} - if D_th_helper(p3) > D - return mass_s(D, p3.ρ_i) # small spherical ice - end - if F_r == 0 - return mass_nl(p3, D) # large nonspherical unrimed ice - end - if th.D_gr > D >= D_th_helper(p3) - return mass_nl(p3, D) # dense nonspherical ice - end - if th.D_cr > D >= th.D_gr - return mass_s(D, th.ρ_g) # graupel - end - if D >= th.D_cr - return mass_r(p3, D, F_r) # partially rimed ice - end - - # TODO - would something like this be better? - #return ifelse(D_th_helper(p3) > D, mass_s(D, p3.ρ_i), - # ifelse(F_r == 0, mass_nl(p3, D), - # ifelse(th.D_gr > D >= D_th_helper(p3), mass_nl(p3, D), - # ifelse(th.D_cr > D >= th.D_gr, mass_s(D, th.ρ_g), - # mass_r(p3, D, F_r))))) - -end - # Some wrappers to cast types from SF.gamma # (which returns Float64 even when the input is Float32) Γ(a::FT, z::FT) where {FT <: Real} = FT(SF.gamma(a, z)) @@ -432,9 +373,18 @@ function distribution_parameter_solver( return (; λ = exp(x), N_0 = DSD_N₀(p3, N, exp(x))) end -#= """ """ -function terminal_velocity_mass(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where{FT} + terminal_velocity_mass(p3, Chen2022, q, N, ρ_r, F_r) + + - p3 - + - Chen 2022 - + - q - + - N - + - ρ_r - + - F_r - + +""" +function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_r::FT, F_r::FT) where{FT} # Get the thresholds for different particles regimes th = thresholds(p3, ρ_r, F_r) @@ -444,37 +394,41 @@ function terminal_velocity_mass(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) μ = DSD_μ(p3, λ) - # Get the ai, bi, ci constants for velocity calculations - + # Get the ai, bi, ci constants (in si units) for velocity calculations + (; As, Bs, Cs, Es, Fs, Gs) = Chen2022.snow_ice + + bi = [Bs + ρ * Cs, Bs + ρ * Cs] + ai = [Es * ρ^As * 10^(3 * bi[1]), Fs * ρ^As * 10^(3 * bi[2])] + ci = [0, Gs * 10^3] # Redefine α_va to be in si units α_va = α_va_si(p3) - # STILL NEED TO BE SUMMED OVER RESPECTIVE ai bi and ci - if F_r == 0 - term1 = integrate(0, D_th, π / 6 * p3.ρ_i * ai * N_0, bi + μ + 3, ci + λ) - term2 = integrate(D_th, Inf, α_va * ai * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci + λ) - else - term1 = integrate(0, D_th, π / 6 * p3.ρ_i * ai * N_0, bi + μ + 3, ci + λ) - term2 = integrate(D_th, th.D_gr, α_va * ai * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci + λ) - term3 = integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * ai * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi + 3 + μ, ci + λ) - term4 = ( - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi + p3.β_va + μ + κ(3 * p3.σ), ci + λ) + - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi + p3.β_va + μ + κ(2 + 2 * p3.σ), ci + λ) + - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi + p3.β_va + μ + κ(4 + p3.σ), ci + λ) + - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi + p3.β_va + μ + κ(6), ci + λ) - ) + v = 0 + for i in 1:2 + if F_r == 0 + v += integrate(0, D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) + v += integrate(D_th, Inf, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + else + v += integrate(0, D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) + v += integrate(D_th, D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + v += integrate(D_gr, D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + 3 + μ, ci[i] + λ) + v += ( + integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi[i] + p3.β_va + μ + κ(3 * p3.σ), ci[i] + λ) + + integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi[i] + p3.β_va + μ + κ(2 + 2 * p3.σ), ci[i] + λ) + + integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi[i] + p3.β_va + μ + κ(4 + p3.σ), ci[i] + λ) + + integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi[i] + p3.β_va + μ + κ(6), ci[i] + λ) + ) + end end - # Get total sums (top) - return top / q - + return v / q end """ """ -function terminal_velocity_number(p3::PSP3, N::FT, F_r::FT, ρ_r::FT) +function terminal_velocity_number(p3::PSP3, N::FT, F_r::FT, ρ_r::FT) where{FT} -end =# +end end diff --git a/test/p3_tests.jl b/test/p3_tests.jl index 36aa1c78e..3d0e5727b 100644 --- a/test/p3_tests.jl +++ b/test/p3_tests.jl @@ -85,71 +85,6 @@ function test_p3_thresholds(FT) end end -function test_p3_mass(FT) - - p3 = CMP.ParametersP3(FT) - - TT.@testset "p3 mass_ functions" begin - - # Initialize test values - Ds = (FT(1e-5), FT(1e-4), FT(1e-3)) - ρs = (FT(400), FT(600), FT(800)) - F_rs = (FT(0.0), FT(0.5), FT(0.8)) - - # Test to see that the mass_ functions give positive, not NaN values - for D in Ds - for ρ in ρs - for F_r in F_rs - TT.@test P3.mass_s(D, ρ) >= 0 - TT.@test P3.mass_nl(p3, D) >= 0 - TT.@test P3.mass_r(p3, D, F_r) >= 0 - end - end - end - end - - # Test to see that p3_mass gives correct mass - TT.@testset "p3_mass() accurate values" begin - - # Initialize test values - Ds = (FT(1e-5), FT(1e-4), FT(1e-3)) - ρs = (FT(400), FT(600), FT(800)) - F_rs = (FT(0.0), FT(0.5), FT(0.8)) - eps = FT(1e-3) #TODO - this is very large for eps - - for ρ in ρs - for F_r in F_rs - D_th = P3.D_th_helper(p3) - D1 = D_th / 2 - th = P3.thresholds(p3, ρ, F_r) - - if (F_r > 0) - th = P3.thresholds(p3, ρ, F_r) - - D2 = (th.D_gr + D_th) / 2 - D3 = (th.D_cr + th.D_gr) / 2 - D4 = th.D_cr + eps - - TT.@test P3.p3_mass(p3, D1, F_r, th) == - P3.mass_s(D1, p3.ρ_i) - TT.@test P3.p3_mass(p3, D2, F_r, th) == P3.mass_nl(p3, D2) - TT.@test P3.p3_mass(p3, D3, F_r, th) == - P3.mass_s(D3, th.ρ_g) - TT.@test P3.p3_mass(p3, D4, F_r, th) == - P3.mass_r(p3, D4, F_r) - else - D2 = D1 + eps - TT.@test P3.p3_mass(p3, D1, F_r, th) == - P3.mass_s(D1, p3.ρ_i) - TT.@test P3.p3_mass(p3, D2, F_r, th) == P3.mass_nl(p3, D2) - end - - end - end - end - -end - function test_p3_shape_solver(FT) p3 = CMP.ParametersP3(FT) @@ -200,12 +135,10 @@ end println("Testing Float32") test_p3_thresholds(Float32) -test_p3_mass(Float32) #TODO - only works for Float64 now. We should switch the units inside the solver # from SI base to something more managable test_p3_shape_solver(Float32) println("Testing Float64") test_p3_thresholds(Float64) -test_p3_mass(Float64) test_p3_shape_solver(Float64) From 8947cc04b58b766341dd969d9a1535f046a57e38 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Fri, 23 Feb 2024 12:27:31 -0800 Subject: [PATCH 09/33] terminal velocities can calculate --- src/P3Scheme.jl | 95 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 76 insertions(+), 19 deletions(-) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index e0ce4aec4..abfcae30c 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -376,15 +376,18 @@ end """ terminal_velocity_mass(p3, Chen2022, q, N, ρ_r, F_r) - - p3 - - - Chen 2022 - - - q - - - N - - - ρ_r - - - F_r - + - p3 - a struct with P3 scheme parameters + - Chen 2022 - a struch with terminal velocity parameters as in Chen(2022) + - q - mass mixing ratio + - N - number mixing ratio + - ρ_r - rime density (q_rim/B_rim) [kg/m^3] + - F_r - rime mass fraction (q_rim/q_i) + - ρ_a - density of air + Returns the mass (total)-weighted fall speed + Eq C10 of Morrison and Milbrandt (2015) """ -function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_r::FT, F_r::FT) where{FT} +function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_r::FT, F_r::FT, ρ_a::FT) where{FT} # Get the thresholds for different particles regimes th = thresholds(p3, ρ_r, F_r) @@ -395,29 +398,32 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, μ = DSD_μ(p3, λ) # Get the ai, bi, ci constants (in si units) for velocity calculations - (; As, Bs, Cs, Es, Fs, Gs) = Chen2022.snow_ice + (; As, Bs, Cs, Es, Fs, Gs) = Chen2022 - bi = [Bs + ρ * Cs, Bs + ρ * Cs] - ai = [Es * ρ^As * 10^(3 * bi[1]), Fs * ρ^As * 10^(3 * bi[2])] + bi = [Bs + ρ_a * Cs, Bs + ρ_a * Cs] + ai = [Es * ρ_a^As * 10^(3 * bi[1]), Fs * ρ_a^As * 10^(3 * bi[2])] ci = [0, Gs * 10^3] + κ = FT(1/3) + # Redefine α_va to be in si units α_va = α_va_si(p3) + # TODO Update the velocity to use a different formulation for D > 0.625 mm v = 0 for i in 1:2 if F_r == 0 - v += integrate(0, D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) + v += integrate(FT(0), D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) v += integrate(D_th, Inf, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) else - v += integrate(0, D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) - v += integrate(D_th, D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) - v += integrate(D_gr, D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + 3 + μ, ci[i] + λ) + v += integrate(FT(0), D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) + v += integrate(D_th, th.D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + v += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + 3 + μ, ci[i] + λ) v += ( - integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi[i] + p3.β_va + μ + κ(3 * p3.σ), ci[i] + λ) + - integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi[i] + p3.β_va + μ + κ(2 + 2 * p3.σ), ci[i] + λ) + - integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi[i] + p3.β_va + μ + κ(4 + p3.σ), ci[i] + λ) + - integrate(D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi[i] + p3.β_va + μ + κ(6), ci[i] + λ) + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi[i] + p3.β_va + μ + κ*(3 * p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi[i] + p3.β_va + μ + κ*(2 + 2 * p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi[i] + p3.β_va + μ + κ*(4 + p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi[i] + p3.β_va + μ + 6 * κ, ci[i] + λ) ) end end @@ -426,9 +432,60 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, end """ + terminal_velocity_number(p3, Chen2022, q, N, ρ_r, F_r) + + - p3 - a struct with P3 scheme parameters + - Chen 2022 - a struch with terminal velocity parameters as in Chen(2022) + - q - mass mixing ratio + - N - number mixing ratio + - ρ_r - rime density (q_rim/B_rim) [kg/m^3] + - F_r - rime mass fraction (q_rim/q_i) + - ρ_a - density of air + + Returns the number (total)-weighted fall speed + Eq C11 of Morrison and Milbrandt (2015) """ -function terminal_velocity_number(p3::PSP3, N::FT, F_r::FT, ρ_r::FT) where{FT} +function terminal_velocity_number(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_r::FT, F_r::FT, ρ_a::FT) where{FT} + # Get the thresholds for different particles regimes + th = thresholds(p3, ρ_r, F_r) + D_th = D_th_helper(p3) + + # Get the shape parameters + (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) + μ = DSD_μ(p3, λ) + + # Get the ai, bi, ci constants (in si units) for velocity calculations + (; As, Bs, Cs, Es, Fs, Gs) = Chen2022 + + bi = [Bs + ρ_a * Cs, Bs + ρ_a * Cs] + ai = [Es * ρ_a^As * 10^(3 * bi[1]), Fs * ρ_a^As * 10^(3 * bi[2])] + ci = [0, Gs * 10^3] + + κ = FT(1/3) + + # Redefine α_va to be in si units + α_va = α_va_si(p3) + + # TODO Update the velocity to use a different formulation for D > 0.625 mm + v = 0 + for i in 1:2 + if F_r == 0 + v += integrate(FT(0), D_th, ai[i] * N_0, bi[i] + μ, ci[i] + λ) + v += integrate(D_th, Inf, ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + else + v += integrate(FT(0), D_th, ai[i] * N_0, bi[i] + μ, ci[i] + λ) + v += integrate(D_th, th.D_gr, ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + v += integrate(th.D_gr, th.D_cr, ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ, ci[i] + λ) + v += ( + integrate(th.D_cr, Inf, ai[i] * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi[i] + μ + κ*(3 * p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, ai[i] * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi[i] + μ + κ*(2 + 2 * p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, ai[i] * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi[i] + μ + κ*(4 + p3.σ), ci[i] + λ) + + integrate(th.D_cr, Inf, ai[i] * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi[i] + μ + 6 * κ, ci[i] + λ) + ) + end + end + return v / N end end From a9d97aaee0149723985360d9b240015d39fa38aa Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Fri, 23 Feb 2024 13:14:30 -0800 Subject: [PATCH 10/33] added D_m calculation --- src/P3Scheme.jl | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index abfcae30c..5c01c3242 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -488,4 +488,44 @@ function terminal_velocity_number(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce return v / N end +""" + D_m (p3, q, N, ρ_r, F_r) + + - p3 - a struct with P3 scheme parameters + - q - mass mixing ratio + - N - number mixing ratio + - ρ_r - rime density (q_rim/B_rim) [kg/m^3] + - F_r - rime mass fraction (q_rim/q_i) + + Return the mass weighted mean particle size [m] +""" +function D_m(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} + # Get the thresholds for different particles regimes + th = thresholds(p3, ρ_r, F_r) + D_th = D_th_helper(p3) + + # Get the shape parameters + (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) + μ = DSD_μ(p3, λ) + + # Redefine α_va to be in si units + α_va = α_va_si(p3) + + # Calculate numerator + n = 0 + if F_r == 0 + n += integrate(FT(0), D_th, π / 6 * p3.ρ_i * N_0, μ + 4, λ) + n += integrate(D_th, Inf, α_va * N_0, μ + p3.β_va + 1, λ) + else + n += integrate(FT(0), D_th, π / 6 * p3.ρ_i * N_0, μ + 4, λ) + n += integrate(D_th, th.D_gr, α_va * N_0, μ + p3.β_va + 1, λ) + n += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * N_0, μ + 4, λ) + n += integrate(th.D_cr, Inf, α_va / (1 - F_r) * N_0, μ + p3.β_va + 1, λ) + end + + # Normalize by q + return n/q + +end + end From eb99cf6b529ce69beed8062132b94ca7500163ed Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Fri, 23 Feb 2024 15:00:56 -0800 Subject: [PATCH 11/33] Terminal Velocity plots started --- docs/src/plots/P3TerminalVelocityPlots.jl | 97 +++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 docs/src/plots/P3TerminalVelocityPlots.jl diff --git a/docs/src/plots/P3TerminalVelocityPlots.jl b/docs/src/plots/P3TerminalVelocityPlots.jl new file mode 100644 index 000000000..5608ff187 --- /dev/null +++ b/docs/src/plots/P3TerminalVelocityPlots.jl @@ -0,0 +1,97 @@ +import CLIMAParameters +import CloudMicrophysics as CM +import CloudMicrophysics.P3Scheme as P3 +import CloudMicrophysics.Parameters as CMP +import CairoMakie as Plt + +const PSP3 = CMP.ParametersP3 + +FT = Float64 + +function get_values(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_a::FT, x_resolution::Int, y_resolution::Int) where {FT} + F_rs = range(FT(0), stop = FT(1 - eps(FT)), length = x_resolution) + ρ_rs = range(FT(25), stop = FT(975), length = y_resolution) + + V_m = zeros(x_resolution, y_resolution) + D_m = zeros(x_resolution, y_resolution) + + for i in 1:x_resolution + for j in 1:y_resolution + F_r = F_rs[i] + ρ_r = ρ_rs[j] + + V_m[i, j] = P3.terminal_velocity_mass(p3, Chen2022, q, N, ρ_r, F_r, ρ_a) + D_m[i, j] = P3.D_m(p3, q, N, ρ_r, F_r) + end + end + + minV = minimum(v for v in V_m if !isnan(v)) + maxV = maximum(v for v in V_m if !isnan(v)) + + return (; F_rs, ρ_rs, V_m, D_m, minV, maxV) +end + +function figure_2() + p3 = CMP.ParametersP3(FT) + Chen2022 = CMP.Chen2022VelType(FT) + # density of air in kg/m^3 + ρ_a = FT(1.293) + + f = Plt.Figure() + + # small D_m + q_s = FT(0.001) + N_s = FT(1e6) + + Plt.Axis( + f[1, 1], + xlabel = "F_r", + ylabel = "ρ_r", + title = "Small Dₘ", + width = 300, + height = 300, + limits = (0, 1.0, 25, 975), + xticks = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0], + yticks = [200, 400, 600, 800], + ) + + (F_rs, ρ_rs, V_m, D_m, minV, maxV) = get_values( + p3, + Chen2022.snow_ice, + q_s, + N_s, + ρ_a, + 50, + 50) + + println(D_m[1:10]) + + Plt.heatmap!( + F_rs, + ρ_rs, + V_m, + colormap = Plt.reverse(Plt.cgrad(:Spectral)) + ) + Plt.contour!( + F_rs, + ρ_rs, + D_m, + color = :black, + labels = true, + levels = 3 + ) + Plt.Colorbar( + f[1, 2], + limits = (minV, maxV), + colormap = Plt.reverse(Plt.cgrad(:Spectral)), + flipaxis = true, + ) + + + Plt.resize_to_layout!(f) + Plt.save("MorrisonandMilbrandtFig2.svg", f) +end + +println("start") +figure_2() +println("done") \ No newline at end of file From 703c60d8dbe86bb463582c1f62850ce5901e6d6d Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Tue, 27 Feb 2024 13:00:52 -0800 Subject: [PATCH 12/33] terminal velocity edits --- docs/src/plots/P3TerminalVelocityPlots.jl | 211 ++++++++++++++++++---- src/P3Scheme.jl | 32 +++- 2 files changed, 205 insertions(+), 38 deletions(-) diff --git a/docs/src/plots/P3TerminalVelocityPlots.jl b/docs/src/plots/P3TerminalVelocityPlots.jl index 5608ff187..0e1a57a83 100644 --- a/docs/src/plots/P3TerminalVelocityPlots.jl +++ b/docs/src/plots/P3TerminalVelocityPlots.jl @@ -20,15 +20,12 @@ function get_values(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT F_r = F_rs[i] ρ_r = ρ_rs[j] - V_m[i, j] = P3.terminal_velocity_mass(p3, Chen2022, q, N, ρ_r, F_r, ρ_a) - D_m[i, j] = P3.D_m(p3, q, N, ρ_r, F_r) + V_m[i, j] = P3.terminal_velocity_mass(p3, Chen2022, q, N, ρ_r, F_r, ρ_a) + # get D_m in mm for plots + D_m[i, j] = 1e3 * P3.D_m(p3, q, N, ρ_r, F_r) end end - - minV = minimum(v for v in V_m if !isnan(v)) - maxV = maximum(v for v in V_m if !isnan(v)) - - return (; F_rs, ρ_rs, V_m, D_m, minV, maxV) + return (; F_rs, ρ_rs, V_m, D_m) end function figure_2() @@ -38,60 +35,204 @@ function figure_2() ρ_a = FT(1.293) f = Plt.Figure() + xres = 10 + yres = 10 + min = FT(0) + max = FT(10) # small D_m - q_s = FT(0.001) - N_s = FT(1e6) + q_s = FT(0.00074047) + N_s = FT(1e6) + + # medium D_m + q_m = FT(0.0028151) + N_m = FT(1e6) + + # large D_m + q_l = FT(0.02494021) + N_l = FT(1e6) + # get V_m and D_m + (F_rs, ρ_rs, V_ms, D_ms) = get_values(p3, Chen2022.snow_ice, q_s, N_s, ρ_a, xres, yres) + (F_rm, ρ_rm, V_mm, D_mm) = get_values(p3, Chen2022.snow_ice, q_m, N_m, ρ_a, xres, yres) + (F_rl, ρ_rl, V_ml, D_ml) = get_values(p3, Chen2022.snow_ice, q_l, N_l, ρ_a, xres, yres) + + println("small: min: ", minimum(d for d in D_ms if !isnan(d))," max: ", maximum(d for d in D_ms if !isnan(d))) + println("medium: min: ", minimum(d for d in D_mm if !isnan(d))," max: ", maximum(d for d in D_mm if !isnan(d))) + println("large: min: ", minimum(d for d in D_ml if !isnan(d))," max: ", maximum(d for d in D_ml if !isnan(d))) + + #println(D_mm) + + println("") + #println(D_ml) + Plt.Axis( f[1, 1], xlabel = "F_r", ylabel = "ρ_r", title = "Small Dₘ", - width = 300, - height = 300, + width = 350, + height = 350, + limits = (0, 1.0, 25, 975), + xticks = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0], + yticks = [200, 400, 600, 800], + ) + + Plt.heatmap!(F_rs, ρ_rs, V_ms, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max),) + Plt.contour!(F_rs, ρ_rs, D_ms, color = :black, labels = true, levels = 3,) + + Plt.Axis( + f[1, 2], + xlabel = "F_r", + ylabel = "ρ_r", + title = "Medium Dₘ", + width = 350, + height = 350, + limits = (0, 1.0, 25, 975), + xticks = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0], + yticks = [200, 400, 600, 800], + ) + + Plt.heatmap!(F_rm, ρ_rm, V_mm, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max),) + Plt.contour!(F_rm, ρ_rm, D_mm, color = :black, labels = true, levels = 3) + + Plt.Axis( + f[1, 3], + xlabel = "F_r", + ylabel = "ρ_r", + title = "Large Dₘ", + width = 350, + height = 350, limits = (0, 1.0, 25, 975), xticks = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0], yticks = [200, 400, 600, 800], ) - (F_rs, ρ_rs, V_m, D_m, minV, maxV) = get_values( - p3, - Chen2022.snow_ice, - q_s, - N_s, - ρ_a, - 50, - 50) + Plt.heatmap!(F_rl, ρ_rl, V_ml, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max),) + Plt.contour!(F_rl, ρ_rl, D_ml, color = :black, labels = true, levels = 3) + + Plt.Colorbar( + f[2, 2], + limits = (min, max), + colormap = Plt.reverse(Plt.cgrad(:Spectral)), + flipaxis = true, + vertical = false, + ) - println(D_m[1:10]) + # Attempt to plot D_m for clearer information than the contour plots and debugging + Plt.Axis(f[3, 1], height = 350, width = 350) Plt.heatmap!( F_rs, ρ_rs, - V_m, + D_ms, colormap = Plt.reverse(Plt.cgrad(:Spectral)) - ) - Plt.contour!( - F_rs, - ρ_rs, - D_m, - color = :black, - labels = true, - levels = 3 - ) + ) Plt.Colorbar( - f[1, 2], - limits = (minV, maxV), + f[4, 1], + limits = (minimum(d for d in D_ms if !isnan(d)), maximum(d for d in D_ms if !isnan(d))), + colormap = Plt.reverse(Plt.cgrad(:Spectral)), + flipaxis = true, + vertical = false, + ) + Plt.Axis(f[3, 2], height = 350, width = 350) + Plt.heatmap!( + F_rm, + ρ_rm, + D_mm, + colormap = Plt.reverse(Plt.cgrad(:Spectral)) + ) + Plt.Colorbar( + f[4, 2], + limits = (minimum(d for d in D_mm if !isnan(d)), maximum(d for d in D_mm if !isnan(d))), + colormap = Plt.reverse(Plt.cgrad(:Spectral)), + flipaxis = true, + vertical = false, + ) + Plt.Axis(f[3, 3], height = 350, width = 350) + Plt.heatmap!( + F_rl, + ρ_rl, + D_ml, + colormap = Plt.reverse(Plt.cgrad(:Spectral)) + ) + Plt.Colorbar( + f[4, 3], + limits = (minimum(d for d in D_ml if !isnan(d)), maximum(d for d in D_ml if !isnan(d))), colormap = Plt.reverse(Plt.cgrad(:Spectral)), flipaxis = true, + vertical = false, ) - Plt.resize_to_layout!(f) Plt.save("MorrisonandMilbrandtFig2.svg", f) end println("start") -figure_2() -println("done") \ No newline at end of file +#figure_2() +#println("done") +#println(P3.D_th_helper(p3)) +#println(P3.thresholds(p3, FT(500), FT(0.5))) +#println("") +#println("small = ", P3.q_gamma(p3, FT(0.5), FT(1e7), FT(log(4.9 * 10^2)), P3.thresholds(p3, FT(500), FT(0.5)))) + + +import RootSolvers as RS +ρ_r = FT(500) +F_r = FT(0.8) +N = FT(1e6) +Dₘ = 0.006 +println("started") +x = q <= 0 ? FT(0) : log(q) +shape_problem(x) = Dₘ - P3.D_m(p3, x, N, ρ_r, F_r) +x = + RS.find_zero( + shape_problem, + RS.SecantMethod(log(0.01), log(0.015)), + RS.CompactSolution(), + RS.RelativeSolutionTolerance(eps(FT)), + 5, + ).root + +println("q_solved = ", exp(x)) + +function plotDvsq() + n = 100 + log10qs = range(FT(-4), stop = FT(0), length = n) + qs = [FT(10^q) for q in log10qs] + + F_r = range(FT(0), stop = 0.9, length = n) + + N = FT(2 * 1e7) + ρ_r = FT(700) + + Dms = zeros(n, n) + for i in 1:n + for j in 1:n + Dms[i, j] = P3.D_m(p3, qs[i], N, ρ_r, F_r[j]) + end + end + + println(log10qs) + println(qs) + println(Dms) + + min = 0 + max = 0.006 + + f = Plt.Figure() + Plt.Axis(f[1, 1], xlabel = "F_r", ylabel = "log10(q)", title = "D_m") + Plt.heatmap!(F_r, log10qs, Dms, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max)) + Plt.Colorbar( + f[2, 1], + limits = (min, max), + colormap = Plt.reverse(Plt.cgrad(:Spectral)), + flipaxis = true, + vertical = false, + ) + + Plt.save("testFigure.svg", f) + +end + +#plotDvsq() \ No newline at end of file diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 5c01c3242..58512313e 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -272,7 +272,22 @@ function q_gamma( D_th = D_th_helper(p3) λ = exp(log_λ) + λ = max(10, λ) N_0 = DSD_N₀(p3, N, λ) + + + println("λ = ", λ) + println("N_0 = ", N_0) + #println("qs : ") + println(" qs = ", q_s(p3, p3.ρ_i, N_0, λ, FT(0), D_th)) + #println(" qrz = ", q_rz(p3, N_0, λ, D_th)) + println(" qn = ", q_n(p3, N_0, λ, D_th, th.D_gr)) + println(" qs = ", q_s(p3, th.ρ_g, N_0, λ, th.D_gr, th.D_cr)) + println(" qr = ", q_r(p3, F_r, N_0, λ, th.D_cr)) + println("q_gamma = ", q_s(p3, p3.ρ_i, N_0, λ, FT(0), D_th) + + q_n(p3, N_0, λ, D_th, th.D_gr) + + q_s(p3, th.ρ_g, N_0, λ, th.D_gr, th.D_cr) + + q_r(p3, F_r, N_0, λ, th.D_cr)) return ifelse( F_r == FT(0), @@ -354,8 +369,9 @@ function distribution_parameter_solver( # To ensure that λ is positive solve for x such that λ = exp(x) # We divide by N̂ to deal with large N₀ values for Float32 - N̂ = FT(1e20) + N̂ = FT(1) #FT(1e20) shape_problem(x) = q / N̂ - q_gamma(p3, F_r, N / N̂, x, th) + println("q/N̂ = ", q/N̂) # Get intial guess for solver (; min, max) = get_bounds(N, N̂, q, F_r, p3, th) @@ -367,7 +383,7 @@ function distribution_parameter_solver( RS.SecantMethod(min, max), RS.CompactSolution(), RS.RelativeSolutionTolerance(eps(FT)), - 10, + 5, ).root return (; λ = exp(x), N_0 = DSD_N₀(p3, N, exp(x))) @@ -499,14 +515,18 @@ end Return the mass weighted mean particle size [m] """ -function D_m(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} +function D_m(p3::PSP3, log_q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} # Get the thresholds for different particles regimes th = thresholds(p3, ρ_r, F_r) D_th = D_th_helper(p3) + q = exp(log_q) # Get the shape parameters (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) μ = DSD_μ(p3, λ) + #println("thresholds = ", th) + println("λ = ", λ) + println("N_0 = ", N_0) # Redefine α_va to be in si units α_va = α_va_si(p3) @@ -523,6 +543,12 @@ function D_m(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} n += integrate(th.D_cr, Inf, α_va / (1 - F_r) * N_0, μ + p3.β_va + 1, λ) end + if n/q > 1e16 + #println("Dₘ = ", n/q, " q = ", q, " N = ", N, " λ = ", λ, " N0 = ", N_0, " F_r = ", F_r, " ρ_r = ", ρ_r) + n = 0 + end + + #println("Dₘ = ", n/q) # Normalize by q return n/q From 0787161c86b0346e31ee35b5a1e6b7304bf02491 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Wed, 6 Mar 2024 12:49:32 -0800 Subject: [PATCH 13/33] merging --- docs/src/plots/P3TerminalVelocityPlots.jl | 52 +++-------------------- src/P3Scheme.jl | 39 +++-------------- 2 files changed, 10 insertions(+), 81 deletions(-) diff --git a/docs/src/plots/P3TerminalVelocityPlots.jl b/docs/src/plots/P3TerminalVelocityPlots.jl index 0e1a57a83..7089b166a 100644 --- a/docs/src/plots/P3TerminalVelocityPlots.jl +++ b/docs/src/plots/P3TerminalVelocityPlots.jl @@ -169,8 +169,8 @@ function figure_2() end println("start") -#figure_2() -#println("done") +figure_2() +println("done") #println(P3.D_th_helper(p3)) #println(P3.thresholds(p3, FT(500), FT(0.5))) #println("") @@ -180,11 +180,10 @@ println("start") import RootSolvers as RS ρ_r = FT(500) F_r = FT(0.8) -N = FT(1e6) +N = FT(1e8) Dₘ = 0.006 println("started") -x = q <= 0 ? FT(0) : log(q) -shape_problem(x) = Dₘ - P3.D_m(p3, x, N, ρ_r, F_r) +shape_problem(x) = Dₘ - P3.D_m(p3, exp(x), N, ρ_r, F_r) x = RS.find_zero( shape_problem, @@ -194,45 +193,4 @@ x = 5, ).root -println("q_solved = ", exp(x)) - -function plotDvsq() - n = 100 - log10qs = range(FT(-4), stop = FT(0), length = n) - qs = [FT(10^q) for q in log10qs] - - F_r = range(FT(0), stop = 0.9, length = n) - - N = FT(2 * 1e7) - ρ_r = FT(700) - - Dms = zeros(n, n) - for i in 1:n - for j in 1:n - Dms[i, j] = P3.D_m(p3, qs[i], N, ρ_r, F_r[j]) - end - end - - println(log10qs) - println(qs) - println(Dms) - - min = 0 - max = 0.006 - - f = Plt.Figure() - Plt.Axis(f[1, 1], xlabel = "F_r", ylabel = "log10(q)", title = "D_m") - Plt.heatmap!(F_r, log10qs, Dms, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max)) - Plt.Colorbar( - f[2, 1], - limits = (min, max), - colormap = Plt.reverse(Plt.cgrad(:Spectral)), - flipaxis = true, - vertical = false, - ) - - Plt.save("testFigure.svg", f) - -end - -#plotDvsq() \ No newline at end of file +println("q_solved = ", exp(x)) \ No newline at end of file diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index a3a8e556a..9d5f0598c 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -267,40 +267,20 @@ end # or # q_rim > 0 and D_min = D_gr, D_max = D_cr, ρ = ρ_g function q_s(p3::PSP3, ρ::FT, μ::FT, λ::FT, D_min::FT, D_max::FT) where {FT} - x = μ + 4 - return integrate(D_min, D_max, FT(π) / 6 * ρ * N_0, DSD_μ(p3, λ) + 3, λ) - #return FT(π) / 6 * ρ / λ^x * (Γ(x, λ * D_min) - Γ(x, λ * D_max)) + return integrate(D_min, D_max, FT(π) / 6 * ρ, μ + 3, λ) end # q_rim = 0 and D_min = D_th, D_max = inf function q_rz(p3::PSP3, μ::FT, λ::FT, D_min::FT) where {FT} - x = μ + p3.β_va + 1 - return α_va_si(p3) / λ^x * (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) -function q_rz(p3::PSP3, N_0::FT, λ::FT, D_min::FT) where {FT} - x = DSD_μ(p3, λ) + p3.β_va + 1 - return integrate(D_min, FT(Inf), α_va_si(p3) * N_0, DSD_μ(p3, λ) + p3.β_va, λ) - #return α_va_si(p3) * N_0 / λ^x * - # (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) + return integrate(D_min, FT(Inf), α_va_si(p3), μ + p3.β_va, λ) end # q_rim > 0 and D_min = D_th and D_max = D_gr function q_n(p3::PSP3, μ::FT, λ::FT, D_min::FT, D_max::FT) where {FT} - x = μ + p3.β_va + 1 - return α_va_si(p3) / λ^x * (Γ(x, λ * D_min) - Γ(x, λ * D_max)) -function q_n(p3::PSP3, N_0::FT, λ::FT, D_min::FT, D_max::FT) where {FT} - x = DSD_μ(p3, λ) + p3.β_va + 1 - return integrate(D_min, D_max, α_va_si(p3) * N_0, DSD_μ(p3, λ) + p3.β_va, λ) - #return α_va_si(p3) * N_0 / λ^x * (Γ(x, λ * D_min) - Γ(x, λ * D_max)) + return integrate(D_min, D_max, α_va_si(p3), μ + p3.β_va, λ) end # partially rimed ice or large unrimed ice (upper bound on D is infinity) # q_rim > 0 and D_min = D_cr, D_max = inf function q_r(p3::PSP3, F_r::FT, μ::FT, λ::FT, D_min::FT) where {FT} - x = μ + p3.β_va + 1 - return α_va_si(p3) / (1 - F_r) / λ^x * - (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) -function q_r(p3::PSP3, F_r::FT, N_0::FT, λ::FT, D_min::FT) where {FT} - x = DSD_μ(p3, λ) + p3.β_va + 1 - return integrate(D_min, FT(Inf), α_va_si(p3) * N_0 / (1 - F_r), DSD_μ(p3, λ) + p3.β_va, λ) - #return α_va_si(p3) * N_0 / (1 - F_r) / λ^x * - # (Γ(x) + Γ(x, λ * D_min) - (x - 1) * Γ(x - 1)) + return integrate(D_min, FT(Inf), α_va_si(p3) / (1 - F_r), μ + p3.β_va, λ) end """ @@ -568,9 +548,6 @@ function D_m(p3::PSP3, log_q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} # Get the shape parameters (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) μ = DSD_μ(p3, λ) - #println("thresholds = ", th) - println("λ = ", λ) - println("N_0 = ", N_0) # Redefine α_va to be in si units α_va = α_va_si(p3) @@ -586,13 +563,7 @@ function D_m(p3::PSP3, log_q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} n += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * N_0, μ + 4, λ) n += integrate(th.D_cr, Inf, α_va / (1 - F_r) * N_0, μ + p3.β_va + 1, λ) end - - if n/q > 1e16 - #println("Dₘ = ", n/q, " q = ", q, " N = ", N, " λ = ", λ, " N0 = ", N_0, " F_r = ", F_r, " ρ_r = ", ρ_r) - n = 0 - end - - #println("Dₘ = ", n/q) + # Normalize by q return n/q From a182c07a8f15a832b1bc10f8c2eca0927e2ff78d Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Wed, 6 Mar 2024 15:17:44 -0800 Subject: [PATCH 14/33] correct Dm for small and medium --- docs/src/plots/P3TerminalVelocityPlots.jl | 66 +++++++++++++---------- src/P3Scheme.jl | 3 +- 2 files changed, 40 insertions(+), 29 deletions(-) diff --git a/docs/src/plots/P3TerminalVelocityPlots.jl b/docs/src/plots/P3TerminalVelocityPlots.jl index 7089b166a..369846b41 100644 --- a/docs/src/plots/P3TerminalVelocityPlots.jl +++ b/docs/src/plots/P3TerminalVelocityPlots.jl @@ -8,6 +8,8 @@ const PSP3 = CMP.ParametersP3 FT = Float64 +p3 = CMP.ParametersP3(FT) + function get_values(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_a::FT, x_resolution::Int, y_resolution::Int) where {FT} F_rs = range(FT(0), stop = FT(1 - eps(FT)), length = x_resolution) ρ_rs = range(FT(25), stop = FT(975), length = y_resolution) @@ -29,27 +31,26 @@ function get_values(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT end function figure_2() - p3 = CMP.ParametersP3(FT) Chen2022 = CMP.Chen2022VelType(FT) # density of air in kg/m^3 ρ_a = FT(1.293) f = Plt.Figure() - xres = 10 - yres = 10 + xres = 100 + yres = 100 min = FT(0) max = FT(10) # small D_m - q_s = FT(0.00074047) + q_s = FT(0.0008) N_s = FT(1e6) # medium D_m - q_m = FT(0.0028151) + q_m = FT(0.22) N_m = FT(1e6) # large D_m - q_l = FT(0.02494021) + q_l = FT(0.7) N_l = FT(1e6) # get V_m and D_m @@ -57,16 +58,14 @@ function figure_2() (F_rm, ρ_rm, V_mm, D_mm) = get_values(p3, Chen2022.snow_ice, q_m, N_m, ρ_a, xres, yres) (F_rl, ρ_rl, V_ml, D_ml) = get_values(p3, Chen2022.snow_ice, q_l, N_l, ρ_a, xres, yres) - println("small: min: ", minimum(d for d in D_ms if !isnan(d))," max: ", maximum(d for d in D_ms if !isnan(d))) - println("medium: min: ", minimum(d for d in D_mm if !isnan(d))," max: ", maximum(d for d in D_mm if !isnan(d))) - println("large: min: ", minimum(d for d in D_ml if !isnan(d))," max: ", maximum(d for d in D_ml if !isnan(d))) - - #println(D_mm) - - println("") - #println(D_ml) + println("small q = ", q_s, ": min: ", minimum(d for d in D_ms if !isnan(d))," max: ", maximum(d for d in D_ms if !isnan(d))) + println("medium q = ", q_m, ": min: ", minimum(d for d in D_mm if !isnan(d))," max: ", maximum(d for d in D_mm if !isnan(d))) + println("large q = ", q_l, ": min: ", minimum(d for d in D_ml if !isnan(d))," max: ", maximum(d for d in D_ml if !isnan(d))) - Plt.Axis( + points = [0, 0.1, 0.2, 0.5, 1, 2, 3, 4, 5, 7.5, 10] + labels = string.(points) + + ax1 = Plt.Axis( f[1, 1], xlabel = "F_r", ylabel = "ρ_r", @@ -78,10 +77,10 @@ function figure_2() yticks = [200, 400, 600, 800], ) - Plt.heatmap!(F_rs, ρ_rs, V_ms, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max),) + Plt.contourf!(F_rs, ρ_rs, V_ms, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max), levels = points, extendhigh = :darkred) Plt.contour!(F_rs, ρ_rs, D_ms, color = :black, labels = true, levels = 3,) - Plt.Axis( + ax2 = Plt.Axis( f[1, 2], xlabel = "F_r", ylabel = "ρ_r", @@ -93,10 +92,10 @@ function figure_2() yticks = [200, 400, 600, 800], ) - Plt.heatmap!(F_rm, ρ_rm, V_mm, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max),) + Plt.contourf!(F_rm, ρ_rm, V_mm, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max), levels = points, extendhigh = :darkred) Plt.contour!(F_rm, ρ_rm, D_mm, color = :black, labels = true, levels = 3) - Plt.Axis( + ax3 = Plt.Axis( f[1, 3], xlabel = "F_r", ylabel = "ρ_r", @@ -108,21 +107,27 @@ function figure_2() yticks = [200, 400, 600, 800], ) - Plt.heatmap!(F_rl, ρ_rl, V_ml, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max),) + Plt.contourf!(F_rl, ρ_rl, V_ml, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max), levels = points, extendhigh = :darkred) Plt.contour!(F_rl, ρ_rl, D_ml, color = :black, labels = true, levels = 3) Plt.Colorbar( f[2, 2], limits = (min, max), - colormap = Plt.reverse(Plt.cgrad(:Spectral)), + colormap = Plt.reverse(Plt.cgrad(:Spectral, length(points), categorical = true)), flipaxis = true, vertical = false, + ticks = (points, labels), + highclip = :darkred ) + Plt.linkxaxes!(ax1, ax2) + Plt.linkxaxes!(ax2, ax3) + Plt.linkyaxes!(ax1, ax2) + Plt.linkyaxes!(ax2, ax3) # Attempt to plot D_m for clearer information than the contour plots and debugging - Plt.Axis(f[3, 1], height = 350, width = 350) - Plt.heatmap!( + ax4 = Plt.Axis(f[3, 1], height = 350, width = 350, xlabel = "F_r", ylabel = "ρ_r", title = "Small Dₘ vs F_r and ρ_r") + Plt.contourf!( F_rs, ρ_rs, D_ms, @@ -135,8 +140,8 @@ function figure_2() flipaxis = true, vertical = false, ) - Plt.Axis(f[3, 2], height = 350, width = 350) - Plt.heatmap!( + ax5 = Plt.Axis(f[3, 2], height = 350, width = 350, xlabel = "F_r", ylabel = "ρ_r", title = "Medium Dₘ vs F_r and ρ_r") + Plt.contourf!( F_rm, ρ_rm, D_mm, @@ -149,8 +154,8 @@ function figure_2() flipaxis = true, vertical = false, ) - Plt.Axis(f[3, 3], height = 350, width = 350) - Plt.heatmap!( + ax6 = Plt.Axis(f[3, 3], height = 350, width = 350, xlabel = "F_r", ylabel = "ρ_r", title = "Large Dₘ vs F_r and ρ_r") + Plt.contourf!( F_rl, ρ_rl, D_ml, @@ -163,6 +168,13 @@ function figure_2() flipaxis = true, vertical = false, ) + + Plt.linkxaxes!(ax1, ax4) + Plt.linkxaxes!(ax4, ax5) + Plt.linkxaxes!(ax5, ax6) + Plt.linkyaxes!(ax1, ax4) + Plt.linkyaxes!(ax4, ax5) + Plt.linkyaxes!(ax5, ax6) Plt.resize_to_layout!(f) Plt.save("MorrisonandMilbrandtFig2.svg", f) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 9d5f0598c..2112a9a24 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -539,12 +539,11 @@ end Return the mass weighted mean particle size [m] """ -function D_m(p3::PSP3, log_q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} +function D_m(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} # Get the thresholds for different particles regimes th = thresholds(p3, ρ_r, F_r) D_th = D_th_helper(p3) - q = exp(log_q) # Get the shape parameters (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) μ = DSD_μ(p3, λ) From ed0291c4ad330c7bf6a6247394c6c3ff6643def8 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Mon, 11 Mar 2024 17:22:16 -0400 Subject: [PATCH 15/33] edits --- src/P3Scheme.jl | 77 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 2112a9a24..854d944f9 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -200,26 +200,6 @@ function DSD_μ_approx(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} return min(p3.μ_max, max(FT(0), μ)) end -""" - integrate(a, b, c1, c2, c3) - - - a - lower bound - - b - upper bound - - c1, c2, c3 - respective constants - - Integrates the function c1 * D ^ (c2) * exp(-c3 * D) dD from a to b - Returns the result -""" -function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where{FT} - if b == Inf - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3)) - elseif a == 0 - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2) - Γ(1 + c2, b * c3)) - else - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3) - Γ(1 + c2, b * c3)) - end -end - """ DSD_μ(p3, λ) @@ -249,6 +229,51 @@ function DSD_N₀(p3::PSP3, N::FT, λ::FT) where {FT} return N / Γ(1 + μ) * λ^(1 + μ) end +""" + integrate(a, b, c1, c2, c3) + + - a - lower bound + - b - upper bound + - c1, c2, c3 - respective constants + + Integrates the function c1 * D ^ (c2) * exp(-c3 * D) dD from a to b + Returns the result +""" +function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where{FT} + if b == Inf + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3)) + elseif a == 0 + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2) - Γ(1 + c2, b * c3)) + else + return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3) - Γ(1 + c2, b * c3)) + end +end + +""" + get_coeffs(p3, th) + + - p3 - a struct with P3 scheme parameters + - th - thresholds tuple as returned by thresholds() + - F_r - rime mass fraction [q_rim/q_i] + +Returns the coefficients for m(D), a(D), and the respective powers of D + Where the indices are as follows: + 1 - small, spherical ice + 2 - large, unrimed ice + 3 - dense, nonspherical ice + 4 - graupel + 5 - partially rimed ice + 6 - second half of partially rimed ice (only for a) +""" +function get_coeffs(p3::PSP3, th, F_r::FT) where{FT} + α_va = α_va_si(p3) + m = [π / 6 * p3.ρ_i, α_va, α_va, π / 6 * th.ρ_g, α_va / (1- F_r)] + m_power = [FT(3), p3.β_va, p3.β_va, 3, p3.β_va] + a = [π/4, p3.γ, p3.γ, π/4, F_r * π/4, (1 - F_r) * p3.γ] + a_power = [FT(2), p3.σ, p3.σ, FT(2), FT(2), p3.σ] + return (; m, m_power, a, a_power) +end + """ q_(p3, ρ, F_r, λ, μ, D_min, D_max) @@ -413,6 +438,18 @@ function distribution_parameter_solver( return (; λ = exp(x), N_0 = DSD_N₀(p3, N, exp(x))) end +""" + ϕ_coeff(m, a) + + - m - coefficient of mass + - a - coefficient of mass + + Returns the coefficient of aspect ratio (ignoring powers of D) +""" +function ϕ_coeff(p3::PSP3, m::FT, a::FT) where{FT} + return 16 * p3.ρ_i ^ 2 * a ^ 3 / (9 * π^2 * m^2) +end + """ terminal_velocity_mass(p3, Chen2022, q, N, ρ_r, F_r) From 19157528aefd12aa731b0ade55ec5efba5ade3f9 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Tue, 12 Mar 2024 13:38:01 -0400 Subject: [PATCH 16/33] Added large table calculations for Chen --- docs/src/API.md | 2 +- docs/src/plots/TerminalVelocityComparisons.jl | 2 +- src/Common.jl | 24 +++++- src/Microphysics1M.jl | 4 +- src/Microphysics2M.jl | 2 +- src/parameters/TerminalVelocity.jl | 23 ++++++ test/common_functions_tests.jl | 73 +++++++++++++++++++ 7 files changed, 121 insertions(+), 9 deletions(-) diff --git a/docs/src/API.md b/docs/src/API.md index 6b7e209f9..fb7930ca6 100644 --- a/docs/src/API.md +++ b/docs/src/API.md @@ -113,7 +113,7 @@ Common.a_w_xT Common.a_w_eT Common.a_w_ice Common.Chen2022_vel_add -Common.Chen2022_vel_coeffs +Common.Chen2022_vel_coeffs_small ``` # Parameters diff --git a/docs/src/plots/TerminalVelocityComparisons.jl b/docs/src/plots/TerminalVelocityComparisons.jl index 39992a80a..855918162 100644 --- a/docs/src/plots/TerminalVelocityComparisons.jl +++ b/docs/src/plots/TerminalVelocityComparisons.jl @@ -34,7 +34,7 @@ function rain_terminal_velocity_individual_Chen( ρ::FT, D_r::FT, #in m ) where {FT <: Real} - ai, bi, ci = CMO.Chen2022_vel_coeffs(velo_scheme, ρ) + ai, bi, ci = CMO.Chen2022_vel_coeffs_small(velo_scheme, ρ) D_r = D_r * 1000 #D_r is in mm in the paper --> multiply D_r by 1000 v = 0 diff --git a/src/Common.jl b/src/Common.jl index a712542bc..fd684b838 100644 --- a/src/Common.jl +++ b/src/Common.jl @@ -17,7 +17,7 @@ export a_w_xT export a_w_eT export a_w_ice export Chen2022_vel_add -export Chen2022_vel_coeffs +export Chen2022_vel_coeffs_small """ G_func(air_props, tps, T, Liquid()) @@ -205,7 +205,7 @@ function a_w_ice(tps::TPS, T::FT) where {FT} end """ - Chen2022_vel_coeffs(precip_type, velo_scheme, ρ) + Chen2022_vel_coeffs_small(precip_type, velo_scheme, ρ) - velo_scheme - type for terminal velocity scheme (contains free parameters) - ρ - air density @@ -213,7 +213,7 @@ end Returns the coefficients from Appendix B in Chen et al 2022 DOI: 10.1016/j.atmosres.2022.106171 """ -function Chen2022_vel_coeffs( +function Chen2022_vel_coeffs_small( velo_scheme::CMP.Chen2022VelTypeRain, ρ::FT, ) where {FT} @@ -231,7 +231,7 @@ function Chen2022_vel_coeffs( return (aiu, bi, ciu) end -function Chen2022_vel_coeffs( +function Chen2022_vel_coeffs_small( velo_scheme::CMP.Chen2022VelTypeSnowIce{FT}, ρ::FT, ) where {FT} @@ -247,6 +247,22 @@ function Chen2022_vel_coeffs( return (aiu, bi, ciu) end +function Chen2022_vel_coeffs_large( + velo_scheme::CMP.Chen2022VelTypeSnowIce{FT}, + ρ::FT, +) where {FT} + (; Al, Bl, Cl, El, Fl, Gl, Hl) = velo_scheme + + ai = (Bl * ρ^Al, El * ρ^Al * exp(Hl * ρ)) + bi = (Cl, Fl) + ci = (FT(0), Gl) + # unit conversions + aiu = ai .* 1000 .^ bi + ciu = ci .* 1000 + + return (aiu, bi, ciu) +end + """ Chen2022_vel_add(a, b, c, λ, k) diff --git a/src/Microphysics1M.jl b/src/Microphysics1M.jl index ed2462be1..32aa0f127 100644 --- a/src/Microphysics1M.jl +++ b/src/Microphysics1M.jl @@ -143,7 +143,7 @@ function terminal_velocity( fall_w = FT(0) if q > FT(0) # coefficients from Table B1 from Chen et. al. 2022 - aiu, bi, ciu = CO.Chen2022_vel_coeffs(vel, ρ) + aiu, bi, ciu = CO.Chen2022_vel_coeffs_small(vel, ρ) # size distribution parameter λ::FT = lambda(pdf, mass, q, ρ) # eq 20 from Chen et al 2022 @@ -172,7 +172,7 @@ function terminal_velocity( aec = ae + Δa # coefficients from Appendix B from Chen et. al. 2022 - aiu, bi, ciu = CO.Chen2022_vel_coeffs(vel, ρ) + aiu, bi, ciu = CO.Chen2022_vel_coeffs_small(vel, ρ) ρᵢ = vel.ρᵢ κ = FT(-1 / 3) #oblate k = 3 # mass weighted diff --git a/src/Microphysics2M.jl b/src/Microphysics2M.jl index c63e05f61..66784ea96 100644 --- a/src/Microphysics2M.jl +++ b/src/Microphysics2M.jl @@ -358,7 +358,7 @@ function rain_terminal_velocity( return (FT(0), FT(0)) end # coefficients from Table B1 from Chen et. al. 2022 - aiu, bi, ciu = CO.Chen2022_vel_coeffs(vel, ρ) + aiu, bi, ciu = CO.Chen2022_vel_coeffs_small(vel, ρ) # size distribution parameter λ = raindrops_limited_vars(pdf, q_rai, ρ, N_rai).λr diff --git a/src/parameters/TerminalVelocity.jl b/src/parameters/TerminalVelocity.jl index 6f7df10f7..8cb46fe37 100644 --- a/src/parameters/TerminalVelocity.jl +++ b/src/parameters/TerminalVelocity.jl @@ -150,6 +150,13 @@ struct Chen2022VelTypeSnowIce{FT} <: ParametersType{FT} Es::FT Fs::FT Gs::FT + Al::FT + Bl::FT + Cl::FT + El::FT + Fl::FT + Gl::FT + Hl::FT "density of cloud ice [kg/m3]" ρᵢ::FT end @@ -163,6 +170,13 @@ function Chen2022VelTypeSnowIce(toml_dict::CP.AbstractTOMLDict) :Chen2022_table_B3_Es => :Es, :Chen2022_table_B3_Fs => :Fs, :Chen2022_table_B3_Gs => :Gs, + :Chen2022_table_B5_Al => :Al, + :Chen2022_table_B5_Bl => :Bl, + :Chen2022_table_B5_Cl => :Cl, + :Chen2022_table_B5_El => :El, + :Chen2022_table_B5_Fl => :Fl, + :Chen2022_table_B5_Gl => :Gl, + :Chen2022_table_B5_Hl => :Hl, :density_ice_water => :ρᵢ, ) p = CP.get_parameter_values(toml_dict, name_map, "CloudMicrophysics") @@ -174,6 +188,15 @@ function Chen2022VelTypeSnowIce(toml_dict::CP.AbstractTOMLDict) p.Es[1] - p.Es[2] * (log(p.ρᵢ))^2 + p.Es[3] * sqrt(p.ρᵢ), -exp(p.Fs[1] - p.Fs[2] * (log(p.ρᵢ))^2 + p.Fs[3] * log(p.ρᵢ)), FT(1) / (p.Gs[1] + p.Gs[2] / (log(p.ρᵢ)) - p.Gs[3] * log(p.ρᵢ) / p.ρᵢ), + p.Al[1] + p.Al[2] * log(p.ρᵢ) + p.Al[3] * (p.ρᵢ)^(-3 / 2), + exp(p.Bl[1] + p.Bl[2] * log(p.ρᵢ)^2 + p.Bl[3] * log(p.ρᵢ)), + exp(p.Cl[1] + p.Cl[2] / log(p.ρᵢ) + p.Cl[3] / p.ρᵢ), + p.El[1] + p.El[2] * log(p.ρᵢ) * sqrt(p.ρᵢ) + p.El[3] * sqrt(p.ρᵢ), + p.Fl[1] + p.Fl[2] * log(p.ρᵢ) + p.Fl[3] * exp(-p.ρᵢ), + ( + p.Gl[1] + p.Gl[2] * log(p.ρᵢ) * sqrt(p.ρᵢ) + p.Gl[3] / sqrt(p.ρᵢ) + )^(-1), + p.Hl[1] + p.Hl[2] * (p.ρᵢ)^(5 / 2) - p.Hl[3] * exp(-p.ρᵢ), p.ρᵢ, ) end diff --git a/test/common_functions_tests.jl b/test/common_functions_tests.jl index 0254099a8..42c2b2905 100644 --- a/test/common_functions_tests.jl +++ b/test/common_functions_tests.jl @@ -147,14 +147,87 @@ function test_a_w_ice(FT) end end +function test_Chen_coefficients(FT) + ρ = FT(1.2) + Ch2022 = CMP.Chen2022VelType(FT) + + TT.@testset "Chen terminal velocity rain (B1)" begin + aiu, bi, ciu = CO.Chen2022_vel_coeffs_small(Ch2022.rain, ρ) + + TT.@test all( + isapprox.( + aiu, + [ + FT(286768.02047954104), + FT(-1.6916433443360287e6), + FT(9843.240767655458), + ], + rtol = eps(FT), + ), + ) + TT.@test all( + isapprox.( + bi, + [FT(2.249342), FT(2.249342), FT(1.098942)], + rtol = eps(FT), + ), + ) + TT.@test all( + isapprox.(ciu, [FT(0), FT(184.325), FT(184.325)], rtol = eps(FT)), + ) + end + + TT.@testset "Chen terminal velocity small ice (B2)" begin + aiu, bi, ciu = CO.Chen2022_vel_coeffs_small(Ch2022.snow_ice, ρ) + + TT.@test all( + isapprox.( + aiu, + [380.9523818928577, -378.94964461870484], + rtol = eps(FT), + ), + ) + TT.@test all( + isapprox.( + bi, + [0.7065511618279822, 0.7065511618279822], + rtol = eps(FT), + ), + ) + TT.@test all(isapprox.(ciu, [0.0, 5194.870484289714], rtol = eps(FT))) + end + + TT.@testset "Chen terminal velocity large ice (B4)" begin + aiu, bi, ciu = CO.Chen2022_vel_coeffs_large(Ch2022.snow_ice, ρ) + + TT.@test all( + isapprox.( + aiu, + [144.32735209844674, -0.9732523067775996], + rtol = eps(FT), + ), + ) + TT.@test all( + isapprox.( + bi, + [0.5375612365666844, 0.020917783512726773], + rtol = eps(FT), + ), + ) + TT.@test all(isapprox.(ciu, [0.0, 86.47212246687042], rtol = eps(FT))) + end +end + println("Testing Float64") test_H2SO4_soln_saturation_vapor_pressure(Float64) test_a_w_xT(Float64) test_a_w_eT(Float64) test_a_w_ice(Float64) +test_Chen_coefficients(Float64) println("Testing Float32") test_H2SO4_soln_saturation_vapor_pressure(Float32) test_a_w_xT(Float32) test_a_w_eT(Float32) test_a_w_ice(Float32) +#test_Chen_coefficients(Float32) From ee7d05f34cebc3f6f047f04f7c46fe0198391769 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Wed, 13 Mar 2024 14:31:28 -0400 Subject: [PATCH 17/33] edits --- docs/src/plots/P3SchemePlots.jl | 8 -------- src/P3Scheme.jl | 16 +++++++++++----- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/docs/src/plots/P3SchemePlots.jl b/docs/src/plots/P3SchemePlots.jl index a25e28b06..9e13e94db 100644 --- a/docs/src/plots/P3SchemePlots.jl +++ b/docs/src/plots/P3SchemePlots.jl @@ -59,14 +59,6 @@ function p3_mass( if D >= th.D_cr return mass_r(p3, D, F_r) # partially rimed ice end - - # TODO - would something like this be better? - #return ifelse(D_th_helper(p3) > D, mass_s(D, p3.ρ_i), - # ifelse(F_r == 0, mass_nl(p3, D), - # ifelse(th.D_gr > D >= D_th_helper(p3), mass_nl(p3, D), - # ifelse(th.D_cr > D >= th.D_gr, mass_s(D, th.ρ_g), - # mass_r(p3, D, F_r))))) - end """ diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 854d944f9..87ec57595 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -470,6 +470,10 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, th = thresholds(p3, ρ_r, F_r) D_th = D_th_helper(p3) + if th.D_gr > 0.000625 || th.D_cr > 0.000625 + println(th) + end + # Get the shape parameters (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) μ = DSD_μ(p3, λ) @@ -491,17 +495,19 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, for i in 1:2 if F_r == 0 v += integrate(FT(0), D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) - v += integrate(D_th, Inf, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + v += integrate(D_th, Inf, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) else v += integrate(FT(0), D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) - v += integrate(D_th, th.D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + p3.β_va + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) - v += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + 3 + μ, ci[i] + λ) - v += ( + v += integrate(D_th, th.D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + v += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ + 3, ci[i] + λ) + # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) + v += integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) + #= v += ( integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi[i] + p3.β_va + μ + κ*(3 * p3.σ), ci[i] + λ) + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi[i] + p3.β_va + μ + κ*(2 + 2 * p3.σ), ci[i] + λ) + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi[i] + p3.β_va + μ + κ*(4 + p3.σ), ci[i] + λ) + integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi[i] + p3.β_va + μ + 6 * κ, ci[i] + λ) - ) + ) =# end end From ae5210119adf8009e8946fd751adfe42a55692a6 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Wed, 13 Mar 2024 16:00:22 -0400 Subject: [PATCH 18/33] updates --- docs/src/plots/P3TerminalVelocityPlots.jl | 9 ++- src/P3Scheme.jl | 72 ++++++++++++++++------- 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/docs/src/plots/P3TerminalVelocityPlots.jl b/docs/src/plots/P3TerminalVelocityPlots.jl index 369846b41..fd021770a 100644 --- a/docs/src/plots/P3TerminalVelocityPlots.jl +++ b/docs/src/plots/P3TerminalVelocityPlots.jl @@ -205,4 +205,11 @@ x = 5, ).root -println("q_solved = ", exp(x)) \ No newline at end of file +println("q_solved = ", exp(x)) + +Chen2022 = CMP.Chen2022VelType(FT) +q = FT(0.0008) +N = FT(1e6) +ρ_r = FT(900) +F_r = FT(0.99) +P3.terminal_velocity_mass(p3, Chen2022.snow_ice, q, N, ρ_r, F_r, FT(1.293)) \ No newline at end of file diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 87ec57595..83f0796a1 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -16,6 +16,7 @@ import SpecialFunctions as SF import RootSolvers as RS import ClimaParams as CP import CloudMicrophysics.Parameters as CMP +import CloudMicrophysics.Common as CO const PSP3 = CMP.ParametersP3 @@ -469,23 +470,16 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, # Get the thresholds for different particles regimes th = thresholds(p3, ρ_r, F_r) D_th = D_th_helper(p3) - - if th.D_gr > 0.000625 || th.D_cr > 0.000625 - println(th) - end + cutoff = FT(0.000625) # TO be added to the struct # Get the shape parameters (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) μ = DSD_μ(p3, λ) # Get the ai, bi, ci constants (in si units) for velocity calculations - (; As, Bs, Cs, Es, Fs, Gs) = Chen2022 - - bi = [Bs + ρ_a * Cs, Bs + ρ_a * Cs] - ai = [Es * ρ_a^As * 10^(3 * bi[1]), Fs * ρ_a^As * 10^(3 * bi[2])] - ci = [0, Gs * 10^3] + (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) - κ = FT(1/3) + κ = FT(-1/6) #FT(1/3) # Redefine α_va to be in si units α_va = α_va_si(p3) @@ -495,19 +489,55 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, for i in 1:2 if F_r == 0 v += integrate(FT(0), D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) - v += integrate(D_th, Inf, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + v += integrate(D_th, cutoff, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + + # Get velocity coefficients for large particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) + v += integrate(cutoff, Inf, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) else + large = false v += integrate(FT(0), D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) - v += integrate(D_th, th.D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) - v += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ + 3, ci[i] + λ) - # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) - v += integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) - #= v += ( - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi[i] + p3.β_va + μ + κ*(3 * p3.σ), ci[i] + λ) + - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi[i] + p3.β_va + μ + κ*(2 + 2 * p3.σ), ci[i] + λ) + - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi[i] + p3.β_va + μ + κ*(4 + p3.σ), ci[i] + λ) + - integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi[i] + p3.β_va + μ + 6 * κ, ci[i] + λ) - ) =# + + # D_th to D_gr + if !large && th.D_gr > cutoff + v += integrate(D_th, cutoff, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + + # large particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) + large = true + + v += integrate(cutoff, th.D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + else + v += integrate(D_th, th.D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + end + + # D_gr to D_cr + if !large && th.D_cr > cutoff + v += integrate(th.D_gr, cutoff, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ + 3, ci[i] + λ) + + # large particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) + large = true + + v += integrate(cutoff, th.D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ + 3, ci[i] + λ) + else + v += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ + 3, ci[i] + λ) + end + + # D_cr to Infinity + if !large + # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) + v += integrate(th.D_cr, cutoff, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) + + # large particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) + large = true + + v += integrate(cutoff, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) + else + # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) + v += integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) + end end end From 79fcbfa641ddcb749c1e2d67c52acf7a1249df99 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Thu, 14 Mar 2024 19:07:02 -0400 Subject: [PATCH 19/33] tests and quadgk --- Project.toml | 1 + docs/Project.toml | 1 + src/Common.jl | 16 ----------- src/P3Scheme.jl | 73 ++++++++--------------------------------------- test/Project.toml | 3 +- test/p3_tests.jl | 56 ++++++++++++++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 78 deletions(-) diff --git a/Project.toml b/Project.toml index 249ac8029..542802fd9 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ version = "0.18.0" ClimaParams = "5c42b081-d73a-476f-9059-fd94b934656c" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" RootSolvers = "7181ea78-2dcb-4de3-ab41-2b8ab5a31e74" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" Thermodynamics = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c" diff --git a/docs/Project.toml b/docs/Project.toml index cc62f8d8e..2df7feda3 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -9,6 +9,7 @@ DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244" Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RootSolvers = "7181ea78-2dcb-4de3-ab41-2b8ab5a31e74" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" diff --git a/src/Common.jl b/src/Common.jl index 867866a70..15f09e861 100644 --- a/src/Common.jl +++ b/src/Common.jl @@ -247,22 +247,6 @@ function Chen2022_vel_coeffs_small( return (aiu, bi, ciu) end -function Chen2022_vel_coeffs_large( - velo_scheme::CMP.Chen2022VelTypeSnowIce{FT}, - ρ::FT, -) where {FT} - (; Al, Bl, Cl, El, Fl, Gl, Hl) = velo_scheme - - ai = (Bl * ρ^Al, El * ρ^Al * exp(Hl * ρ)) - bi = (Cl, Fl) - ci = (FT(0), Gl) - # unit conversions - aiu = ai .* 1000 .^ bi - ciu = ci .* 1000 - - return (aiu, bi, ciu) -end - """ Chen2022_vel_coeffs_large(velo_scheme, ρ) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 83f0796a1..a298ab04e 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -12,8 +12,9 @@ Note: Particle size is defined as its maximum length (i.e. max dimesion). module P3Scheme import SpecialFunctions as SF - +import QuadGK as QGK import RootSolvers as RS + import ClimaParams as CP import CloudMicrophysics.Parameters as CMP import CloudMicrophysics.Common as CO @@ -484,7 +485,6 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, # Redefine α_va to be in si units α_va = α_va_si(p3) - # TODO Update the velocity to use a different formulation for D > 0.625 mm v = 0 for i in 1:2 if F_r == 0 @@ -526,17 +526,23 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, # D_cr to Infinity if !large + (I, e) = QGK.quadgk(D -> (16 * p3.ρ_i^2 * (F_r * π/4 * D^2 + (1-F_r) * p3.γ*D ^ p3.σ)^3 / (9 * π * (α_va/ (1 - F_r) * D ^ p3.β_va)^2)) ^ κ * ai[i] * D^(bi[i]) * exp(- ci[i] * D) * (α_va/ (1 - F_r) * D ^ p3.β_va) * N_0 * D^μ * exp(-λ*D), th.D_cr, cutoff) + v += I # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) - v += integrate(th.D_cr, cutoff, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) + #v += integrate(th.D_cr, cutoff, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) # large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) large = true - v += integrate(cutoff, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) + (I, e) = QGK.quadgk(D -> (16 * p3.ρ_i^2 * (F_r * π/4 * D^2 + (1-F_r) * p3.γ*D ^ p3.σ)^3 / (9 * π * (α_va/ (1 - F_r) * D ^ p3.β_va)^2)) ^ κ * ai[i] * D^(bi[i]) * exp(- ci[i] * D) * (α_va/ (1 - F_r) * D ^ p3.β_va) * N_0 * D^μ * exp(-λ*D), cutoff, Inf) + v += I + #v += integrate(cutoff, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) else + (I, e) = QGK.quadgk(D -> (16 * p3.ρ_i^2 * (F_r * π/4 * D^2 + (1-F_r) * p3.γ*D ^ p3.σ)^3 / (9 * π * (α_va/ (1 - F_r) * D ^ p3.β_va)^2)) ^ κ * ai[i] * D^(bi[i]) * exp(- ci[i] * D) * (α_va/ (1 - F_r) * D ^ p3.β_va) * N_0 * D^μ * exp(-λ*D), th.D_cr, Inf) + v += I # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) - v += integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) + #v += integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) end end end @@ -544,62 +550,7 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, return v / q end -""" - terminal_velocity_number(p3, Chen2022, q, N, ρ_r, F_r) - - - p3 - a struct with P3 scheme parameters - - Chen 2022 - a struch with terminal velocity parameters as in Chen(2022) - - q - mass mixing ratio - - N - number mixing ratio - - ρ_r - rime density (q_rim/B_rim) [kg/m^3] - - F_r - rime mass fraction (q_rim/q_i) - - ρ_a - density of air - - Returns the number (total)-weighted fall speed - Eq C11 of Morrison and Milbrandt (2015) -""" -function terminal_velocity_number(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_r::FT, F_r::FT, ρ_a::FT) where{FT} - # Get the thresholds for different particles regimes - th = thresholds(p3, ρ_r, F_r) - D_th = D_th_helper(p3) - - # Get the shape parameters - (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) - μ = DSD_μ(p3, λ) - - # Get the ai, bi, ci constants (in si units) for velocity calculations - (; As, Bs, Cs, Es, Fs, Gs) = Chen2022 - - bi = [Bs + ρ_a * Cs, Bs + ρ_a * Cs] - ai = [Es * ρ_a^As * 10^(3 * bi[1]), Fs * ρ_a^As * 10^(3 * bi[2])] - ci = [0, Gs * 10^3] - - κ = FT(1/3) - - # Redefine α_va to be in si units - α_va = α_va_si(p3) - - # TODO Update the velocity to use a different formulation for D > 0.625 mm - v = 0 - for i in 1:2 - if F_r == 0 - v += integrate(FT(0), D_th, ai[i] * N_0, bi[i] + μ, ci[i] + λ) - v += integrate(D_th, Inf, ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) - else - v += integrate(FT(0), D_th, ai[i] * N_0, bi[i] + μ, ci[i] + λ) - v += integrate(D_th, th.D_gr, ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) - v += integrate(th.D_gr, th.D_cr, ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ, ci[i] + λ) - v += ( - integrate(th.D_cr, Inf, ai[i] * N_0 * (p3.γ * (1-F_r)) ^ (3 * κ), bi[i] + μ + κ*(3 * p3.σ), ci[i] + λ) + - integrate(th.D_cr, Inf, ai[i] * N_0 * (p3.γ ^ 2 * π * F_r * 3/4 * (1-F_r)^2) ^ κ, bi[i] + μ + κ*(2 + 2 * p3.σ), ci[i] + λ) + - integrate(th.D_cr, Inf, ai[i] * N_0 * (p3.γ * π ^ 2 * F_r ^ 2 * 3/16 * (1-F_r)) ^ κ, bi[i] + μ + κ*(4 + p3.σ), ci[i] + λ) + - integrate(th.D_cr, Inf, ai[i] * N_0 * (F_r^3 * π^3 / 64) ^ κ, bi[i] + μ + 6 * κ, ci[i] + λ) - ) - end - end - - return v / N -end +# TODO: add accurate number weighted velocity function that mimics the mass weighted one """ D_m (p3, q, N, ρ_r, F_r) diff --git a/test/Project.toml b/test/Project.toml index 453d1d3c6..233d678da 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -16,6 +16,7 @@ KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" MLJ = "add582a8-e3ab-11e8-2d5e-e98b27df1bc7" MLJFlux = "094fc8d1-fd35-5302-93ea-dabda2abf845" MLJModels = "d491faf4-2d78-11e9-2867-c94bc002c0b7" +QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" RootSolvers = "7181ea78-2dcb-4de3-ab41-2b8ab5a31e74" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" @@ -23,4 +24,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Thermodynamics = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c" [compat] -KernelAbstractions = "0.9" \ No newline at end of file +KernelAbstractions = "0.9" diff --git a/test/p3_tests.jl b/test/p3_tests.jl index 69163ec89..bc0278b48 100644 --- a/test/p3_tests.jl +++ b/test/p3_tests.jl @@ -135,6 +135,60 @@ function test_p3_shape_solver(FT) end end +function test_velocities(FT) + Chen2022 = CMP.Chen2022VelType(FT) + p3 = CMP.ParametersP3(FT) + q = FT(0.22) + N = FT(1e6) + ρ_a = FT(1.2) + ρ_rs = [FT(200), FT(400), FT(600), FT(800)] + F_rs = [FT(0.2), FT(0.4), FT(0.6), FT(0.8)] + + TT.@testset "Mass-weighted terminal velocities" begin + paper_vals = [[1.5, 1.5, 1.5, 1.5], [1.5, 2.5, 2.5, 2.5], [2.5, 2.5, 2.5, 2.5], [2.5, 3.5, 3.5, 3.5]] + for i in 1:length(ρ_rs) + for j in 1:length(F_rs) + ρ_r = ρ_rs[i] + F_r = F_rs[j] + + calculated_vel = P3.terminal_velocity_mass(p3, Chen2022.snow_ice, q, N, ρ_r, F_r, ρ_a) + + TT.@test calculated_vel > 0 + TT.@test paper_vals[i][j] ≈ calculated_vel atol = 3.14 + + end + end + end + + TT.@testset "Mass-weighted mean diameters" begin + paper_vals = [[5, 5, 5, 5], [4.5, 4.5, 4.5, 4.5], [3.5, 3.5, 3.5, 3.5], [3.5, 2.5, 2.5, 2.5]] + for i in 1:length(ρ_rs) + for j in 1:length(F_rs) + ρ_r = ρ_rs[i] + F_r = F_rs[j] + + calculated_dm = P3.D_m(p3, q, N, ρ_r, F_r) * 1e3 + + TT.@test calculated_dm > 0 + TT.@test paper_vals[i][j] ≈ calculated_dm atol = 3.14 + + end + end + end +end + +function test_neg_vel(FT) + Chen2022 = CMP.Chen2022VelType(FT) + p3 = CMP.ParametersP3(FT) + q = FT(0.0008) + N = FT(1e6) + ρ_r = FT(900) + F_r = FT(0.99) + + # Check for negative velocity + TT.@test P3.terminal_velocity_mass(p3, Chen2022.snow_ice, q, N, ρ_r, F_r, FT(1.2)) > 0 +end + println("Testing Float32") test_p3_thresholds(Float32) #TODO - only works for Float64 now. We should switch the units inside the solver @@ -144,3 +198,5 @@ test_p3_thresholds(Float32) println("Testing Float64") test_p3_thresholds(Float64) test_p3_shape_solver(Float64) +test_velocities(Float64) +test_neg_vel(Float64) # expected to fail From 2e864f9e88efe188f2d3d30b20c813c04ede638c Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Thu, 14 Mar 2024 19:10:19 -0400 Subject: [PATCH 20/33] format --- docs/src/plots/P3TerminalVelocityPlots.jl | 253 +++++++++++++++------- src/P3Scheme.jl | 228 +++++++++++++++---- test/p3_tests.jl | 46 +++- 3 files changed, 388 insertions(+), 139 deletions(-) diff --git a/docs/src/plots/P3TerminalVelocityPlots.jl b/docs/src/plots/P3TerminalVelocityPlots.jl index fd021770a..4c7d89537 100644 --- a/docs/src/plots/P3TerminalVelocityPlots.jl +++ b/docs/src/plots/P3TerminalVelocityPlots.jl @@ -6,26 +6,35 @@ import CairoMakie as Plt const PSP3 = CMP.ParametersP3 -FT = Float64 +FT = Float64 p3 = CMP.ParametersP3(FT) -function get_values(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_a::FT, x_resolution::Int, y_resolution::Int) where {FT} +function get_values( + p3::PSP3, + Chen2022::CMP.Chen2022VelTypeSnowIce, + q::FT, + N::FT, + ρ_a::FT, + x_resolution::Int, + y_resolution::Int, +) where {FT} F_rs = range(FT(0), stop = FT(1 - eps(FT)), length = x_resolution) ρ_rs = range(FT(25), stop = FT(975), length = y_resolution) V_m = zeros(x_resolution, y_resolution) D_m = zeros(x_resolution, y_resolution) - for i in 1:x_resolution - for j in 1:y_resolution + for i in 1:x_resolution + for j in 1:y_resolution F_r = F_rs[i] ρ_r = ρ_rs[j] - V_m[i, j] = P3.terminal_velocity_mass(p3, Chen2022, q, N, ρ_r, F_r, ρ_a) + V_m[i, j] = + P3.terminal_velocity_mass(p3, Chen2022, q, N, ρ_r, F_r, ρ_a) # get D_m in mm for plots D_m[i, j] = 1e3 * P3.D_m(p3, q, N, ρ_r, F_r) - end + end end return (; F_rs, ρ_rs, V_m, D_m) end @@ -43,81 +52,131 @@ function figure_2() # small D_m q_s = FT(0.0008) - N_s = FT(1e6) - + N_s = FT(1e6) + # medium D_m - q_m = FT(0.22) + q_m = FT(0.22) N_m = FT(1e6) # large D_m - q_l = FT(0.7) + q_l = FT(0.7) N_l = FT(1e6) - + # get V_m and D_m - (F_rs, ρ_rs, V_ms, D_ms) = get_values(p3, Chen2022.snow_ice, q_s, N_s, ρ_a, xres, yres) - (F_rm, ρ_rm, V_mm, D_mm) = get_values(p3, Chen2022.snow_ice, q_m, N_m, ρ_a, xres, yres) - (F_rl, ρ_rl, V_ml, D_ml) = get_values(p3, Chen2022.snow_ice, q_l, N_l, ρ_a, xres, yres) - - println("small q = ", q_s, ": min: ", minimum(d for d in D_ms if !isnan(d))," max: ", maximum(d for d in D_ms if !isnan(d))) - println("medium q = ", q_m, ": min: ", minimum(d for d in D_mm if !isnan(d))," max: ", maximum(d for d in D_mm if !isnan(d))) - println("large q = ", q_l, ": min: ", minimum(d for d in D_ml if !isnan(d))," max: ", maximum(d for d in D_ml if !isnan(d))) + (F_rs, ρ_rs, V_ms, D_ms) = + get_values(p3, Chen2022.snow_ice, q_s, N_s, ρ_a, xres, yres) + (F_rm, ρ_rm, V_mm, D_mm) = + get_values(p3, Chen2022.snow_ice, q_m, N_m, ρ_a, xres, yres) + (F_rl, ρ_rl, V_ml, D_ml) = + get_values(p3, Chen2022.snow_ice, q_l, N_l, ρ_a, xres, yres) + + println( + "small q = ", + q_s, + ": min: ", + minimum(d for d in D_ms if !isnan(d)), + " max: ", + maximum(d for d in D_ms if !isnan(d)), + ) + println( + "medium q = ", + q_m, + ": min: ", + minimum(d for d in D_mm if !isnan(d)), + " max: ", + maximum(d for d in D_mm if !isnan(d)), + ) + println( + "large q = ", + q_l, + ": min: ", + minimum(d for d in D_ml if !isnan(d)), + " max: ", + maximum(d for d in D_ml if !isnan(d)), + ) points = [0, 0.1, 0.2, 0.5, 1, 2, 3, 4, 5, 7.5, 10] labels = string.(points) - + ax1 = Plt.Axis( - f[1, 1], - xlabel = "F_r", - ylabel = "ρ_r", - title = "Small Dₘ", - width = 350, + f[1, 1], + xlabel = "F_r", + ylabel = "ρ_r", + title = "Small Dₘ", + width = 350, height = 350, - limits = (0, 1.0, 25, 975), + limits = (0, 1.0, 25, 975), xticks = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0], yticks = [200, 400, 600, 800], ) - Plt.contourf!(F_rs, ρ_rs, V_ms, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max), levels = points, extendhigh = :darkred) - Plt.contour!(F_rs, ρ_rs, D_ms, color = :black, labels = true, levels = 3,) + Plt.contourf!( + F_rs, + ρ_rs, + V_ms, + colormap = Plt.reverse(Plt.cgrad(:Spectral)), + colorrange = (min, max), + levels = points, + extendhigh = :darkred, + ) + Plt.contour!(F_rs, ρ_rs, D_ms, color = :black, labels = true, levels = 3) ax2 = Plt.Axis( - f[1, 2], - xlabel = "F_r", - ylabel = "ρ_r", - title = "Medium Dₘ", - width = 350, + f[1, 2], + xlabel = "F_r", + ylabel = "ρ_r", + title = "Medium Dₘ", + width = 350, height = 350, - limits = (0, 1.0, 25, 975), + limits = (0, 1.0, 25, 975), xticks = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0], yticks = [200, 400, 600, 800], ) - Plt.contourf!(F_rm, ρ_rm, V_mm, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max), levels = points, extendhigh = :darkred) + Plt.contourf!( + F_rm, + ρ_rm, + V_mm, + colormap = Plt.reverse(Plt.cgrad(:Spectral)), + colorrange = (min, max), + levels = points, + extendhigh = :darkred, + ) Plt.contour!(F_rm, ρ_rm, D_mm, color = :black, labels = true, levels = 3) ax3 = Plt.Axis( - f[1, 3], - xlabel = "F_r", - ylabel = "ρ_r", - title = "Large Dₘ", - width = 350, + f[1, 3], + xlabel = "F_r", + ylabel = "ρ_r", + title = "Large Dₘ", + width = 350, height = 350, - limits = (0, 1.0, 25, 975), + limits = (0, 1.0, 25, 975), xticks = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0], yticks = [200, 400, 600, 800], ) - Plt.contourf!(F_rl, ρ_rl, V_ml, colormap = Plt.reverse(Plt.cgrad(:Spectral)), colorrange = (min, max), levels = points, extendhigh = :darkred) + Plt.contourf!( + F_rl, + ρ_rl, + V_ml, + colormap = Plt.reverse(Plt.cgrad(:Spectral)), + colorrange = (min, max), + levels = points, + extendhigh = :darkred, + ) Plt.contour!(F_rl, ρ_rl, D_ml, color = :black, labels = true, levels = 3) Plt.Colorbar( - f[2, 2], - limits = (min, max), - colormap = Plt.reverse(Plt.cgrad(:Spectral, length(points), categorical = true)), + f[2, 2], + limits = (min, max), + colormap = Plt.reverse( + Plt.cgrad(:Spectral, length(points), categorical = true), + ), flipaxis = true, - vertical = false, - ticks = (points, labels), - highclip = :darkred + vertical = false, + ticks = (points, labels), + highclip = :darkred, ) Plt.linkxaxes!(ax1, ax2) @@ -126,47 +185,77 @@ function figure_2() Plt.linkyaxes!(ax2, ax3) # Attempt to plot D_m for clearer information than the contour plots and debugging - ax4 = Plt.Axis(f[3, 1], height = 350, width = 350, xlabel = "F_r", ylabel = "ρ_r", title = "Small Dₘ vs F_r and ρ_r") + ax4 = Plt.Axis( + f[3, 1], + height = 350, + width = 350, + xlabel = "F_r", + ylabel = "ρ_r", + title = "Small Dₘ vs F_r and ρ_r", + ) Plt.contourf!( - F_rs, - ρ_rs, + F_rs, + ρ_rs, D_ms, - colormap = Plt.reverse(Plt.cgrad(:Spectral)) + colormap = Plt.reverse(Plt.cgrad(:Spectral)), ) Plt.Colorbar( - f[4, 1], - limits = (minimum(d for d in D_ms if !isnan(d)), maximum(d for d in D_ms if !isnan(d))), + f[4, 1], + limits = ( + minimum(d for d in D_ms if !isnan(d)), + maximum(d for d in D_ms if !isnan(d)), + ), colormap = Plt.reverse(Plt.cgrad(:Spectral)), flipaxis = true, - vertical = false, + vertical = false, + ) + ax5 = Plt.Axis( + f[3, 2], + height = 350, + width = 350, + xlabel = "F_r", + ylabel = "ρ_r", + title = "Medium Dₘ vs F_r and ρ_r", ) - ax5 = Plt.Axis(f[3, 2], height = 350, width = 350, xlabel = "F_r", ylabel = "ρ_r", title = "Medium Dₘ vs F_r and ρ_r") Plt.contourf!( - F_rm, - ρ_rm, + F_rm, + ρ_rm, D_mm, - colormap = Plt.reverse(Plt.cgrad(:Spectral)) + colormap = Plt.reverse(Plt.cgrad(:Spectral)), ) Plt.Colorbar( - f[4, 2], - limits = (minimum(d for d in D_mm if !isnan(d)), maximum(d for d in D_mm if !isnan(d))), + f[4, 2], + limits = ( + minimum(d for d in D_mm if !isnan(d)), + maximum(d for d in D_mm if !isnan(d)), + ), colormap = Plt.reverse(Plt.cgrad(:Spectral)), flipaxis = true, - vertical = false, + vertical = false, + ) + ax6 = Plt.Axis( + f[3, 3], + height = 350, + width = 350, + xlabel = "F_r", + ylabel = "ρ_r", + title = "Large Dₘ vs F_r and ρ_r", ) - ax6 = Plt.Axis(f[3, 3], height = 350, width = 350, xlabel = "F_r", ylabel = "ρ_r", title = "Large Dₘ vs F_r and ρ_r") Plt.contourf!( - F_rl, - ρ_rl, + F_rl, + ρ_rl, D_ml, - colormap = Plt.reverse(Plt.cgrad(:Spectral)) + colormap = Plt.reverse(Plt.cgrad(:Spectral)), ) Plt.Colorbar( - f[4, 3], - limits = (minimum(d for d in D_ml if !isnan(d)), maximum(d for d in D_ml if !isnan(d))), + f[4, 3], + limits = ( + minimum(d for d in D_ml if !isnan(d)), + maximum(d for d in D_ml if !isnan(d)), + ), colormap = Plt.reverse(Plt.cgrad(:Spectral)), flipaxis = true, - vertical = false, + vertical = false, ) Plt.linkxaxes!(ax1, ax4) @@ -175,12 +264,12 @@ function figure_2() Plt.linkyaxes!(ax1, ax4) Plt.linkyaxes!(ax4, ax5) Plt.linkyaxes!(ax5, ax6) - - Plt.resize_to_layout!(f) + + Plt.resize_to_layout!(f) Plt.save("MorrisonandMilbrandtFig2.svg", f) end -println("start") +println("start") figure_2() println("done") #println(P3.D_th_helper(p3)) @@ -190,26 +279,26 @@ println("done") import RootSolvers as RS -ρ_r = FT(500) +ρ_r = FT(500) F_r = FT(0.8) -N = FT(1e8) +N = FT(1e8) Dₘ = 0.006 println("started") shape_problem(x) = Dₘ - P3.D_m(p3, exp(x), N, ρ_r, F_r) x = - RS.find_zero( - shape_problem, - RS.SecantMethod(log(0.01), log(0.015)), - RS.CompactSolution(), - RS.RelativeSolutionTolerance(eps(FT)), - 5, - ).root + RS.find_zero( + shape_problem, + RS.SecantMethod(log(0.01), log(0.015)), + RS.CompactSolution(), + RS.RelativeSolutionTolerance(eps(FT)), + 5, + ).root -println("q_solved = ", exp(x)) +println("q_solved = ", exp(x)) Chen2022 = CMP.Chen2022VelType(FT) q = FT(0.0008) N = FT(1e6) ρ_r = FT(900) F_r = FT(0.99) -P3.terminal_velocity_mass(p3, Chen2022.snow_ice, q, N, ρ_r, F_r, FT(1.293)) \ No newline at end of file +P3.terminal_velocity_mass(p3, Chen2022.snow_ice, q, N, ρ_r, F_r, FT(1.293)) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index a298ab04e..db8db7558 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -241,13 +241,13 @@ end Integrates the function c1 * D ^ (c2) * exp(-c3 * D) dD from a to b Returns the result """ -function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where{FT} +function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where {FT} if b == Inf - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3)) - elseif a == 0 - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2) - Γ(1 + c2, b * c3)) - else - return c1 * c3 ^ (-c2 - 1) * (Γ(1 + c2, a * c3) - Γ(1 + c2, b * c3)) + return c1 * c3^(-c2 - 1) * (Γ(1 + c2, a * c3)) + elseif a == 0 + return c1 * c3^(-c2 - 1) * (Γ(1 + c2) - Γ(1 + c2, b * c3)) + else + return c1 * c3^(-c2 - 1) * (Γ(1 + c2, a * c3) - Γ(1 + c2, b * c3)) end end @@ -267,11 +267,11 @@ Returns the coefficients for m(D), a(D), and the respective powers of D 5 - partially rimed ice 6 - second half of partially rimed ice (only for a) """ -function get_coeffs(p3::PSP3, th, F_r::FT) where{FT} +function get_coeffs(p3::PSP3, th, F_r::FT) where {FT} α_va = α_va_si(p3) - m = [π / 6 * p3.ρ_i, α_va, α_va, π / 6 * th.ρ_g, α_va / (1- F_r)] + m = [π / 6 * p3.ρ_i, α_va, α_va, π / 6 * th.ρ_g, α_va / (1 - F_r)] m_power = [FT(3), p3.β_va, p3.β_va, 3, p3.β_va] - a = [π/4, p3.γ, p3.γ, π/4, F_r * π/4, (1 - F_r) * p3.γ] + a = [π / 4, p3.γ, p3.γ, π / 4, F_r * π / 4, (1 - F_r) * p3.γ] a_power = [FT(2), p3.σ, p3.σ, FT(2), FT(2), p3.σ] return (; m, m_power, a, a_power) end @@ -448,8 +448,8 @@ end Returns the coefficient of aspect ratio (ignoring powers of D) """ -function ϕ_coeff(p3::PSP3, m::FT, a::FT) where{FT} - return 16 * p3.ρ_i ^ 2 * a ^ 3 / (9 * π^2 * m^2) +function ϕ_coeff(p3::PSP3, m::FT, a::FT) where {FT} + return 16 * p3.ρ_i^2 * a^3 / (9 * π^2 * m^2) end """ @@ -466,7 +466,15 @@ end Returns the mass (total)-weighted fall speed Eq C10 of Morrison and Milbrandt (2015) """ -function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, q::FT, N::FT, ρ_r::FT, F_r::FT, ρ_a::FT) where{FT} +function terminal_velocity_mass( + p3::PSP3, + Chen2022::CMP.Chen2022VelTypeSnowIce, + q::FT, + N::FT, + ρ_r::FT, + F_r::FT, + ρ_a::FT, +) where {FT} # Get the thresholds for different particles regimes th = thresholds(p3, ρ_r, F_r) @@ -480,66 +488,192 @@ function terminal_velocity_mass(p3::PSP3, Chen2022::CMP.Chen2022VelTypeSnowIce, # Get the ai, bi, ci constants (in si units) for velocity calculations (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) - κ = FT(-1/6) #FT(1/3) + κ = FT(-1 / 6) #FT(1/3) # Redefine α_va to be in si units α_va = α_va_si(p3) v = 0 for i in 1:2 - if F_r == 0 - v += integrate(FT(0), D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) - v += integrate(D_th, cutoff, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) - + if F_r == 0 + v += integrate( + FT(0), + D_th, + π / 6 * p3.ρ_i * ai[i] * N_0, + bi[i] + μ + 3, + ci[i] + λ, + ) + v += integrate( + D_th, + cutoff, + α_va * + ai[i] * + N_0 * + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * π * α_va^2))^κ, + bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), + ci[i] + λ, + ) + # Get velocity coefficients for large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) - v += integrate(cutoff, Inf, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) - else + v += integrate( + cutoff, + Inf, + α_va * + ai[i] * + N_0 * + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * π * α_va^2))^κ, + bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), + ci[i] + λ, + ) + else large = false - v += integrate(FT(0), D_th, π / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ) - + v += integrate( + FT(0), + D_th, + π / 6 * p3.ρ_i * ai[i] * N_0, + bi[i] + μ + 3, + ci[i] + λ, + ) + # D_th to D_gr - if !large && th.D_gr > cutoff - v += integrate(D_th, cutoff, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) - + if !large && th.D_gr > cutoff + v += integrate( + D_th, + cutoff, + α_va * + ai[i] * + N_0 * + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * π * α_va^2))^κ, + bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), + ci[i] + λ, + ) + # large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) - large = true + large = true - v += integrate(cutoff, th.D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + v += integrate( + cutoff, + th.D_gr, + α_va * + ai[i] * + N_0 * + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * π * α_va^2))^κ, + bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), + ci[i] + λ, + ) else - v += integrate(D_th, th.D_gr, α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * p3.γ^3/(9 * π * α_va ^ 2)) ^ κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ) + v += integrate( + D_th, + th.D_gr, + α_va * + ai[i] * + N_0 * + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * π * α_va^2))^κ, + bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), + ci[i] + λ, + ) end # D_gr to D_cr - if !large && th.D_cr > cutoff - v += integrate(th.D_gr, cutoff, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ + 3, ci[i] + λ) - + if !large && th.D_cr > cutoff + v += integrate( + th.D_gr, + cutoff, + π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), + bi[i] + μ + 3, + ci[i] + λ, + ) + # large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) - large = true + large = true - v += integrate(cutoff, th.D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ + 3, ci[i] + λ) - else - v += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ + 3, ci[i] + λ) + v += integrate( + cutoff, + th.D_cr, + π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), + bi[i] + μ + 3, + ci[i] + λ, + ) + else + v += integrate( + th.D_gr, + th.D_cr, + π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), + bi[i] + μ + 3, + ci[i] + λ, + ) end - + # D_cr to Infinity if !large - (I, e) = QGK.quadgk(D -> (16 * p3.ρ_i^2 * (F_r * π/4 * D^2 + (1-F_r) * p3.γ*D ^ p3.σ)^3 / (9 * π * (α_va/ (1 - F_r) * D ^ p3.β_va)^2)) ^ κ * ai[i] * D^(bi[i]) * exp(- ci[i] * D) * (α_va/ (1 - F_r) * D ^ p3.β_va) * N_0 * D^μ * exp(-λ*D), th.D_cr, cutoff) + (I, e) = QGK.quadgk( + D -> + ( + 16 * + p3.ρ_i^2 * + (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / + (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) + )^κ * + ai[i] * + D^(bi[i]) * + exp(-ci[i] * D) * + (α_va / (1 - F_r) * D^p3.β_va) * + N_0 * + D^μ * + exp(-λ * D), + th.D_cr, + cutoff, + ) v += I # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) #v += integrate(th.D_cr, cutoff, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) - + # large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) large = true - (I, e) = QGK.quadgk(D -> (16 * p3.ρ_i^2 * (F_r * π/4 * D^2 + (1-F_r) * p3.γ*D ^ p3.σ)^3 / (9 * π * (α_va/ (1 - F_r) * D ^ p3.β_va)^2)) ^ κ * ai[i] * D^(bi[i]) * exp(- ci[i] * D) * (α_va/ (1 - F_r) * D ^ p3.β_va) * N_0 * D^μ * exp(-λ*D), cutoff, Inf) + (I, e) = QGK.quadgk( + D -> + ( + 16 * + p3.ρ_i^2 * + (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / + (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) + )^κ * + ai[i] * + D^(bi[i]) * + exp(-ci[i] * D) * + (α_va / (1 - F_r) * D^p3.β_va) * + N_0 * + D^μ * + exp(-λ * D), + cutoff, + Inf, + ) v += I #v += integrate(cutoff, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) - else - (I, e) = QGK.quadgk(D -> (16 * p3.ρ_i^2 * (F_r * π/4 * D^2 + (1-F_r) * p3.γ*D ^ p3.σ)^3 / (9 * π * (α_va/ (1 - F_r) * D ^ p3.β_va)^2)) ^ κ * ai[i] * D^(bi[i]) * exp(- ci[i] * D) * (α_va/ (1 - F_r) * D ^ p3.β_va) * N_0 * D^μ * exp(-λ*D), th.D_cr, Inf) + else + (I, e) = QGK.quadgk( + D -> + ( + 16 * + p3.ρ_i^2 * + (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / + (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) + )^κ * + ai[i] * + D^(bi[i]) * + exp(-ci[i] * D) * + (α_va / (1 - F_r) * D^p3.β_va) * + N_0 * + D^μ * + exp(-λ * D), + th.D_cr, + Inf, + ) v += I # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) #v += integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) @@ -576,19 +710,19 @@ function D_m(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} α_va = α_va_si(p3) # Calculate numerator - n = 0 - if F_r == 0 + n = 0 + if F_r == 0 n += integrate(FT(0), D_th, π / 6 * p3.ρ_i * N_0, μ + 4, λ) n += integrate(D_th, Inf, α_va * N_0, μ + p3.β_va + 1, λ) - else - n += integrate(FT(0), D_th, π / 6 * p3.ρ_i * N_0, μ + 4, λ) + else + n += integrate(FT(0), D_th, π / 6 * p3.ρ_i * N_0, μ + 4, λ) n += integrate(D_th, th.D_gr, α_va * N_0, μ + p3.β_va + 1, λ) - n += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * N_0, μ + 4, λ) + n += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * N_0, μ + 4, λ) n += integrate(th.D_cr, Inf, α_va / (1 - F_r) * N_0, μ + p3.β_va + 1, λ) end - + # Normalize by q - return n/q + return n / q end diff --git a/test/p3_tests.jl b/test/p3_tests.jl index bc0278b48..ac3a09614 100644 --- a/test/p3_tests.jl +++ b/test/p3_tests.jl @@ -138,20 +138,33 @@ end function test_velocities(FT) Chen2022 = CMP.Chen2022VelType(FT) p3 = CMP.ParametersP3(FT) - q = FT(0.22) + q = FT(0.22) N = FT(1e6) - ρ_a = FT(1.2) + ρ_a = FT(1.2) ρ_rs = [FT(200), FT(400), FT(600), FT(800)] F_rs = [FT(0.2), FT(0.4), FT(0.6), FT(0.8)] TT.@testset "Mass-weighted terminal velocities" begin - paper_vals = [[1.5, 1.5, 1.5, 1.5], [1.5, 2.5, 2.5, 2.5], [2.5, 2.5, 2.5, 2.5], [2.5, 3.5, 3.5, 3.5]] + paper_vals = [ + [1.5, 1.5, 1.5, 1.5], + [1.5, 2.5, 2.5, 2.5], + [2.5, 2.5, 2.5, 2.5], + [2.5, 3.5, 3.5, 3.5], + ] for i in 1:length(ρ_rs) - for j in 1:length(F_rs) + for j in 1:length(F_rs) ρ_r = ρ_rs[i] F_r = F_rs[j] - calculated_vel = P3.terminal_velocity_mass(p3, Chen2022.snow_ice, q, N, ρ_r, F_r, ρ_a) + calculated_vel = P3.terminal_velocity_mass( + p3, + Chen2022.snow_ice, + q, + N, + ρ_r, + F_r, + ρ_a, + ) TT.@test calculated_vel > 0 TT.@test paper_vals[i][j] ≈ calculated_vel atol = 3.14 @@ -160,10 +173,15 @@ function test_velocities(FT) end end - TT.@testset "Mass-weighted mean diameters" begin - paper_vals = [[5, 5, 5, 5], [4.5, 4.5, 4.5, 4.5], [3.5, 3.5, 3.5, 3.5], [3.5, 2.5, 2.5, 2.5]] + TT.@testset "Mass-weighted mean diameters" begin + paper_vals = [ + [5, 5, 5, 5], + [4.5, 4.5, 4.5, 4.5], + [3.5, 3.5, 3.5, 3.5], + [3.5, 2.5, 2.5, 2.5], + ] for i in 1:length(ρ_rs) - for j in 1:length(F_rs) + for j in 1:length(F_rs) ρ_r = ρ_rs[i] F_r = F_rs[j] @@ -177,7 +195,7 @@ function test_velocities(FT) end end -function test_neg_vel(FT) +function test_neg_vel(FT) Chen2022 = CMP.Chen2022VelType(FT) p3 = CMP.ParametersP3(FT) q = FT(0.0008) @@ -186,7 +204,15 @@ function test_neg_vel(FT) F_r = FT(0.99) # Check for negative velocity - TT.@test P3.terminal_velocity_mass(p3, Chen2022.snow_ice, q, N, ρ_r, F_r, FT(1.2)) > 0 + TT.@test P3.terminal_velocity_mass( + p3, + Chen2022.snow_ice, + q, + N, + ρ_r, + F_r, + FT(1.2), + ) > 0 end println("Testing Float32") From 429e40b3e0d82cd4eb6ac2f7df279f3df332ae1d Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Fri, 15 Mar 2024 14:02:21 -0400 Subject: [PATCH 21/33] deleted unused func --- src/P3Scheme.jl | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index db8db7558..1416e9e69 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -440,18 +440,6 @@ function distribution_parameter_solver( return (; λ = exp(x), N_0 = DSD_N₀(p3, N, exp(x))) end -""" - ϕ_coeff(m, a) - - - m - coefficient of mass - - a - coefficient of mass - - Returns the coefficient of aspect ratio (ignoring powers of D) -""" -function ϕ_coeff(p3::PSP3, m::FT, a::FT) where {FT} - return 16 * p3.ρ_i^2 * a^3 / (9 * π^2 * m^2) -end - """ terminal_velocity_mass(p3, Chen2022, q, N, ρ_r, F_r) From bcd9ae2cda10314e147e4f898d8cad71d8aff4e3 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Mon, 8 Apr 2024 12:54:27 -0400 Subject: [PATCH 22/33] velocities are positive! --- docs/src/plots/P3TerminalVelocityPlots.jl | 27 ++++++++++++++++------- src/P3Scheme.jl | 20 +++++++++++++---- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/docs/src/plots/P3TerminalVelocityPlots.jl b/docs/src/plots/P3TerminalVelocityPlots.jl index 4c7d89537..e82175f07 100644 --- a/docs/src/plots/P3TerminalVelocityPlots.jl +++ b/docs/src/plots/P3TerminalVelocityPlots.jl @@ -42,7 +42,7 @@ end function figure_2() Chen2022 = CMP.Chen2022VelType(FT) # density of air in kg/m^3 - ρ_a = FT(1.293) + ρ_a = FT(1.2) #FT(1.293) f = Plt.Figure() xres = 100 @@ -269,16 +269,16 @@ function figure_2() Plt.save("MorrisonandMilbrandtFig2.svg", f) end -println("start") +#println("start") figure_2() -println("done") +#println("done") #println(P3.D_th_helper(p3)) #println(P3.thresholds(p3, FT(500), FT(0.5))) #println("") #println("small = ", P3.q_gamma(p3, FT(0.5), FT(1e7), FT(log(4.9 * 10^2)), P3.thresholds(p3, FT(500), FT(0.5)))) -import RootSolvers as RS +#= import RootSolvers as RS ρ_r = FT(500) F_r = FT(0.8) N = FT(1e8) @@ -294,11 +294,22 @@ x = 5, ).root -println("q_solved = ", exp(x)) +println("q_solved = ", exp(x)) =# Chen2022 = CMP.Chen2022VelType(FT) q = FT(0.0008) N = FT(1e6) -ρ_r = FT(900) -F_r = FT(0.99) -P3.terminal_velocity_mass(p3, Chen2022.snow_ice, q, N, ρ_r, F_r, FT(1.293)) +ρ_r = FT(950) +F_r = FT(0.95) + +(λ, N_0) = P3.distribution_parameter_solver(p3, q, N, ρ_r, F_r) +println("λ = ", λ, " N_0 = ", N_0) + +D_m = P3.D_m(p3, q, N, ρ_r, F_r) +println("D_m = ", D_m) + +println(P3.D_th_helper(p3)) + +println(P3.thresholds(p3, ρ_r, F_r)) + +P3.terminal_velocity_mass(p3, Chen2022.snow_ice, q, N, ρ_r, F_r, FT(1.2)) #FT(1.293)) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 1416e9e69..5a5f3a48e 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -467,15 +467,12 @@ function terminal_velocity_mass( # Get the thresholds for different particles regimes th = thresholds(p3, ρ_r, F_r) D_th = D_th_helper(p3) - cutoff = FT(0.000625) # TO be added to the struct + cutoff = FT(0.000625) # TODO add to the struct # Get the shape parameters (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) μ = DSD_μ(p3, λ) - # Get the ai, bi, ci constants (in si units) for velocity calculations - (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) - κ = FT(-1 / 6) #FT(1/3) # Redefine α_va to be in si units @@ -484,6 +481,8 @@ function terminal_velocity_mass( v = 0 for i in 1:2 if F_r == 0 + # Velocity coefficients for small particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) v += integrate( FT(0), D_th, @@ -515,7 +514,10 @@ function terminal_velocity_mass( ci[i] + λ, ) else + # Velocity coefficients for small particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) large = false + v += integrate( FT(0), D_th, @@ -536,6 +538,7 @@ function terminal_velocity_mass( bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ, ) + #= println("D_th to cutoff") =# # large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) @@ -551,6 +554,7 @@ function terminal_velocity_mass( bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ, ) + #= println("large, cutoff to D_gr") =# else v += integrate( D_th, @@ -562,6 +566,7 @@ function terminal_velocity_mass( bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ, ) + #= println("D_th to D_gr") =# end # D_gr to D_cr @@ -573,6 +578,7 @@ function terminal_velocity_mass( bi[i] + μ + 3, ci[i] + λ, ) + #= println("D_gr to cutoff") =# # large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) @@ -585,6 +591,7 @@ function terminal_velocity_mass( bi[i] + μ + 3, ci[i] + λ, ) + #= println("large, cutoff to D_cr") =# else v += integrate( th.D_gr, @@ -593,6 +600,7 @@ function terminal_velocity_mass( bi[i] + μ + 3, ci[i] + λ, ) + #= printlln("D_gr to D_cr") =# end # D_cr to Infinity @@ -616,6 +624,8 @@ function terminal_velocity_mass( cutoff, ) v += I + #= println("D_cr to cutoff") =# + # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) #v += integrate(th.D_cr, cutoff, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) @@ -642,6 +652,7 @@ function terminal_velocity_mass( Inf, ) v += I + #= println("large, cutoff to Infinity") =# #v += integrate(cutoff, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) else (I, e) = QGK.quadgk( @@ -663,6 +674,7 @@ function terminal_velocity_mass( Inf, ) v += I + #= println("D_cr to Infinity") =# # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) #v += integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) end From 0f5ad87ccc9da8e108e5aabbd477c240d4846e99 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Mon, 8 Apr 2024 13:35:03 -0400 Subject: [PATCH 23/33] plot edits --- docs/src/plots/P3TerminalVelocityPlots.jl | 45 +---------------------- 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/docs/src/plots/P3TerminalVelocityPlots.jl b/docs/src/plots/P3TerminalVelocityPlots.jl index e82175f07..98c35645b 100644 --- a/docs/src/plots/P3TerminalVelocityPlots.jl +++ b/docs/src/plots/P3TerminalVelocityPlots.jl @@ -269,47 +269,4 @@ function figure_2() Plt.save("MorrisonandMilbrandtFig2.svg", f) end -#println("start") -figure_2() -#println("done") -#println(P3.D_th_helper(p3)) -#println(P3.thresholds(p3, FT(500), FT(0.5))) -#println("") -#println("small = ", P3.q_gamma(p3, FT(0.5), FT(1e7), FT(log(4.9 * 10^2)), P3.thresholds(p3, FT(500), FT(0.5)))) - - -#= import RootSolvers as RS -ρ_r = FT(500) -F_r = FT(0.8) -N = FT(1e8) -Dₘ = 0.006 -println("started") -shape_problem(x) = Dₘ - P3.D_m(p3, exp(x), N, ρ_r, F_r) -x = - RS.find_zero( - shape_problem, - RS.SecantMethod(log(0.01), log(0.015)), - RS.CompactSolution(), - RS.RelativeSolutionTolerance(eps(FT)), - 5, - ).root - -println("q_solved = ", exp(x)) =# - -Chen2022 = CMP.Chen2022VelType(FT) -q = FT(0.0008) -N = FT(1e6) -ρ_r = FT(950) -F_r = FT(0.95) - -(λ, N_0) = P3.distribution_parameter_solver(p3, q, N, ρ_r, F_r) -println("λ = ", λ, " N_0 = ", N_0) - -D_m = P3.D_m(p3, q, N, ρ_r, F_r) -println("D_m = ", D_m) - -println(P3.D_th_helper(p3)) - -println(P3.thresholds(p3, ρ_r, F_r)) - -P3.terminal_velocity_mass(p3, Chen2022.snow_ice, q, N, ρ_r, F_r, FT(1.2)) #FT(1.293)) +figure_2() \ No newline at end of file From 8ec4d13bff1f07ce86bc4e4039ae8d5b449a0a4d Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Mon, 8 Apr 2024 13:36:10 -0400 Subject: [PATCH 24/33] figure --- docs/src/plots/P3TerminalVelocityPlots.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/plots/P3TerminalVelocityPlots.jl b/docs/src/plots/P3TerminalVelocityPlots.jl index 98c35645b..f99dbe352 100644 --- a/docs/src/plots/P3TerminalVelocityPlots.jl +++ b/docs/src/plots/P3TerminalVelocityPlots.jl @@ -269,4 +269,5 @@ function figure_2() Plt.save("MorrisonandMilbrandtFig2.svg", f) end +# Terminal Velocity figure figure_2() \ No newline at end of file From 6e275d7241e31743572e34a3f2034b0147eb1c9b Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Mon, 8 Apr 2024 16:42:54 -0400 Subject: [PATCH 25/33] aqua tests passing? --- Project.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 5e67b04e9..a248f4cb0 100644 --- a/Project.toml +++ b/Project.toml @@ -30,7 +30,8 @@ DocStringExtensions = "0.8, 0.9" EnsembleKalmanProcesses = "1.1.5" ForwardDiff = "0.10" MLJ = "0.20" +QuadGK = "2.9.4" RootSolvers = "0.3, 0.4" SpecialFunctions = "1, 2" Thermodynamics = "0.12.4" -julia = "1.6" +julia = "1.6" \ No newline at end of file From 001b7182d2ac6ccf13923616d64cb5b92a136ff4 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Tue, 9 Apr 2024 13:52:14 -0400 Subject: [PATCH 26/33] clean up --- docs/src/plots/P3TerminalVelocityPlots.jl | 74 ++++++++------------- src/P3Scheme.jl | 81 ++++++++--------------- test/p3_tests.jl | 18 ++--- test/performance_tests.jl | 16 +++++ 4 files changed, 80 insertions(+), 109 deletions(-) diff --git a/docs/src/plots/P3TerminalVelocityPlots.jl b/docs/src/plots/P3TerminalVelocityPlots.jl index f99dbe352..71fcabf15 100644 --- a/docs/src/plots/P3TerminalVelocityPlots.jl +++ b/docs/src/plots/P3TerminalVelocityPlots.jl @@ -70,34 +70,6 @@ function figure_2() (F_rl, ρ_rl, V_ml, D_ml) = get_values(p3, Chen2022.snow_ice, q_l, N_l, ρ_a, xres, yres) - println( - "small q = ", - q_s, - ": min: ", - minimum(d for d in D_ms if !isnan(d)), - " max: ", - maximum(d for d in D_ms if !isnan(d)), - ) - println( - "medium q = ", - q_m, - ": min: ", - minimum(d for d in D_mm if !isnan(d)), - " max: ", - maximum(d for d in D_mm if !isnan(d)), - ) - println( - "large q = ", - q_l, - ": min: ", - minimum(d for d in D_ml if !isnan(d)), - " max: ", - maximum(d for d in D_ml if !isnan(d)), - ) - - points = [0, 0.1, 0.2, 0.5, 1, 2, 3, 4, 5, 7.5, 10] - labels = string.(points) - ax1 = Plt.Axis( f[1, 1], xlabel = "F_r", @@ -115,11 +87,18 @@ function figure_2() ρ_rs, V_ms, colormap = Plt.reverse(Plt.cgrad(:Spectral)), - colorrange = (min, max), - levels = points, - extendhigh = :darkred, ) Plt.contour!(F_rs, ρ_rs, D_ms, color = :black, labels = true, levels = 3) + Plt.Colorbar( + f[2, 1], + limits = ( + minimum(v for v in V_ms if !isnan(v)), + maximum(v for v in V_ms if !isnan(v)), + ), + colormap = Plt.reverse(Plt.cgrad(:Spectral)), + flipaxis = true, + vertical = false, + ) ax2 = Plt.Axis( f[1, 2], @@ -138,11 +117,18 @@ function figure_2() ρ_rm, V_mm, colormap = Plt.reverse(Plt.cgrad(:Spectral)), - colorrange = (min, max), - levels = points, - extendhigh = :darkred, ) Plt.contour!(F_rm, ρ_rm, D_mm, color = :black, labels = true, levels = 3) + Plt.Colorbar( + f[2, 2], + limits = ( + minimum(v for v in V_mm if !isnan(v)), + maximum(v for v in V_mm if !isnan(v)), + ), + colormap = Plt.reverse(Plt.cgrad(:Spectral)), + flipaxis = true, + vertical = false, + ) ax3 = Plt.Axis( f[1, 3], @@ -161,29 +147,25 @@ function figure_2() ρ_rl, V_ml, colormap = Plt.reverse(Plt.cgrad(:Spectral)), - colorrange = (min, max), - levels = points, - extendhigh = :darkred, ) Plt.contour!(F_rl, ρ_rl, D_ml, color = :black, labels = true, levels = 3) - Plt.Colorbar( - f[2, 2], - limits = (min, max), - colormap = Plt.reverse( - Plt.cgrad(:Spectral, length(points), categorical = true), + f[2, 3], + limits = ( + minimum(v for v in V_ml if !isnan(v)), + maximum(v for v in V_ml if !isnan(v)), ), + colormap = Plt.reverse(Plt.cgrad(:Spectral)), flipaxis = true, vertical = false, - ticks = (points, labels), - highclip = :darkred, ) Plt.linkxaxes!(ax1, ax2) Plt.linkxaxes!(ax2, ax3) Plt.linkyaxes!(ax1, ax2) Plt.linkyaxes!(ax2, ax3) - # Attempt to plot D_m for clearer information than the contour plots and debugging + + # Plot D_m as second row of comparisons ax4 = Plt.Axis( f[3, 1], @@ -270,4 +252,4 @@ function figure_2() end # Terminal Velocity figure -figure_2() \ No newline at end of file +figure_2() diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 5a5f3a48e..6bca4ea95 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -251,31 +251,6 @@ function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where {FT} end end -""" - get_coeffs(p3, th) - - - p3 - a struct with P3 scheme parameters - - th - thresholds tuple as returned by thresholds() - - F_r - rime mass fraction [q_rim/q_i] - -Returns the coefficients for m(D), a(D), and the respective powers of D - Where the indices are as follows: - 1 - small, spherical ice - 2 - large, unrimed ice - 3 - dense, nonspherical ice - 4 - graupel - 5 - partially rimed ice - 6 - second half of partially rimed ice (only for a) -""" -function get_coeffs(p3::PSP3, th, F_r::FT) where {FT} - α_va = α_va_si(p3) - m = [π / 6 * p3.ρ_i, α_va, α_va, π / 6 * th.ρ_g, α_va / (1 - F_r)] - m_power = [FT(3), p3.β_va, p3.β_va, 3, p3.β_va] - a = [π / 4, p3.γ, p3.γ, π / 4, F_r * π / 4, (1 - F_r) * p3.γ] - a_power = [FT(2), p3.σ, p3.σ, FT(2), FT(2), p3.σ] - return (; m, m_power, a, a_power) -end - """ q_(p3, ρ, F_r, λ, μ, D_min, D_max) @@ -473,6 +448,7 @@ function terminal_velocity_mass( (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) μ = DSD_μ(p3, λ) + # TO DO: Change when each value used depending on type of particle κ = FT(-1 / 6) #FT(1/3) # Redefine α_va to be in si units @@ -482,11 +458,11 @@ function terminal_velocity_mass( for i in 1:2 if F_r == 0 # Velocity coefficients for small particles - (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) + (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) v += integrate( FT(0), D_th, - π / 6 * p3.ρ_i * ai[i] * N_0, + FT(π) / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ, ) @@ -496,7 +472,7 @@ function terminal_velocity_mass( α_va * ai[i] * N_0 * - (16 * p3.ρ_i^2 * p3.γ^3 / (9 * π * α_va^2))^κ, + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ, ) @@ -509,7 +485,7 @@ function terminal_velocity_mass( α_va * ai[i] * N_0 * - (16 * p3.ρ_i^2 * p3.γ^3 / (9 * π * α_va^2))^κ, + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ, ) @@ -521,7 +497,7 @@ function terminal_velocity_mass( v += integrate( FT(0), D_th, - π / 6 * p3.ρ_i * ai[i] * N_0, + FT(π) / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ, ) @@ -534,13 +510,12 @@ function terminal_velocity_mass( α_va * ai[i] * N_0 * - (16 * p3.ρ_i^2 * p3.γ^3 / (9 * π * α_va^2))^κ, + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ, ) - #= println("D_th to cutoff") =# - # large particles + # Switch to large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) large = true @@ -550,11 +525,10 @@ function terminal_velocity_mass( α_va * ai[i] * N_0 * - (16 * p3.ρ_i^2 * p3.γ^3 / (9 * π * α_va^2))^κ, + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ, ) - #= println("large, cutoff to D_gr") =# else v += integrate( D_th, @@ -562,11 +536,10 @@ function terminal_velocity_mass( α_va * ai[i] * N_0 * - (16 * p3.ρ_i^2 * p3.γ^3 / (9 * π * α_va^2))^κ, + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, bi[i] + μ + p3.β_va + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ, ) - #= println("D_th to D_gr") =# end # D_gr to D_cr @@ -574,33 +547,42 @@ function terminal_velocity_mass( v += integrate( th.D_gr, cutoff, - π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), + FT(π) / 6 * + th.ρ_g * + ai[i] * + N_0 * + (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ + 3, ci[i] + λ, ) - #= println("D_gr to cutoff") =# - # large particles + # Switch to large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) large = true v += integrate( cutoff, th.D_cr, - π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), + FT(π) / 6 * + th.ρ_g * + ai[i] * + N_0 * + (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ + 3, ci[i] + λ, ) - #= println("large, cutoff to D_cr") =# else v += integrate( th.D_gr, th.D_cr, - π / 6 * th.ρ_g * ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), + FT(π) / 6 * + th.ρ_g * + ai[i] * + N_0 * + (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ + 3, ci[i] + λ, ) - #= printlln("D_gr to D_cr") =# end # D_cr to Infinity @@ -624,12 +606,8 @@ function terminal_velocity_mass( cutoff, ) v += I - #= println("D_cr to cutoff") =# - - # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) - #v += integrate(th.D_cr, cutoff, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) - # large particles + # Switch to large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) large = true @@ -652,8 +630,6 @@ function terminal_velocity_mass( Inf, ) v += I - #= println("large, cutoff to Infinity") =# - #v += integrate(cutoff, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) else (I, e) = QGK.quadgk( D -> @@ -674,9 +650,6 @@ function terminal_velocity_mass( Inf, ) v += I - #= println("D_cr to Infinity") =# - # approximating sigma as 2 (for closed form integration) (this overestimates A LOT) - #v += integrate(th.D_cr, Inf, 1/(1 - F_r) * α_va * ai[i] * N_0 * (16 * p3.ρ_i ^ 2 * (F_r * π / 4 + (1 - F_r) * p3.γ)^3 / (9 * π * (1/(1 - F_r) * α_va) ^ 2)) ^ (κ), bi[i] + μ + p3.β_va + κ*(6 - 2 * p3.β_va), ci[i] + λ) end end end diff --git a/test/p3_tests.jl b/test/p3_tests.jl index ac3a09614..dcc0f29a0 100644 --- a/test/p3_tests.jl +++ b/test/p3_tests.jl @@ -142,14 +142,14 @@ function test_velocities(FT) N = FT(1e6) ρ_a = FT(1.2) ρ_rs = [FT(200), FT(400), FT(600), FT(800)] - F_rs = [FT(0.2), FT(0.4), FT(0.6), FT(0.8)] + F_rs = [FT(0), FT(0.2), FT(0.4), FT(0.6), FT(0.8)] TT.@testset "Mass-weighted terminal velocities" begin paper_vals = [ - [1.5, 1.5, 1.5, 1.5], - [1.5, 2.5, 2.5, 2.5], - [2.5, 2.5, 2.5, 2.5], - [2.5, 3.5, 3.5, 3.5], + [1.5, 1.5, 1.5, 1.5, 1.5], + [1.5, 1.5, 2.5, 2.5, 2.5], + [1.5, 2.5, 2.5, 2.5, 2.5], + [1.5, 2.5, 3.5, 3.5, 3.5], ] for i in 1:length(ρ_rs) for j in 1:length(F_rs) @@ -175,10 +175,10 @@ function test_velocities(FT) TT.@testset "Mass-weighted mean diameters" begin paper_vals = [ - [5, 5, 5, 5], - [4.5, 4.5, 4.5, 4.5], - [3.5, 3.5, 3.5, 3.5], - [3.5, 2.5, 2.5, 2.5], + [5, 5, 5, 5, 5], + [4.5, 4.5, 4.5, 4.5, 4.5], + [3.5, 3.5, 3.5, 3.5, 3.5], + [3.5, 3.5, 2.5, 2.5, 2.5], ] for i in 1:length(ρ_rs) for j in 1:length(F_rs) diff --git a/test/performance_tests.jl b/test/performance_tests.jl index cbe34b7e3..25c13af00 100644 --- a/test/performance_tests.jl +++ b/test/performance_tests.jl @@ -86,6 +86,7 @@ function benchmark_test(FT) ρ_r = FT(400.0) F_r = FT(0.95) + N = FT(1e8) T_air_2 = FT(250) T_air_cold = FT(230) @@ -121,6 +122,21 @@ function benchmark_test(FT) # P3 scheme bench_press(P3.thresholds, (p3, ρ_r, F_r), 12e6, 2048, 80) + if FT == Float64 + bench_press( + P3.distribution_parameter_solver, + (p3, q_ice, N, ρ_r, F_r), + 1e5, + ) + bench_press( + P3.terminal_velocity_mass, + (p3, ch2022.snow_ice, q_ice, N, ρ_r, F_r, ρ_air), + 2e6, + 4e4, + 2e3, + ) + bench_press(P3.D_m, (p3, q_ice, N, ρ_r, F_r), 1e5) + end # aerosol activation bench_press( From bcf27304b9ed1e8196d7208785837c5333bc8c00 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Wed, 10 Apr 2024 13:15:16 -0400 Subject: [PATCH 27/33] docs fixed? --- docs/src/plots/P3SchemePlots.jl | 90 ++++++++++++++++----------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/docs/src/plots/P3SchemePlots.jl b/docs/src/plots/P3SchemePlots.jl index 97e1526d4..17523d802 100644 --- a/docs/src/plots/P3SchemePlots.jl +++ b/docs/src/plots/P3SchemePlots.jl @@ -132,36 +132,36 @@ function define_axis(fig, row_range, col_range, title, ylabel, yticks, aspect) sol_8 = P3.thresholds(p3, 400.0, 0.8) #! format: off - fig1_a_0 = Plt.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.0 ) for D in D_range], color = cl[1], linewidth = lw) - fig1_a_5 = Plt.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.5, sol_5) for D in D_range], color = cl[2], linewidth = lw) - fig1_a_8 = Plt.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.8, sol_8) for D in D_range], color = cl[3], linewidth = lw) + fig1_a_0 = CMK.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.0 ) for D in D_range], color = cl[1], linewidth = lw) + fig1_a_5 = CMK.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.5, sol_5) for D in D_range], color = cl[2], linewidth = lw) + fig1_a_8 = CMK.lines!(ax1_a, D_range * 1e3, [p3_mass(p3, D, 0.8, sol_8) for D in D_range], color = cl[3], linewidth = lw) - d_tha = Plt.vlines!(ax1_a, P3.D_th_helper(p3) * 1e3, linestyle = :dash, color = cl[4], linewidth = lw) - d_cr_5 = Plt.vlines!(ax1_a, sol_5[1] * 1e3, linestyle = :dot, color = cl[2], linewidth = lw) - d_cr_8 = Plt.vlines!(ax1_a, sol_8[1] * 1e3, linestyle = :dot, color = cl[3], linewidth = lw) - d_gr_5 = Plt.vlines!(ax1_a, sol_5[2] * 1e3, linestyle = :dash, color = cl[2], linewidth = lw) - d_gr_8 = Plt.vlines!(ax1_a, sol_8[2] * 1e3, linestyle = :dash, color = cl[3], linewidth = lw) + d_tha = CMK.vlines!(ax1_a, P3.D_th_helper(p3) * 1e3, linestyle = :dash, color = cl[4], linewidth = lw) + d_cr_5 = CMK.vlines!(ax1_a, sol_5[1] * 1e3, linestyle = :dot, color = cl[2], linewidth = lw) + d_cr_8 = CMK.vlines!(ax1_a, sol_8[1] * 1e3, linestyle = :dot, color = cl[3], linewidth = lw) + d_gr_5 = CMK.vlines!(ax1_a, sol_5[2] * 1e3, linestyle = :dash, color = cl[2], linewidth = lw) + d_gr_8 = CMK.vlines!(ax1_a, sol_8[2] * 1e3, linestyle = :dash, color = cl[3], linewidth = lw) - leg1_a = Plt.Legend(fig1_a[8:9, 1], [fig1_a_0, fig1_a_5, fig1_a_8], [Plt.L"$F_{r} = 0.0$", Plt.L"$F_{r} = 0.5$", Plt.L"$F_{r} = 0.8$"], framevisible = false) - leg1_a_dth = Plt.Legend(fig1_a[8:9, 3], [d_tha], [Plt.L"$D_{th}$"], framevisible = false) - leg1_a_dcr = Plt.Legend(fig1_a[8:9, 7], [d_cr_5, d_cr_8], [Plt.L"$D_{cr}$ for $F_{r} = 0.5$", Plt.L"$D_{cr}$ for $F_{r} = 0.8$"], framevisible = false) - leg1_a_dgr = Plt.Legend(fig1_a[8:9, 5], [d_gr_5, d_gr_8], [Plt.L"$D_{gr}$ for $F_{r} = 0.5$", Plt.L"$D_{gr}$ for $F_{r} = 0.8$"], framevisible = false) + leg1_a = CMK.Legend(fig1_a[8:9, 1], [fig1_a_0, fig1_a_5, fig1_a_8], [CMK.L"$F_{r} = 0.0$", CMK.L"$F_{r} = 0.5$", CMK.L"$F_{r} = 0.8$"], framevisible = false) + leg1_a_dth = CMK.Legend(fig1_a[8:9, 3], [d_tha], [CMK.L"$D_{th}$"], framevisible = false) + leg1_a_dcr = CMK.Legend(fig1_a[8:9, 7], [d_cr_5, d_cr_8], [CMK.L"$D_{cr}$ for $F_{r} = 0.5$", CMK.L"$D_{cr}$ for $F_{r} = 0.8$"], framevisible = false) + leg1_a_dgr = CMK.Legend(fig1_a[8:9, 5], [d_gr_5, d_gr_8], [CMK.L"$D_{gr}$ for $F_{r} = 0.5$", CMK.L"$D_{gr}$ for $F_{r} = 0.8$"], framevisible = false) #! format: on - Plt.save("MorrisonandMilbrandtFig1a.svg", fig1_a) + CMK.save("MorrisonandMilbrandtFig1a.svg", fig1_a) - fig1_b = Plt.Figure() - ax1_b = Plt.Axis( + fig1_b = CMK.Figure() + ax1_b = CMK.Axis( fig1_b[1:10, 1:11], - title = Plt.L"m(D) regime for $F_r = 0.95$", - xlabel = Plt.L"$D$ (mm)", - ylabel = Plt.L"$m$ (kg)", - xscale = Plt.log10, - yscale = Plt.log10, + title = CMK.L"m(D) regime for $F_r = 0.95$", + xlabel = CMK.L"$D$ (mm)", + ylabel = CMK.L"$m$ (kg)", + xscale = CMK.log10, + yscale = CMK.log10, yminorticksvisible = true, - yminorticks = Plt.IntervalsBetween(3), + yminorticks = CMK.IntervalsBetween(3), xminorticksvisible = true, - xminorticks = Plt.IntervalsBetween(5), + xminorticks = CMK.IntervalsBetween(5), xticks = [0.01, 0.1, 1, 10], aspect = 1.67, limits = ((0.02, 10.0), nothing), @@ -172,25 +172,25 @@ function define_axis(fig, row_range, col_range, title, ylabel, yticks, aspect) sol_8 = P3.thresholds(p3, 800.0, 0.95) #! format: off - fig1_b200 = Plt.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_2) for D in D_range], color = cl[1], linewidth = lw) - fig1_b400 = Plt.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_4) for D in D_range], color = cl[2], linewidth = lw) - fig1_b800 = Plt.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_8) for D in D_range], color = cl[3], linewidth = lw) - - d_thb = Plt.vlines!(ax1_b, P3.D_th_helper(p3) * 1e3, linestyle = :dash, color = cl[4], linewidth = lw) - d_cr_200 = Plt.vlines!(ax1_b, sol_2[1] * 1e3, linestyle = :dot, color = cl[1], linewidth = lw) - d_cr_400 = Plt.vlines!(ax1_b, sol_4[1] * 1e3, linestyle = :dot, color = cl[2], linewidth = lw) - d_cr_800 = Plt.vlines!(ax1_b, sol_8[1] * 1e3, linestyle = :dot, color = cl[3], linewidth = lw) - d_gr_200 = Plt.vlines!(ax1_b, sol_2[2] * 1e3, linestyle = :dash, color = cl[1], linewidth = lw) - d_gr_400 = Plt.vlines!(ax1_b, sol_4[2] * 1e3, linestyle = :dash, color = cl[2], linewidth = lw) - d_gr_800 = Plt.vlines!(ax1_b, sol_8[2] * 1e3, linestyle = :dash, color = cl[3], linewidth = lw) - - leg1_b = Plt.Legend(fig1_b[11:12, 4], [fig1_b200, fig1_b400, fig1_b800], [Plt.L"$\rho_{r} = 200.0 kg m^{-3}$", Plt.L"$\rho_{r} = 400.0 kg m^{-3}$", Plt.L"$\rho_{r} = 800.0 kg m^{-3}$",], framevisible = false) - leg1_b_dth = Plt.Legend(fig1_b[11:12, 5], [d_thb], [Plt.L"$D_{th}$"], framevisible = false) - leg1_b_dcr = Plt.Legend(fig1_b[11:12, 8], [d_cr_200, d_cr_400, d_cr_800], [Plt.L"$D_{cr}$ for $\rho_{r} = 200.0 kg m^{-3}$", Plt.L"$D_{cr}$ for $\rho_{r} = 400.0 kg m^{-3}$", Plt.L"$D_{cr}$ for $\rho_{r} = 800.0 kg m^{-3}$",], framevisible = false) - leg1_b_dgr = Plt.Legend(fig1_b[11:12, 6], [d_gr_200, d_gr_400, d_gr_800], [Plt.L"$D_{gr}$ for $\rho_{r} = 200.0 kg m^{-3}$", Plt.L"$D_{gr}$ for $\rho_{r} = 400.0 kg m^{-3}$", Plt.L"$D_{gr}$ for $\rho_{r} = 800.0 kg m^{-3}$",], framevisible = false) + fig1_b200 = CMK.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_2) for D in D_range], color = cl[1], linewidth = lw) + fig1_b400 = CMK.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_4) for D in D_range], color = cl[2], linewidth = lw) + fig1_b800 = CMK.lines!(ax1_b, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_8) for D in D_range], color = cl[3], linewidth = lw) + + d_thb = CMK.vlines!(ax1_b, P3.D_th_helper(p3) * 1e3, linestyle = :dash, color = cl[4], linewidth = lw) + d_cr_200 = CMK.vlines!(ax1_b, sol_2[1] * 1e3, linestyle = :dot, color = cl[1], linewidth = lw) + d_cr_400 = CMK.vlines!(ax1_b, sol_4[1] * 1e3, linestyle = :dot, color = cl[2], linewidth = lw) + d_cr_800 = CMK.vlines!(ax1_b, sol_8[1] * 1e3, linestyle = :dot, color = cl[3], linewidth = lw) + d_gr_200 = CMK.vlines!(ax1_b, sol_2[2] * 1e3, linestyle = :dash, color = cl[1], linewidth = lw) + d_gr_400 = CMK.vlines!(ax1_b, sol_4[2] * 1e3, linestyle = :dash, color = cl[2], linewidth = lw) + d_gr_800 = CMK.vlines!(ax1_b, sol_8[2] * 1e3, linestyle = :dash, color = cl[3], linewidth = lw) + + leg1_b = CMK.Legend(fig1_b[11:12, 4], [fig1_b200, fig1_b400, fig1_b800], [CMK.L"$\rho_{r} = 200.0 kg m^{-3}$", CMK.L"$\rho_{r} = 400.0 kg m^{-3}$", CMK.L"$\rho_{r} = 800.0 kg m^{-3}$",], framevisible = false) + leg1_b_dth = CMK.Legend(fig1_b[11:12, 5], [d_thb], [CMK.L"$D_{th}$"], framevisible = false) + leg1_b_dcr = CMK.Legend(fig1_b[11:12, 8], [d_cr_200, d_cr_400, d_cr_800], [CMK.L"$D_{cr}$ for $\rho_{r} = 200.0 kg m^{-3}$", CMK.L"$D_{cr}$ for $\rho_{r} = 400.0 kg m^{-3}$", CMK.L"$D_{cr}$ for $\rho_{r} = 800.0 kg m^{-3}$",], framevisible = false) + leg1_b_dgr = CMK.Legend(fig1_b[11:12, 6], [d_gr_200, d_gr_400, d_gr_800], [CMK.L"$D_{gr}$ for $\rho_{r} = 200.0 kg m^{-3}$", CMK.L"$D_{gr}$ for $\rho_{r} = 400.0 kg m^{-3}$", CMK.L"$D_{gr}$ for $\rho_{r} = 800.0 kg m^{-3}$",], framevisible = false) #! format: on - Plt.save("MorrisonandMilbrandtFig1b.svg", fig1_b) + CMK.save("MorrisonandMilbrandtFig1b.svg", fig1_b) end #! format: off @@ -214,9 +214,9 @@ function p3_relations_plot() sol4_5 = P3.thresholds(p3, 400.0, 0.5) sol4_8 = P3.thresholds(p3, 400.0, 0.8) # m(D) - fig1_0 = CMK.lines!(ax1, D_range * 1e3, [P3.p3_mass(p3, D, 0.0 ) for D in D_range], color = cl[1], linewidth = lw) - fig1_5 = CMK.lines!(ax1, D_range * 1e3, [P3.p3_mass(p3, D, 0.5, sol4_5) for D in D_range], color = cl[2], linewidth = lw) - fig1_8 = CMK.lines!(ax1, D_range * 1e3, [P3.p3_mass(p3, D, 0.8, sol4_8) for D in D_range], color = cl[3], linewidth = lw) + fig1_0 = CMK.lines!(ax1, D_range * 1e3, [p3_mass(p3, D, 0.0 ) for D in D_range], color = cl[1], linewidth = lw) + fig1_5 = CMK.lines!(ax1, D_range * 1e3, [p3_mass(p3, D, 0.5, sol4_5) for D in D_range], color = cl[2], linewidth = lw) + fig1_8 = CMK.lines!(ax1, D_range * 1e3, [p3_mass(p3, D, 0.8, sol4_8) for D in D_range], color = cl[3], linewidth = lw) # a(D) fig2_0 = CMK.lines!(ax2, D_range * 1e3, [area(p3, D, 0.0 ) for D in D_range], color = cl[1], linewidth = lw) fig2_5 = CMK.lines!(ax2, D_range * 1e3, [area(p3, D, 0.5, sol4_5) for D in D_range], color = cl[2], linewidth = lw) @@ -235,9 +235,9 @@ function p3_relations_plot() sol_4 = P3.thresholds(p3, 400.0, 0.95) sol_8 = P3.thresholds(p3, 800.0, 0.95) # m(D) - fig3_200 = CMK.lines!(ax3, D_range * 1e3, [P3.p3_mass(p3, D, 0.95, sol_2) for D in D_range], color = cl[1], linewidth = lw) - fig3_400 = CMK.lines!(ax3, D_range * 1e3, [P3.p3_mass(p3, D, 0.95, sol_4) for D in D_range], color = cl[2], linewidth = lw) - fig3_800 = CMK.lines!(ax3, D_range * 1e3, [P3.p3_mass(p3, D, 0.95, sol_8) for D in D_range], color = cl[3], linewidth = lw) + fig3_200 = CMK.lines!(ax3, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_2) for D in D_range], color = cl[1], linewidth = lw) + fig3_400 = CMK.lines!(ax3, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_4) for D in D_range], color = cl[2], linewidth = lw) + fig3_800 = CMK.lines!(ax3, D_range * 1e3, [p3_mass(p3, D, 0.95, sol_8) for D in D_range], color = cl[3], linewidth = lw) # a(D) fig3_200 = CMK.lines!(ax4, D_range * 1e3, [area(p3, D, 0.5, sol_2) for D in D_range], color = cl[1], linewidth = lw) fig3_400 = CMK.lines!(ax4, D_range * 1e3, [area(p3, D, 0.5, sol_4) for D in D_range], color = cl[2], linewidth = lw) From abacd1b95ef818a55b161aa57325b651efc26619 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Wed, 10 Apr 2024 14:01:01 -0400 Subject: [PATCH 28/33] V_n added --- src/P3Scheme.jl | 228 ++++++++++++++++++++++++++++++++++++++ test/p3_tests.jl | 29 +++++ test/performance_tests.jl | 7 ++ 3 files changed, 264 insertions(+) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 6bca4ea95..5a611a651 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -657,6 +657,234 @@ function terminal_velocity_mass( return v / q end +""" + terminal_velocity_number(p3, Chen2022, q, N, ρ_r, F_r) + + - p3 - a struct with P3 scheme parameters + - Chen 2022 - a struch with terminal velocity parameters as in Chen(2022) + - q - mass mixing ratio + - N - number mixing ratio + - ρ_r - rime density (q_rim/B_rim) [kg/m^3] + - F_r - rime mass fraction (q_rim/q_i) + - ρ_a - density of air + + Returns the number (total)-weighted fall speed + Eq C11 of Morrison and Milbrandt (2015) +""" +function terminal_velocity_number( + p3::PSP3, + Chen2022::CMP.Chen2022VelTypeSnowIce, + q::FT, + N::FT, + ρ_r::FT, + F_r::FT, + ρ_a::FT, +) where {FT} + + # Get the thresholds for different particles regimes + th = thresholds(p3, ρ_r, F_r) + D_th = D_th_helper(p3) + cutoff = FT(0.000625) # TODO add to the struct + + # Get the shape parameters + (λ, N_0) = distribution_parameter_solver(p3, q, N, ρ_r, F_r) + μ = DSD_μ(p3, λ) + + # TO DO: Change when each value used depending on type of particle + κ = FT(-1 / 6) #FT(1/3) + + # Redefine α_va to be in si units + α_va = α_va_si(p3) + + v = 0 + for i in 1:2 + if F_r == 0 + # Velocity coefficients for small particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) + v += integrate( + FT(0), + D_th, + ai[i] * N_0, + bi[i] + μ, + ci[i] + λ, + ) + v += integrate( + D_th, + cutoff, + ai[i] * + N_0 * + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, + bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), + ci[i] + λ, + ) + + # Get velocity coefficients for large particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) + v += integrate( + cutoff, + Inf, + ai[i] * + N_0 * + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, + bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), + ci[i] + λ, + ) + else + # Velocity coefficients for small particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) + large = false + + v += integrate( + FT(0), + D_th, + ai[i] * N_0, + bi[i] + μ, + ci[i] + λ, + ) + + # D_th to D_gr + if !large && th.D_gr > cutoff + v += integrate( + D_th, + cutoff, + ai[i] * + N_0 * + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, + bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), + ci[i] + λ, + ) + + # Switch to large particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) + large = true + + v += integrate( + cutoff, + th.D_gr, + ai[i] * + N_0 * + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, + bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), + ci[i] + λ, + ) + else + v += integrate( + D_th, + th.D_gr, + ai[i] * + N_0 * + (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, + bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), + ci[i] + λ, + ) + end + + # D_gr to D_cr + if !large && th.D_cr > cutoff + v += integrate( + th.D_gr, + cutoff, + ai[i] * + N_0 * + (p3.ρ_i / th.ρ_g)^(2 * κ), + bi[i] + μ, + ci[i] + λ, + ) + + # Switch to large particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) + large = true + + v += integrate( + cutoff, + th.D_cr, + ai[i] * + N_0 * + (p3.ρ_i / th.ρ_g)^(2 * κ), + bi[i] + μ, + ci[i] + λ, + ) + else + v += integrate( + th.D_gr, + th.D_cr, + ai[i] * + N_0 * + (p3.ρ_i / th.ρ_g)^(2 * κ), + bi[i] + μ, + ci[i] + λ, + ) + end + + # D_cr to Infinity + if !large + (I, e) = QGK.quadgk( + D -> + ( + 16 * + p3.ρ_i^2 * + (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / + (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) + )^κ * + ai[i] * + D^(bi[i]) * + exp(-ci[i] * D) * + N_0 * + D^μ * + exp(-λ * D), + th.D_cr, + cutoff, + ) + v += I + + # Switch to large particles + (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) + large = true + + (I, e) = QGK.quadgk( + D -> + ( + 16 * + p3.ρ_i^2 * + (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / + (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) + )^κ * + ai[i] * + D^(bi[i]) * + exp(-ci[i] * D) * + N_0 * + D^μ * + exp(-λ * D), + cutoff, + Inf, + ) + v += I + else + (I, e) = QGK.quadgk( + D -> + ( + 16 * + p3.ρ_i^2 * + (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / + (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) + )^κ * + ai[i] * + D^(bi[i]) * + exp(-ci[i] * D) * + N_0 * + D^μ * + exp(-λ * D), + th.D_cr, + Inf, + ) + v += I + end + end + end + + return v / N +end + # TODO: add accurate number weighted velocity function that mimics the mass weighted one """ diff --git a/test/p3_tests.jl b/test/p3_tests.jl index dcc0f29a0..c442bd060 100644 --- a/test/p3_tests.jl +++ b/test/p3_tests.jl @@ -173,6 +173,35 @@ function test_velocities(FT) end end + TT.@testset "Number-weighted terminal velocities" begin + expected_vals = [ + [1.52, 1.46, 1.41, 1.36, 1.24], + [1.52, 1.47, 1.44, 1.42, 1.35], + [1.52, 1.47, 1.45, 1.44, 1.42], + [1.52, 1.47, 1.45, 1.45, 1.45], + ] + for i in 1:length(ρ_rs) + for j in 1:length(F_rs) + ρ_r = ρ_rs[i] + F_r = F_rs[j] + + calculated_vel = P3.terminal_velocity_number( + p3, + Chen2022.snow_ice, + q, + N, + ρ_r, + F_r, + ρ_a, + ) + + TT.@test calculated_vel > 0 + TT.@test expected_vals[i][j] ≈ calculated_vel atol = 0.1 + + end + end + end + TT.@testset "Mass-weighted mean diameters" begin paper_vals = [ [5, 5, 5, 5, 5], diff --git a/test/performance_tests.jl b/test/performance_tests.jl index 25c13af00..a8f8c43a1 100644 --- a/test/performance_tests.jl +++ b/test/performance_tests.jl @@ -135,6 +135,13 @@ function benchmark_test(FT) 4e4, 2e3, ) + bench_press( + P3.terminal_velocity_number, + (p3, ch2022.snow_ice, q_ice, N, ρ_r, F_r, ρ_air), + 2e5, + 3e4, + 2e3, + ) bench_press(P3.D_m, (p3, q_ice, N, ρ_r, F_r), 1e5) end From 11391c544de8f3a528223ef2b233568894cffa08 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Wed, 10 Apr 2024 16:26:29 -0400 Subject: [PATCH 29/33] format --- src/P3Scheme.jl | 36 +++++++----------------------------- test/p3_tests.jl | 2 +- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 5a611a651..2da64a082 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -701,19 +701,11 @@ function terminal_velocity_number( if F_r == 0 # Velocity coefficients for small particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) - v += integrate( - FT(0), - D_th, - ai[i] * N_0, - bi[i] + μ, - ci[i] + λ, - ) + v += integrate(FT(0), D_th, ai[i] * N_0, bi[i] + μ, ci[i] + λ) v += integrate( D_th, cutoff, - ai[i] * - N_0 * - (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, + ai[i] * N_0 * (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ, ) @@ -723,9 +715,7 @@ function terminal_velocity_number( v += integrate( cutoff, Inf, - ai[i] * - N_0 * - (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, + ai[i] * N_0 * (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, bi[i] + μ + κ * (3 * p3.σ - 2 * p3.β_va), ci[i] + λ, ) @@ -734,13 +724,7 @@ function terminal_velocity_number( (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) large = false - v += integrate( - FT(0), - D_th, - ai[i] * N_0, - bi[i] + μ, - ci[i] + λ, - ) + v += integrate(FT(0), D_th, ai[i] * N_0, bi[i] + μ, ci[i] + λ) # D_th to D_gr if !large && th.D_gr > cutoff @@ -784,9 +768,7 @@ function terminal_velocity_number( v += integrate( th.D_gr, cutoff, - ai[i] * - N_0 * - (p3.ρ_i / th.ρ_g)^(2 * κ), + ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ, ci[i] + λ, ) @@ -798,9 +780,7 @@ function terminal_velocity_number( v += integrate( cutoff, th.D_cr, - ai[i] * - N_0 * - (p3.ρ_i / th.ρ_g)^(2 * κ), + ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ, ci[i] + λ, ) @@ -808,9 +788,7 @@ function terminal_velocity_number( v += integrate( th.D_gr, th.D_cr, - ai[i] * - N_0 * - (p3.ρ_i / th.ρ_g)^(2 * κ), + ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), bi[i] + μ, ci[i] + λ, ) diff --git a/test/p3_tests.jl b/test/p3_tests.jl index c442bd060..e82b5bb17 100644 --- a/test/p3_tests.jl +++ b/test/p3_tests.jl @@ -173,7 +173,7 @@ function test_velocities(FT) end end - TT.@testset "Number-weighted terminal velocities" begin + TT.@testset "Number-weighted terminal velocities" begin expected_vals = [ [1.52, 1.46, 1.41, 1.36, 1.24], [1.52, 1.47, 1.44, 1.42, 1.35], From 6f92fda131dd44c43f7b6973b5a38ab313651695 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Wed, 10 Apr 2024 17:26:25 -0400 Subject: [PATCH 30/33] deleted old test about negative vel --- test/p3_tests.jl | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/test/p3_tests.jl b/test/p3_tests.jl index e82b5bb17..72953dfdb 100644 --- a/test/p3_tests.jl +++ b/test/p3_tests.jl @@ -224,26 +224,6 @@ function test_velocities(FT) end end -function test_neg_vel(FT) - Chen2022 = CMP.Chen2022VelType(FT) - p3 = CMP.ParametersP3(FT) - q = FT(0.0008) - N = FT(1e6) - ρ_r = FT(900) - F_r = FT(0.99) - - # Check for negative velocity - TT.@test P3.terminal_velocity_mass( - p3, - Chen2022.snow_ice, - q, - N, - ρ_r, - F_r, - FT(1.2), - ) > 0 -end - println("Testing Float32") test_p3_thresholds(Float32) #TODO - only works for Float64 now. We should switch the units inside the solver @@ -254,4 +234,3 @@ println("Testing Float64") test_p3_thresholds(Float64) test_p3_shape_solver(Float64) test_velocities(Float64) -test_neg_vel(Float64) # expected to fail From 9b1bc509b8c827fb7b3db63b749d764bd36b7a1c Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Fri, 12 Apr 2024 13:42:42 -0400 Subject: [PATCH 31/33] comments + edits --- docs/src/plots/P3SchemePlots.jl | 12 +- src/P3Scheme.jl | 227 +++++++++++++------------------- 2 files changed, 94 insertions(+), 145 deletions(-) diff --git a/docs/src/plots/P3SchemePlots.jl b/docs/src/plots/P3SchemePlots.jl index 17523d802..fb36ae059 100644 --- a/docs/src/plots/P3SchemePlots.jl +++ b/docs/src/plots/P3SchemePlots.jl @@ -46,17 +46,13 @@ function p3_mass( D_th = P3.D_th_helper(p3) if D_th > D return mass_s(D, p3.ρ_i) # small spherical ice - end - if F_r == 0 + else if F_r == 0 return mass_nl(p3, D) # large nonspherical unrimed ice - end - if th.D_gr > D >= D_th + else if th.D_gr > D >= D_th return mass_nl(p3, D) # dense nonspherical ice - end - if th.D_cr > D >= th.D_gr + else if th.D_cr > D >= th.D_gr return mass_s(D, th.ρ_g) # graupel - end - if D >= th.D_cr + else if D >= th.D_cr return mass_r(p3, D, F_r) # partially rimed ice end end diff --git a/src/P3Scheme.jl b/src/P3Scheme.jl index 2da64a082..a42de1647 100644 --- a/src/P3Scheme.jl +++ b/src/P3Scheme.jl @@ -232,7 +232,7 @@ function DSD_N₀(p3::PSP3, N::FT, λ::FT) where {FT} end """ - integrate(a, b, c1, c2, c3) + integrate_to_gamma(a, b, c1, c2, c3) - a - lower bound - b - upper bound @@ -241,7 +241,7 @@ end Integrates the function c1 * D ^ (c2) * exp(-c3 * D) dD from a to b Returns the result """ -function integrate(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where {FT} +function integrate_to_gamma(a::FT, b::FT, c1::FT, c2::FT, c3::FT) where {FT} if b == Inf return c1 * c3^(-c2 - 1) * (Γ(1 + c2, a * c3)) elseif a == 0 @@ -269,20 +269,26 @@ end # or # q_rim > 0 and D_min = D_gr, D_max = D_cr, ρ = ρ_g function q_s(p3::PSP3, ρ::FT, μ::FT, λ::FT, D_min::FT, D_max::FT) where {FT} - return integrate(D_min, D_max, FT(π) / 6 * ρ, μ + 3, λ) + return integrate_to_gamma(D_min, D_max, FT(π) / 6 * ρ, μ + 3, λ) end # q_rim = 0 and D_min = D_th, D_max = inf function q_rz(p3::PSP3, μ::FT, λ::FT, D_min::FT) where {FT} - return integrate(D_min, FT(Inf), α_va_si(p3), μ + p3.β_va, λ) + return integrate_to_gamma(D_min, FT(Inf), α_va_si(p3), μ + p3.β_va, λ) end # q_rim > 0 and D_min = D_th and D_max = D_gr function q_n(p3::PSP3, μ::FT, λ::FT, D_min::FT, D_max::FT) where {FT} - return integrate(D_min, D_max, α_va_si(p3), μ + p3.β_va, λ) + return integrate_to_gamma(D_min, D_max, α_va_si(p3), μ + p3.β_va, λ) end # partially rimed ice or large unrimed ice (upper bound on D is infinity) # q_rim > 0 and D_min = D_cr, D_max = inf function q_r(p3::PSP3, F_r::FT, μ::FT, λ::FT, D_min::FT) where {FT} - return integrate(D_min, FT(Inf), α_va_si(p3) / (1 - F_r), μ + p3.β_va, λ) + return integrate_to_gamma( + D_min, + FT(Inf), + α_va_si(p3) / (1 - F_r), + μ + p3.β_va, + λ, + ) end """ @@ -459,14 +465,14 @@ function terminal_velocity_mass( if F_r == 0 # Velocity coefficients for small particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) - v += integrate( + v += integrate_to_gamma( FT(0), D_th, FT(π) / 6 * p3.ρ_i * ai[i] * N_0, bi[i] + μ + 3, ci[i] + λ, ) - v += integrate( + v += integrate_to_gamma( D_th, cutoff, α_va * @@ -479,7 +485,7 @@ function terminal_velocity_mass( # Get velocity coefficients for large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) - v += integrate( + v += integrate_to_gamma( cutoff, Inf, α_va * @@ -494,7 +500,7 @@ function terminal_velocity_mass( (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) large = false - v += integrate( + v += integrate_to_gamma( FT(0), D_th, FT(π) / 6 * p3.ρ_i * ai[i] * N_0, @@ -504,7 +510,7 @@ function terminal_velocity_mass( # D_th to D_gr if !large && th.D_gr > cutoff - v += integrate( + v += integrate_to_gamma( D_th, cutoff, α_va * @@ -519,7 +525,7 @@ function terminal_velocity_mass( (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) large = true - v += integrate( + v += integrate_to_gamma( cutoff, th.D_gr, α_va * @@ -530,7 +536,7 @@ function terminal_velocity_mass( ci[i] + λ, ) else - v += integrate( + v += integrate_to_gamma( D_th, th.D_gr, α_va * @@ -544,7 +550,7 @@ function terminal_velocity_mass( # D_gr to D_cr if !large && th.D_cr > cutoff - v += integrate( + v += integrate_to_gamma( th.D_gr, cutoff, FT(π) / 6 * @@ -560,7 +566,7 @@ function terminal_velocity_mass( (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) large = true - v += integrate( + v += integrate_to_gamma( cutoff, th.D_cr, FT(π) / 6 * @@ -572,7 +578,7 @@ function terminal_velocity_mass( ci[i] + λ, ) else - v += integrate( + v += integrate_to_gamma( th.D_gr, th.D_cr, FT(π) / 6 * @@ -586,69 +592,32 @@ function terminal_velocity_mass( end # D_cr to Infinity + v_m_D_cr(D) = + ( + 16 * + p3.ρ_i^2 * + (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / + (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) + )^κ * + ai[i] * + D^(bi[i]) * + exp(-ci[i] * D) * + (α_va / (1 - F_r) * D^p3.β_va) * + N_0 * + D^μ * + exp(-λ * D) if !large - (I, e) = QGK.quadgk( - D -> - ( - 16 * - p3.ρ_i^2 * - (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / - (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) - )^κ * - ai[i] * - D^(bi[i]) * - exp(-ci[i] * D) * - (α_va / (1 - F_r) * D^p3.β_va) * - N_0 * - D^μ * - exp(-λ * D), - th.D_cr, - cutoff, - ) + (I, e) = QGK.quadgk(D -> v_m_D_cr(D), th.D_cr, cutoff) v += I # Switch to large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) large = true - (I, e) = QGK.quadgk( - D -> - ( - 16 * - p3.ρ_i^2 * - (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / - (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) - )^κ * - ai[i] * - D^(bi[i]) * - exp(-ci[i] * D) * - (α_va / (1 - F_r) * D^p3.β_va) * - N_0 * - D^μ * - exp(-λ * D), - cutoff, - Inf, - ) + (I, e) = QGK.quadgk(D -> v_m_D_cr(D), cutoff, Inf) v += I else - (I, e) = QGK.quadgk( - D -> - ( - 16 * - p3.ρ_i^2 * - (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / - (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) - )^κ * - ai[i] * - D^(bi[i]) * - exp(-ci[i] * D) * - (α_va / (1 - F_r) * D^p3.β_va) * - N_0 * - D^μ * - exp(-λ * D), - th.D_cr, - Inf, - ) + (I, e) = QGK.quadgk(D -> v_m_D_cr(D), th.D_cr, Inf) v += I end end @@ -701,8 +670,14 @@ function terminal_velocity_number( if F_r == 0 # Velocity coefficients for small particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) - v += integrate(FT(0), D_th, ai[i] * N_0, bi[i] + μ, ci[i] + λ) - v += integrate( + v += integrate_to_gamma( + FT(0), + D_th, + ai[i] * N_0, + bi[i] + μ, + ci[i] + λ, + ) + v += integrate_to_gamma( D_th, cutoff, ai[i] * N_0 * (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, @@ -712,7 +687,7 @@ function terminal_velocity_number( # Get velocity coefficients for large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) - v += integrate( + v += integrate_to_gamma( cutoff, Inf, ai[i] * N_0 * (16 * p3.ρ_i^2 * p3.γ^3 / (9 * FT(π) * α_va^2))^κ, @@ -724,11 +699,17 @@ function terminal_velocity_number( (ai, bi, ci) = CO.Chen2022_vel_coeffs_small(Chen2022, ρ_a) large = false - v += integrate(FT(0), D_th, ai[i] * N_0, bi[i] + μ, ci[i] + λ) + v += integrate_to_gamma( + FT(0), + D_th, + ai[i] * N_0, + bi[i] + μ, + ci[i] + λ, + ) # D_th to D_gr if !large && th.D_gr > cutoff - v += integrate( + v += integrate_to_gamma( D_th, cutoff, ai[i] * @@ -742,7 +723,7 @@ function terminal_velocity_number( (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) large = true - v += integrate( + v += integrate_to_gamma( cutoff, th.D_gr, ai[i] * @@ -752,7 +733,7 @@ function terminal_velocity_number( ci[i] + λ, ) else - v += integrate( + v += integrate_to_gamma( D_th, th.D_gr, ai[i] * @@ -765,7 +746,7 @@ function terminal_velocity_number( # D_gr to D_cr if !large && th.D_cr > cutoff - v += integrate( + v += integrate_to_gamma( th.D_gr, cutoff, ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), @@ -777,7 +758,7 @@ function terminal_velocity_number( (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) large = true - v += integrate( + v += integrate_to_gamma( cutoff, th.D_cr, ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), @@ -785,7 +766,7 @@ function terminal_velocity_number( ci[i] + λ, ) else - v += integrate( + v += integrate_to_gamma( th.D_gr, th.D_cr, ai[i] * N_0 * (p3.ρ_i / th.ρ_g)^(2 * κ), @@ -795,66 +776,31 @@ function terminal_velocity_number( end # D_cr to Infinity + v_n_D_cr(D) = + ( + 16 * + p3.ρ_i^2 * + (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / + (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) + )^κ * + ai[i] * + D^(bi[i]) * + exp(-ci[i] * D) * + N_0 * + D^μ * + exp(-λ * D) if !large - (I, e) = QGK.quadgk( - D -> - ( - 16 * - p3.ρ_i^2 * - (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / - (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) - )^κ * - ai[i] * - D^(bi[i]) * - exp(-ci[i] * D) * - N_0 * - D^μ * - exp(-λ * D), - th.D_cr, - cutoff, - ) + (I, e) = QGK.quadgk(D -> v_n_D_cr(D), th.D_cr, cutoff) v += I # Switch to large particles (ai, bi, ci) = CO.Chen2022_vel_coeffs_large(Chen2022, ρ_a) large = true - (I, e) = QGK.quadgk( - D -> - ( - 16 * - p3.ρ_i^2 * - (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / - (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) - )^κ * - ai[i] * - D^(bi[i]) * - exp(-ci[i] * D) * - N_0 * - D^μ * - exp(-λ * D), - cutoff, - Inf, - ) + (I, e) = QGK.quadgk(D -> v_n_D_cr(D), cutoff, Inf) v += I else - (I, e) = QGK.quadgk( - D -> - ( - 16 * - p3.ρ_i^2 * - (F_r * π / 4 * D^2 + (1 - F_r) * p3.γ * D^p3.σ)^3 / - (9 * π * (α_va / (1 - F_r) * D^p3.β_va)^2) - )^κ * - ai[i] * - D^(bi[i]) * - exp(-ci[i] * D) * - N_0 * - D^μ * - exp(-λ * D), - th.D_cr, - Inf, - ) + (I, e) = QGK.quadgk(D -> v_n_D_cr(D), th.D_cr, Inf) v += I end end @@ -891,13 +837,20 @@ function D_m(p3::PSP3, q::FT, N::FT, ρ_r::FT, F_r::FT) where {FT} # Calculate numerator n = 0 if F_r == 0 - n += integrate(FT(0), D_th, π / 6 * p3.ρ_i * N_0, μ + 4, λ) - n += integrate(D_th, Inf, α_va * N_0, μ + p3.β_va + 1, λ) + n += integrate_to_gamma(FT(0), D_th, π / 6 * p3.ρ_i * N_0, μ + 4, λ) + n += integrate_to_gamma(D_th, Inf, α_va * N_0, μ + p3.β_va + 1, λ) else - n += integrate(FT(0), D_th, π / 6 * p3.ρ_i * N_0, μ + 4, λ) - n += integrate(D_th, th.D_gr, α_va * N_0, μ + p3.β_va + 1, λ) - n += integrate(th.D_gr, th.D_cr, π / 6 * th.ρ_g * N_0, μ + 4, λ) - n += integrate(th.D_cr, Inf, α_va / (1 - F_r) * N_0, μ + p3.β_va + 1, λ) + n += integrate_to_gamma(FT(0), D_th, π / 6 * p3.ρ_i * N_0, μ + 4, λ) + n += integrate_to_gamma(D_th, th.D_gr, α_va * N_0, μ + p3.β_va + 1, λ) + n += + integrate_to_gamma(th.D_gr, th.D_cr, π / 6 * th.ρ_g * N_0, μ + 4, λ) + n += integrate_to_gamma( + th.D_cr, + Inf, + α_va / (1 - F_r) * N_0, + μ + p3.β_va + 1, + λ, + ) end # Normalize by q From 82bac13c77f346302c92a751b770533c3d544c5a Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Fri, 12 Apr 2024 13:43:23 -0400 Subject: [PATCH 32/33] project.toml edits --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index a248f4cb0..a3c15ece7 100644 --- a/Project.toml +++ b/Project.toml @@ -34,4 +34,4 @@ QuadGK = "2.9.4" RootSolvers = "0.3, 0.4" SpecialFunctions = "1, 2" Thermodynamics = "0.12.4" -julia = "1.6" \ No newline at end of file +julia = "1.6" From 785cd66ccc4f0a8142f49c86c962e80a60853ec6 Mon Sep 17 00:00:00 2001 From: Anastasia Popova Date: Fri, 12 Apr 2024 14:29:47 -0400 Subject: [PATCH 33/33] fixed format errors --- docs/src/plots/P3SchemePlots.jl | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/docs/src/plots/P3SchemePlots.jl b/docs/src/plots/P3SchemePlots.jl index fb36ae059..f80093d6d 100644 --- a/docs/src/plots/P3SchemePlots.jl +++ b/docs/src/plots/P3SchemePlots.jl @@ -46,13 +46,13 @@ function p3_mass( D_th = P3.D_th_helper(p3) if D_th > D return mass_s(D, p3.ρ_i) # small spherical ice - else if F_r == 0 + elseif F_r == 0 return mass_nl(p3, D) # large nonspherical unrimed ice - else if th.D_gr > D >= D_th + elseif th.D_gr > D >= D_th return mass_nl(p3, D) # dense nonspherical ice - else if th.D_cr > D >= th.D_gr + elseif th.D_cr > D >= th.D_gr return mass_s(D, th.ρ_g) # graupel - else if D >= th.D_cr + elseif D >= th.D_cr return mass_r(p3, D, F_r) # partially rimed ice end end @@ -91,17 +91,13 @@ function area( # Area regime: if P3.D_th_helper(p3) > D return A_s(D) # small spherical ice - end - if F_r == 0 + elseif F_r == 0 return A_ns(p3, D) # large nonspherical unrimed ice - end - if th.D_gr > D >= P3.D_th_helper(p3) + elseif th.D_gr > D >= P3.D_th_helper(p3) return A_ns(p3, D) # dense nonspherical ice - end - if th.D_cr > D >= th.D_gr + elseif th.D_cr > D >= th.D_gr return A_s(D) # graupel - end - if D >= th.D_cr + elseif D >= th.D_cr return A_r(p3, F_r, D) # partially rimed ice end