From e05dfdfde091bdbc4b6c883793a64a5d89d3655a Mon Sep 17 00:00:00 2001 From: Emily de Jong Date: Thu, 22 Feb 2024 13:16:00 -0800 Subject: [PATCH 01/12] Add preliminary cloudy condensation + coalescence, with unit tests --- Project.toml | 1 + src/CloudMicrophysics.jl | 1 + src/MicrophysicsFlexible.jl | 113 +++++++++++++++++++++++++++++ test/Project.toml | 1 + test/microphysicsflexible_tests.jl | 72 ++++++++++++++++++ 5 files changed, 188 insertions(+) create mode 100644 src/MicrophysicsFlexible.jl create mode 100644 test/microphysicsflexible_tests.jl diff --git a/Project.toml b/Project.toml index cac9a0d80..2b91e4afa 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.16.0" [deps] CLIMAParameters = "6eacf6c3-8458-43b9-ae03-caf5306d3d53" +Cloudy = "9e3b23bb-e7cc-4b94-886c-65de2234ba87" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" RootSolvers = "7181ea78-2dcb-4de3-ab41-2b8ab5a31e74" diff --git a/src/CloudMicrophysics.jl b/src/CloudMicrophysics.jl index 16987404b..298bf26d0 100644 --- a/src/CloudMicrophysics.jl +++ b/src/CloudMicrophysics.jl @@ -21,5 +21,6 @@ include("AerosolActivation.jl") include("Nucleation.jl") include("IceNucleation.jl") include("PrecipitationSusceptibility.jl") +include("MicrophysicsFlexible.jl") end # module diff --git a/src/MicrophysicsFlexible.jl b/src/MicrophysicsFlexible.jl new file mode 100644 index 000000000..bc77b4ad4 --- /dev/null +++ b/src/MicrophysicsFlexible.jl @@ -0,0 +1,113 @@ +""" +Flexible N-moment microphysics representation, including: + - Generalized collisional-coalescence described by a kernel K(x,y), with + default parameterization based on Long collision kernel + - Power-series representation of fall-speed + - Power-series representation of condensational growth + TODO: no representation of ventilation effects +""" +module MicrophysicsFlexible + +import Thermodynamics as TD +import Thermodynamics.Parameters as TDP + +import ..Common as CO +import ..Parameters as CMP + +import Cloudy as CL +import Cloudy.ParticleDistributions as CPD +import Cloudy.KernelFunctions as CLK + +export coalescence + +""" +A structure containing the subdistributions, their moments, and additional +dynamical parameters corresponding to rates of collision, sedimentation, and +condensation/evaporation +""" +Base.@kwdef mutable struct CLSetup{FT} + "Subdistributions of the hydrometeor size distribution; defaults to + exponential cloud mode and gamma rain mode" + pdists::Vector{CPD.PrimitiveParticleDistribution{FT}} = Vector([ + CPD.ExponentialPrimitiveParticleDistribution(FT(0), FT(1.0)), + CPD.GammaPrimitiveParticleDistribution(FT(0), FT(1.0), FT(1.0)) + ]) + "Moments of the subdistributions; should correspond with pdists" + mom::Vector{FT} = [FT(0) for i in 1:5] + "Total number of prognostic moments for each subdistribution" + NProgMoms::Vector{Int} = [Integer(CPD.nparams(dist)) for dist in pdists] + "Kernel function for collisional coalescence" + KernelFunc::CLK.KernelFunction{FT} = CLK.LongKernelFunction(FT(0.5236), FT(9.44e-3), FT(5.78e-3)) + "Particle mass thresholds for analytical integration style" + mass_thresholds::Vector{FT} = [10.0, Inf] + "Polynomial order of the kernel approximation for coalescence" + kernel_order::Int = 1 + "Upper limit for evaluation of the polynomial kernel approximation" + kernel_limit::FT = FT(500) + "Coalescence data" + coal_data = nothing +end + +""" + coalescence(CLSetup) + + - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters + TODO: currently implemented only for analytical coalescence style + +Returns a vector of moment tendencies due to collisional coalescence +""" +function coalescence(clinfo::CLSetup{FT}) where {FT} + kernel_tensor = CL.KernelTensors.CoalescenceTensor(clinfo.KernelFunc, clinfo.kernel_order, clinfo.kernel_limit) + if isnothing(clinfo.coal_data) + clinfo.coal_data = CL.Coalescence.initialize_coalescence_data( + CL.EquationTypes.AnalyticalCoalStyle(), + kernel_tensor, + clinfo.NProgMoms, + dist_thresholds = clinfo.mass_thresholds + ) + end + # first, update the particle distributions + for (i, dist) in enumerate(clinfo.pdists) + ind_rng = CL.get_dist_moments_ind_range(clinfo.NProgMoms, i) + CPD.update_dist_from_moments!(dist, clinfo.mom[ind_rng]) + end + CL.Coalescence.update_coal_ints!( + CL.EquationTypes.AnalyticalCoalStyle(), + clinfo.pdists, + clinfo.coal_data + ) + return clinfo.coal_data.coal_ints +end + +""" + condensation(CLSetup) + + - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters + - `aps` - air properties + - `tps` - thermodynamics parameters + - `q` - phase partition + - `ρ` - air density + - `T` - air temperature +Returns a vector of moment tendencies due to condensation/evaporation +""" +function condensation( + clinfo::CLSetup{FT}, + aps::CMP.AirProperties{FT}, + tps::TDP.ThermodynamicsParameters{FT}, + q::TD.PhasePartition{FT}, + ρ::FT, + T::FT, +) where {FT} + S = TD.supersaturation(tps, q, ρ, T, TD.Liquid()) + ξ = CO.G_func(aps, tps, T, TD.Liquid()) + + # first, update the particle distributions + for (i, dist) in enumerate(clinfo.pdists) + ind_rng = CL.get_dist_moments_ind_range(clinfo.NProgMoms, i) + CPD.update_dist_from_moments!(dist, clinfo.mom[ind_rng]) + end + return CL.Condensation.get_cond_evap(S, (; ξ=ξ, pdists=clinfo.pdists)) +end + + +end #module \ No newline at end of file diff --git a/test/Project.toml b/test/Project.toml index 63a215dc5..d5f007f4b 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -5,6 +5,7 @@ CLIMAParameters = "6eacf6c3-8458-43b9-ae03-caf5306d3d53" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" ClimaComms = "3a4d1b5c-c61d-41fd-a00a-5873ba7a1b0d" CloudMicrophysics = "6a9e3e04-43cd-43ba-94b9-e8782df3c71b" +Cloudy = "9e3b23bb-e7cc-4b94-886c-65de2234ba87" KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" RootSolvers = "7181ea78-2dcb-4de3-ab41-2b8ab5a31e74" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" diff --git a/test/microphysicsflexible_tests.jl b/test/microphysicsflexible_tests.jl new file mode 100644 index 000000000..606a64cf1 --- /dev/null +++ b/test/microphysicsflexible_tests.jl @@ -0,0 +1,72 @@ +import Test as TT + +import Thermodynamics as TD + +import CloudMicrophysics.Parameters as CMP +import CloudMicrophysics.MicrophysicsFlexible as CMF +import Cloudy.ParticleDistributions as CPD +import Cloudy.KernelFunctions as CPK + +@info "Microphysics Tests" + +function test_microphysics_flexible(FT) + # Thermodynamics and air properties parameters + aps = CMP.AirProperties(FT) + tps = TD.Parameters.ThermodynamicsParameters(FT) + + TT.@testset "Flexible microphysics - unit tests" begin + # Create Cloudy struct with different distributions + clinfo = CMF.CLSetup{FT}( + pdists = [CPD.LognormalPrimitiveParticleDistribution(FT(10.0), FT(1.0), FT(1.0))], + mom = CPD.get_moments(CPD.LognormalPrimitiveParticleDistribution(FT(10.0), FT(1.0), FT(1.0))), + NProgMoms = [3], + KernelFunc = CPK.ConstantKernelFunction(FT(1)), + mass_thresholds = [FT(100.0)], + kernel_order = 4, + kernel_limit = FT(100) + ) + + # Create Cloudy struct with defaults + clinfo = CMF.CLSetup{FT}() + TT.@test length(clinfo.pdists) == 2 + TT.@test length(clinfo.mom) == 5 + TT.@test length(clinfo.NProgMoms) == 2 + + # Test coalescence + clinfo.mom = [100.0, 10.0, 1.0, 10.0, 20.0] + TT.@test isnothing(clinfo.coal_data) + dmom = CMF.coalescence(clinfo) + TT.@test ~isnothing(clinfo.coal_data) + TT.@test all(dmom[1:clinfo.NProgMoms[1]] .< FT(0)) + TT.@test all(dmom[end - 1:end] .> FT(0)) + + # Test evaporation + ρ = FT(1.1) + T = FT(288.15) + q_tot = FT(1e-3) + q = TD.PhasePartition(q_tot) + cond_evap = CMF.condensation(clinfo, aps, tps, q, ρ, T) + # conserve number density + TT.@test cond_evap[1] == FT(0) + TT.@test cond_evap[clinfo.NProgMoms[1]+1] == FT(0) + # decrease mass density + TT.@test cond_evap[2] < FT(0) + TT.@test cond_evap[clinfo.NProgMoms[1]+2] < FT(0) + + # Test condensation + q_tot = FT(2e-2) + q = TD.PhasePartition(q_tot) + cond_evap = CMF.condensation(clinfo, aps, tps, q, ρ, T) + # conserve number density + TT.@test cond_evap[1] == FT(0) + TT.@test cond_evap[clinfo.NProgMoms[1]+1] == FT(0) + # increase cloud mass density + TT.@test cond_evap[2] > FT(0) + end +end + +println("Testing Float64") +test_microphysics_flexible(Float64) + +# println("Testing Float32") +# test_microphysics_flexible(Float32) \ No newline at end of file From 3552686b795b97672bde0a81a1d0dfdd1d784942 Mon Sep 17 00:00:00 2001 From: Emily de Jong Date: Thu, 22 Feb 2024 13:32:51 -0800 Subject: [PATCH 02/12] Add sedimentation --- src/MicrophysicsFlexible.jl | 16 ++++++++++++++-- test/microphysicsflexible_tests.jl | 4 ++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/MicrophysicsFlexible.jl b/src/MicrophysicsFlexible.jl index bc77b4ad4..3f4208b83 100644 --- a/src/MicrophysicsFlexible.jl +++ b/src/MicrophysicsFlexible.jl @@ -46,10 +46,12 @@ Base.@kwdef mutable struct CLSetup{FT} kernel_limit::FT = FT(500) "Coalescence data" coal_data = nothing + "Sedimentation rate parameters" + vel::Vector{Tuple{FT,FT}} = [(FT(2.0), FT(1.0 / 6))] end """ - coalescence(CLSetup) + coalescence(clinfo) - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters TODO: currently implemented only for analytical coalescence style @@ -80,7 +82,7 @@ function coalescence(clinfo::CLSetup{FT}) where {FT} end """ - condensation(CLSetup) + condensation(clinfo, aps, tps, q, ρ, T) - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters - `aps` - air properties @@ -109,5 +111,15 @@ function condensation( return CL.Condensation.get_cond_evap(S, (; ξ=ξ, pdists=clinfo.pdists)) end +""" + sedimentation(clinfo) + + - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters +Returns the integrated fall speeds corresponding to the rate of change of prognostic moments +""" +function sedimentation(clinfo::CLSetup{FT}) where {FT} + return CL.Sedimentation.get_sedimentation_flux((; pdists=clinfo.pdists, vel=clinfo.vel)) +end + end #module \ No newline at end of file diff --git a/test/microphysicsflexible_tests.jl b/test/microphysicsflexible_tests.jl index 606a64cf1..9ef69248c 100644 --- a/test/microphysicsflexible_tests.jl +++ b/test/microphysicsflexible_tests.jl @@ -62,6 +62,10 @@ function test_microphysics_flexible(FT) TT.@test cond_evap[clinfo.NProgMoms[1]+1] == FT(0) # increase cloud mass density TT.@test cond_evap[2] > FT(0) + + # Test sedimentation + sed_int = CMF.sedimentation(clinfo) + TT.@test all(sed_int .< FT(0)) end end From 742c7ebe6e7774942baab6f20530de3e5a562b2f Mon Sep 17 00:00:00 2001 From: Emily de Jong Date: Fri, 1 Mar 2024 15:04:07 -0800 Subject: [PATCH 03/12] Make cloudy deps an extension instead --- Project.toml | 6 ++ ext/CloudyExt.jl | 139 +++++++++++++++++++++++++++++ src/MicrophysicsFlexible.jl | 112 +++-------------------- test/Project.toml | 2 +- test/microphysicsflexible_tests.jl | 33 ++++--- 5 files changed, 181 insertions(+), 111 deletions(-) create mode 100644 ext/CloudyExt.jl diff --git a/Project.toml b/Project.toml index c554c7d16..47b573f81 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,12 @@ RootSolvers = "7181ea78-2dcb-4de3-ab41-2b8ab5a31e74" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" Thermodynamics = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c" +[weakdeps] +Cloudy = "9e3b23bb-e7cc-4b94-886c-65de2234ba87" + +[extensions] +CloudyExt = "Cloudy" + [compat] ClimaParams = "0.10" DocStringExtensions = "0.8, 0.9" diff --git a/ext/CloudyExt.jl b/ext/CloudyExt.jl new file mode 100644 index 000000000..95201e06d --- /dev/null +++ b/ext/CloudyExt.jl @@ -0,0 +1,139 @@ +""" +Flexible N-moment microphysics representation, including: + - Generalized collisional-coalescence described by a kernel K(x,y), with + default parameterization based on Long collision kernel + - Power-series representation of fall-speed + - Power-series representation of condensational growth + TODO: no representation of ventilation effects +""" +module CloudyExt + +import Thermodynamics as TD +import Thermodynamics.Parameters as TDP +#import CloudMicrophysics +import CloudMicrophysics.Common as CO +import CloudMicrophysics.Parameters as CMP +import CloudMicrophysics.MicrophysicsFlexible: + CLSetup, coalescence, condensation, sedimentation + +import Cloudy as CL +import Cloudy.ParticleDistributions as CPD +import Cloudy.KernelFunctions as CLK + +""" +A structure containing the subdistributions, their moments, and additional +dynamical parameters corresponding to rates of collision, sedimentation, and +condensation/evaporation +""" +function CLSetup{FT}(; + pdists::Vector{<:CPD.PrimitiveParticleDistribution{FT}} = Vector([ + CPD.ExponentialPrimitiveParticleDistribution(FT(0), FT(1.0)), + CPD.GammaPrimitiveParticleDistribution(FT(0), FT(1.0), FT(1.0)), + ]), + mom::Vector{FT} = [FT(0) for i in 1:5], + NProgMoms::Vector{Int} = [Integer(CPD.nparams(dist)) for dist in pdists], + KernelFunc::CLK.KernelFunction{FT} = CLK.LongKernelFunction( + FT(0.5236), + FT(9.44e-3), + FT(5.78e-3), + ), + mass_thresholds::Vector{FT} = [10.0, Inf], + kernel_order::Int = 1, + kernel_limit::FT = FT(500), + coal_data = nothing, + vel::Vector{Tuple{FT, FT}} = [(FT(2.0), FT(1.0 / 6))], +) where {FT <: AbstractFloat} + + CLSetup{FT}( + pdists, + mom, + NProgMoms, + KernelFunc, + mass_thresholds, + kernel_order, + kernel_limit, + coal_data, + vel, + ) +end + +""" + coalescence(clinfo) + + - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters + TODO: currently implemented only for analytical coalescence style + +Returns a vector of moment tendencies due to collisional coalescence +""" +function coalescence(clinfo::CLSetup{FT}) where {FT} + kernel_tensor = CL.KernelTensors.CoalescenceTensor( + clinfo.KernelFunc, + clinfo.kernel_order, + clinfo.kernel_limit, + ) + if isnothing(clinfo.coal_data) + clinfo.coal_data = CL.Coalescence.initialize_coalescence_data( + CL.EquationTypes.AnalyticalCoalStyle(), + kernel_tensor, + clinfo.NProgMoms, + dist_thresholds = clinfo.mass_thresholds, + ) + end + # first, update the particle distributions + for (i, dist) in enumerate(clinfo.pdists) + ind_rng = CL.get_dist_moments_ind_range(clinfo.NProgMoms, i) + CPD.update_dist_from_moments!(dist, clinfo.mom[ind_rng]) + end + CL.Coalescence.update_coal_ints!( + CL.EquationTypes.AnalyticalCoalStyle(), + clinfo.pdists, + clinfo.coal_data, + ) + return clinfo.coal_data.coal_ints +end + +""" + condensation(clinfo, aps, tps, q, ρ, T) + + - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters + - `aps` - air properties + - `tps` - thermodynamics parameters + - `q` - phase partition + - `ρ` - air density + - `T` - air temperature +Returns a vector of moment tendencies due to condensation/evaporation +""" +function condensation( + clinfo::CLSetup{FT}, + aps::CMP.AirProperties{FT}, + tps::TDP.ThermodynamicsParameters{FT}, + q::TD.PhasePartition{FT}, + ρ::FT, + T::FT, +) where {FT} + S = TD.supersaturation(tps, q, ρ, T, TD.Liquid()) + ξ = CO.G_func(aps, tps, T, TD.Liquid()) + + # first, update the particle distributions + for (i, dist) in enumerate(clinfo.pdists) + ind_rng = CL.get_dist_moments_ind_range(clinfo.NProgMoms, i) + CPD.update_dist_from_moments!(dist, clinfo.mom[ind_rng]) + end + return CL.Condensation.get_cond_evap(S, (; ξ = ξ, pdists = clinfo.pdists)) +end + +""" + sedimentation(clinfo) + + - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters +Returns the integrated fall speeds corresponding to the rate of change of prognostic moments +""" +function sedimentation(clinfo::CLSetup{FT}) where {FT} + return CL.Sedimentation.get_sedimentation_flux((; + pdists = clinfo.pdists, + vel = clinfo.vel, + )) +end + + +end #module diff --git a/src/MicrophysicsFlexible.jl b/src/MicrophysicsFlexible.jl index 3f4208b83..a10a77aff 100644 --- a/src/MicrophysicsFlexible.jl +++ b/src/MicrophysicsFlexible.jl @@ -8,118 +8,32 @@ Flexible N-moment microphysics representation, including: """ module MicrophysicsFlexible -import Thermodynamics as TD -import Thermodynamics.Parameters as TDP - -import ..Common as CO -import ..Parameters as CMP - -import Cloudy as CL -import Cloudy.ParticleDistributions as CPD -import Cloudy.KernelFunctions as CLK - -export coalescence - -""" -A structure containing the subdistributions, their moments, and additional -dynamical parameters corresponding to rates of collision, sedimentation, and -condensation/evaporation -""" -Base.@kwdef mutable struct CLSetup{FT} +mutable struct CLSetup{FT} "Subdistributions of the hydrometeor size distribution; defaults to exponential cloud mode and gamma rain mode" - pdists::Vector{CPD.PrimitiveParticleDistribution{FT}} = Vector([ - CPD.ExponentialPrimitiveParticleDistribution(FT(0), FT(1.0)), - CPD.GammaPrimitiveParticleDistribution(FT(0), FT(1.0), FT(1.0)) - ]) + pdists::Vector "Moments of the subdistributions; should correspond with pdists" - mom::Vector{FT} = [FT(0) for i in 1:5] + mom::Vector{FT} "Total number of prognostic moments for each subdistribution" - NProgMoms::Vector{Int} = [Integer(CPD.nparams(dist)) for dist in pdists] + NProgMoms::Vector{Int} "Kernel function for collisional coalescence" - KernelFunc::CLK.KernelFunction{FT} = CLK.LongKernelFunction(FT(0.5236), FT(9.44e-3), FT(5.78e-3)) + KernelFunc::Any "Particle mass thresholds for analytical integration style" - mass_thresholds::Vector{FT} = [10.0, Inf] + mass_thresholds::Vector{FT} "Polynomial order of the kernel approximation for coalescence" - kernel_order::Int = 1 + kernel_order::Int "Upper limit for evaluation of the polynomial kernel approximation" - kernel_limit::FT = FT(500) + kernel_limit::FT "Coalescence data" - coal_data = nothing + coal_data::Any "Sedimentation rate parameters" - vel::Vector{Tuple{FT,FT}} = [(FT(2.0), FT(1.0 / 6))] -end - -""" - coalescence(clinfo) - - - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters - TODO: currently implemented only for analytical coalescence style - -Returns a vector of moment tendencies due to collisional coalescence -""" -function coalescence(clinfo::CLSetup{FT}) where {FT} - kernel_tensor = CL.KernelTensors.CoalescenceTensor(clinfo.KernelFunc, clinfo.kernel_order, clinfo.kernel_limit) - if isnothing(clinfo.coal_data) - clinfo.coal_data = CL.Coalescence.initialize_coalescence_data( - CL.EquationTypes.AnalyticalCoalStyle(), - kernel_tensor, - clinfo.NProgMoms, - dist_thresholds = clinfo.mass_thresholds - ) - end - # first, update the particle distributions - for (i, dist) in enumerate(clinfo.pdists) - ind_rng = CL.get_dist_moments_ind_range(clinfo.NProgMoms, i) - CPD.update_dist_from_moments!(dist, clinfo.mom[ind_rng]) - end - CL.Coalescence.update_coal_ints!( - CL.EquationTypes.AnalyticalCoalStyle(), - clinfo.pdists, - clinfo.coal_data - ) - return clinfo.coal_data.coal_ints + vel::Vector{Tuple{FT, FT}} end -""" - condensation(clinfo, aps, tps, q, ρ, T) - - - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters - - `aps` - air properties - - `tps` - thermodynamics parameters - - `q` - phase partition - - `ρ` - air density - - `T` - air temperature -Returns a vector of moment tendencies due to condensation/evaporation -""" -function condensation( - clinfo::CLSetup{FT}, - aps::CMP.AirProperties{FT}, - tps::TDP.ThermodynamicsParameters{FT}, - q::TD.PhasePartition{FT}, - ρ::FT, - T::FT, -) where {FT} - S = TD.supersaturation(tps, q, ρ, T, TD.Liquid()) - ξ = CO.G_func(aps, tps, T, TD.Liquid()) +function coalescence end - # first, update the particle distributions - for (i, dist) in enumerate(clinfo.pdists) - ind_rng = CL.get_dist_moments_ind_range(clinfo.NProgMoms, i) - CPD.update_dist_from_moments!(dist, clinfo.mom[ind_rng]) - end - return CL.Condensation.get_cond_evap(S, (; ξ=ξ, pdists=clinfo.pdists)) -end +function condensation end -""" - sedimentation(clinfo) +function sedimentation end - - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters -Returns the integrated fall speeds corresponding to the rate of change of prognostic moments -""" -function sedimentation(clinfo::CLSetup{FT}) where {FT} - return CL.Sedimentation.get_sedimentation_flux((; pdists=clinfo.pdists, vel=clinfo.vel)) end - - -end #module \ No newline at end of file diff --git a/test/Project.toml b/test/Project.toml index 44b0475e4..d7aac18c6 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -13,4 +13,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Thermodynamics = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c" [compat] -KernelAbstractions = "0.9" +KernelAbstractions = "0.9" \ No newline at end of file diff --git a/test/microphysicsflexible_tests.jl b/test/microphysicsflexible_tests.jl index 9ef69248c..3cde08205 100644 --- a/test/microphysicsflexible_tests.jl +++ b/test/microphysicsflexible_tests.jl @@ -1,7 +1,6 @@ import Test as TT import Thermodynamics as TD - import CloudMicrophysics.Parameters as CMP import CloudMicrophysics.MicrophysicsFlexible as CMF import Cloudy.ParticleDistributions as CPD @@ -16,16 +15,28 @@ function test_microphysics_flexible(FT) TT.@testset "Flexible microphysics - unit tests" begin # Create Cloudy struct with different distributions - clinfo = CMF.CLSetup{FT}( - pdists = [CPD.LognormalPrimitiveParticleDistribution(FT(10.0), FT(1.0), FT(1.0))], - mom = CPD.get_moments(CPD.LognormalPrimitiveParticleDistribution(FT(10.0), FT(1.0), FT(1.0))), + clinfo = CMF.CLSetup{FT}(; + pdists = [ + CPD.LognormalPrimitiveParticleDistribution( + FT(10.0), + FT(1.0), + FT(1.0), + ), + ], + mom = CPD.get_moments( + CPD.LognormalPrimitiveParticleDistribution( + FT(10.0), + FT(1.0), + FT(1.0), + ), + ), NProgMoms = [3], KernelFunc = CPK.ConstantKernelFunction(FT(1)), mass_thresholds = [FT(100.0)], kernel_order = 4, - kernel_limit = FT(100) + kernel_limit = FT(100), ) - + # Create Cloudy struct with defaults clinfo = CMF.CLSetup{FT}() TT.@test length(clinfo.pdists) == 2 @@ -38,7 +49,7 @@ function test_microphysics_flexible(FT) dmom = CMF.coalescence(clinfo) TT.@test ~isnothing(clinfo.coal_data) TT.@test all(dmom[1:clinfo.NProgMoms[1]] .< FT(0)) - TT.@test all(dmom[end - 1:end] .> FT(0)) + TT.@test all(dmom[(end - 1):end] .> FT(0)) # Test evaporation ρ = FT(1.1) @@ -48,10 +59,10 @@ function test_microphysics_flexible(FT) cond_evap = CMF.condensation(clinfo, aps, tps, q, ρ, T) # conserve number density TT.@test cond_evap[1] == FT(0) - TT.@test cond_evap[clinfo.NProgMoms[1]+1] == FT(0) + TT.@test cond_evap[clinfo.NProgMoms[1] + 1] == FT(0) # decrease mass density TT.@test cond_evap[2] < FT(0) - TT.@test cond_evap[clinfo.NProgMoms[1]+2] < FT(0) + TT.@test cond_evap[clinfo.NProgMoms[1] + 2] < FT(0) # Test condensation q_tot = FT(2e-2) @@ -59,7 +70,7 @@ function test_microphysics_flexible(FT) cond_evap = CMF.condensation(clinfo, aps, tps, q, ρ, T) # conserve number density TT.@test cond_evap[1] == FT(0) - TT.@test cond_evap[clinfo.NProgMoms[1]+1] == FT(0) + TT.@test cond_evap[clinfo.NProgMoms[1] + 1] == FT(0) # increase cloud mass density TT.@test cond_evap[2] > FT(0) @@ -73,4 +84,4 @@ println("Testing Float64") test_microphysics_flexible(Float64) # println("Testing Float32") -# test_microphysics_flexible(Float32) \ No newline at end of file +# test_microphysics_flexible(Float32) From 69b5f0179a873cc345f49bd43a42c395a1d0eb1c Mon Sep 17 00:00:00 2001 From: Emily de Jong Date: Fri, 1 Mar 2024 15:47:44 -0800 Subject: [PATCH 04/12] Add docs --- docs/src/API.md | 9 ++++++++ docs/src/MicrophysicsFlexible.md | 39 ++++++++++++++++++++++++++++++++ ext/CloudyExt.jl | 2 +- src/MicrophysicsFlexible.jl | 31 +++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 docs/src/MicrophysicsFlexible.md diff --git a/docs/src/API.md b/docs/src/API.md index 6b7e209f9..d41d6b02b 100644 --- a/docs/src/API.md +++ b/docs/src/API.md @@ -53,6 +53,15 @@ Microphysics2M.rain_evaporation Microphysics2M.conv_q_liq_to_q_rai ``` +# Flexible N-moment precipitation microphysics +```@docs +MicrophysicsFlexible +MicrophysicsFlexible.CLSetup +MicrophysicsFlexible.coalescence +MicrophysicsFlexible.condensation +MicrophysicsFlexible.sedimentation +``` + # P3 scheme ```@docs diff --git a/docs/src/MicrophysicsFlexible.md b/docs/src/MicrophysicsFlexible.md new file mode 100644 index 000000000..cebb35575 --- /dev/null +++ b/docs/src/MicrophysicsFlexible.md @@ -0,0 +1,39 @@ +# Microphysics Flexible + +The `MicrophysicsFlexible.jl` module relies on the extension defined in `ext/CloudyExt.jl`, based on a flexible N-moment microphysics scheme built in the external package `Cloudy.jl`. This option currently handles warm-rain processes including coalescence, condensation/evaporation, and sedimentation (terminal velocity). Unlike typical moment-based schemes which distinguish between categories such as rain and cloud, and which determine rates of conversion between categories (the canonical autoconversion, accretion, and self-collection), this option gives the user the flexibility to define as many or as few moments as they please, with these coalescence-based processes being solved directly without relying on conversion rates. Likewise, the rate of condensation/evaporation is defined through the rate of diffusion of water vapor to/from the surface of droplets defined by the subdistributions which underpin the method. The user has not only the flexibility to specify the number of moments (and therefore the complexity/accuracy) to use, but also the assumed size distributions corresponding to these moments. For instance, one might define a 5-moment implementation using an Exponential mode for smaller cloud droplets, plus a Gamma mode for larger rain droplets. Or, more creatively, perhaps a 12-moment implementation comprised of four Gamma modes. + +Options for dynamics and size distributions are under continuous development in the `Cloudy.jl` package, thus only the default and suggested use cases are described in detail here. + +## Moments and Sub-Distributions + +The prognostic variables of this parameterization are a set of N moments, which can be further divided into P sets of moments, each of which correponds to a subdistribution p. By design these moments begin at order 0 and increase as integers up to the maximum number of parameters for the chosen subdistribution. The first three such default moments have interpretable meanings: + - ``M_0`` - the number density of droplets [1/m^3] + - ``M_1`` - the mass density of droplets [kg/m^3] + - ``M_2`` - proportional to the radar reflectivity [kg^2/m^3] +and can be converted to more canonical definitions of `q_liq` and `q_rai` through numerical integration. + +When the user wishes to use more than 2 or 3 total variables to represent the system, these moments must be divided between ``P > 1`` sub-distributions, each of which assumes the form of a particular mathematical distribution, such as an Exponential, Lognormal, or Monodisperse (each of which has two parameters), or a Gamma distribution (which takes 3 parameters). + +## Loading the extension +The package `Cloudy.jl` and its dependencies are not loaded by default when using `CloudMicrophysics.jl`. Rather, one must specify: +``` +using CloudMicrophysics +using Cloudy +``` +from the Julia REPL. Upon recognizing that `Cloudy.jl` is being loaded, the extension `CloudyExt.jl` will then be loaded and overwrite the function stubs defined in `src/MicrophysicsFlexible.jl`. + +## Setting up a system +All the details from the number of moments and type of subdistributions, to the parameterizations of coalescence, condensation, and sedimentation are defined through the `CLSetup` (CLoudySetup) mutable struct. This struct is mutable specifically because certain of its components, such as backend-computed coalescence tendencies, are updated prior to being passed to the timestepper. The components of a `CLSetup` object and their defaults are further described below. + +| component | description | default | +|---------------------|------------------------------------------|----------------------------| +| ``pdists`` | Vector of subdistributions corresponding | ``[Exponential, Gamma]`` | +| | to the moments | | +| ``mom`` | Prognostic moments, in the same order as | ``[0, 0, 0, 0, 0]`` | +| | the corresponding subdistributions | | +| ``KernelFunc`` | Form of the coalescence kernel function | ``LongKernelFunction`` | +| ``mass_thresholds`` | Particle size thresholds for coalescence | ``[10.0, Inf]`` | +| | integration | | +| ``kernel order`` | Polynomial order for the approx. kernel | ``1`` | +| ``kernel_limit`` | Size threshold for approx. kernel | ``500`` | +| ``vel`` | Power-series coefficients for velocity | ``[2.0, 1/6]`` | \ No newline at end of file diff --git a/ext/CloudyExt.jl b/ext/CloudyExt.jl index 95201e06d..b61a1bbfb 100644 --- a/ext/CloudyExt.jl +++ b/ext/CloudyExt.jl @@ -10,7 +10,7 @@ module CloudyExt import Thermodynamics as TD import Thermodynamics.Parameters as TDP -#import CloudMicrophysics + import CloudMicrophysics.Common as CO import CloudMicrophysics.Parameters as CMP import CloudMicrophysics.MicrophysicsFlexible: diff --git a/src/MicrophysicsFlexible.jl b/src/MicrophysicsFlexible.jl index a10a77aff..a2edc798f 100644 --- a/src/MicrophysicsFlexible.jl +++ b/src/MicrophysicsFlexible.jl @@ -5,9 +5,15 @@ Flexible N-moment microphysics representation, including: - Power-series representation of fall-speed - Power-series representation of condensational growth TODO: no representation of ventilation effects + TODO: conversion back to N_rai, N_liq, q_rai, q_liq """ module MicrophysicsFlexible +""" +A structure containing the subdistributions, their moments, and additional +dynamical parameters corresponding to rates of collision, sedimentation, and +condensation/evaporation +""" mutable struct CLSetup{FT} "Subdistributions of the hydrometeor size distribution; defaults to exponential cloud mode and gamma rain mode" @@ -30,10 +36,35 @@ mutable struct CLSetup{FT} vel::Vector{Tuple{FT, FT}} end +""" + coalescence(clinfo) + + - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters + TODO: currently implemented only for analytical coalescence style + +Returns a vector of moment tendencies due to collisional coalescence +""" function coalescence end +""" + condensation(clinfo, aps, tps, q, ρ, T) + + - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters + - `aps` - air properties + - `tps` - thermodynamics parameters + - `q` - phase partition + - `ρ` - air density + - `T` - air temperature +Returns a vector of moment tendencies due to condensation/evaporation +""" function condensation end +""" + sedimentation(clinfo) + + - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters +Returns the integrated fall speeds corresponding to the rate of change of prognostic moments +""" function sedimentation end end From 8a9841ebd4692807d49f29f277b14612b408c3c8 Mon Sep 17 00:00:00 2001 From: Emily de Jong Date: Fri, 8 Mar 2024 11:55:36 -0800 Subject: [PATCH 05/12] Add condensation-only parcel model example for Cloudy --- Project.toml | 1 + ext/CloudyExt.jl | 14 +- parcel/Example_NMoment_condensation.jl | 182 +++++++++++++++++++++++++ parcel/Project.toml | 3 +- 4 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 parcel/Example_NMoment_condensation.jl diff --git a/Project.toml b/Project.toml index 47b573f81..f5806e08c 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.18.0" [deps] ClimaParams = "5c42b081-d73a-476f-9059-fd94b934656c" +Cloudy = "9e3b23bb-e7cc-4b94-886c-65de2234ba87" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" RootSolvers = "7181ea78-2dcb-4de3-ab41-2b8ab5a31e74" diff --git a/ext/CloudyExt.jl b/ext/CloudyExt.jl index b61a1bbfb..e116ff7f7 100644 --- a/ext/CloudyExt.jl +++ b/ext/CloudyExt.jl @@ -37,7 +37,7 @@ function CLSetup{FT}(; FT(9.44e-3), FT(5.78e-3), ), - mass_thresholds::Vector{FT} = [10.0, Inf], + mass_thresholds::Vector{FT} = [FT(10.0), FT(Inf)], kernel_order::Int = 1, kernel_limit::FT = FT(500), coal_data = nothing, @@ -98,20 +98,17 @@ end - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters - `aps` - air properties - `tps` - thermodynamics parameters - - `q` - phase partition - - `ρ` - air density - `T` - air temperature + - `S` - saturation ratio (supersaturation = S - 1) Returns a vector of moment tendencies due to condensation/evaporation """ function condensation( clinfo::CLSetup{FT}, aps::CMP.AirProperties{FT}, tps::TDP.ThermodynamicsParameters{FT}, - q::TD.PhasePartition{FT}, - ρ::FT, T::FT, + S::FT, ) where {FT} - S = TD.supersaturation(tps, q, ρ, T, TD.Liquid()) ξ = CO.G_func(aps, tps, T, TD.Liquid()) # first, update the particle distributions @@ -119,7 +116,10 @@ function condensation( ind_rng = CL.get_dist_moments_ind_range(clinfo.NProgMoms, i) CPD.update_dist_from_moments!(dist, clinfo.mom[ind_rng]) end - return CL.Condensation.get_cond_evap(S, (; ξ = ξ, pdists = clinfo.pdists)) + return CL.Condensation.get_cond_evap( + S - 1, + (; ξ = ξ, pdists = clinfo.pdists), + ) end """ diff --git a/parcel/Example_NMoment_condensation.jl b/parcel/Example_NMoment_condensation.jl new file mode 100644 index 000000000..0c76a77dd --- /dev/null +++ b/parcel/Example_NMoment_condensation.jl @@ -0,0 +1,182 @@ +import OrdinaryDiffEq as ODE +import CairoMakie as MK +import Thermodynamics as TD +import CloudMicrophysics as CM +import Cloudy as CL +import Cloudy.ParticleDistributions as CPD +import CloudMicrophysics.Parameters as CMP +import CloudMicrophysics.MicrophysicsFlexible as CMF +import ClimaParams as CP + +FT = Float64 + +""" + ODE problem definitions +""" +function parcel_model_cloudy(dY, Y, p, t) + # Numerical precision used in the simulation + FT = eltype(Y[5:end]) + # Simulation parameters + (; wps, tps, aps, w, clinfo) = p + # Y values + Sₗ = Y[1] + p_air = Y[2] + T = Y[3] + qᵥ = Y[4] + moments = Y[5:end] + + # Constants + Rᵥ = TD.Parameters.R_v(tps) + grav = TD.Parameters.grav(tps) + ρₗ = wps.ρw + + # Get thermodynamic parameters, phase partition and create thermo state. + q = TD.PhasePartition(qᵥ, FT(0), FT(0)) # use dry air density to compute ql + ts = TD.PhaseNonEquil_pTq(tps, p_air, T, q) + ρ_air = TD.air_density(tps, ts) + + qᵢ = FT(0.0) + qₗ = CPD.get_standard_N_q(clinfo.pdists).M_liq * ρₗ / ρ_air + q = TD.PhasePartition(qᵥ + qₗ + qᵢ, qₗ, qᵢ) + ts = TD.PhaseNonEquil_pTq(tps, p_air, T, q) + + # Constants and variables that depend on the moisture content + R_air = TD.gas_constant_air(tps, q) + cp_air = TD.cp_m(tps, q) + L_subl = TD.latent_heat_sublim(tps, T) + L_fus = TD.latent_heat_fusion(tps, T) + L_vap = TD.latent_heat_vapor(tps, T) + ρ_air = TD.air_density(tps, ts) + + # Adiabatic parcel coefficients + a1 = L_vap * grav / cp_air / T^2 / Rᵥ - grav / R_air / T + a2 = 1 / qᵥ + a3 = L_vap^2 / Rᵥ / T^2 / cp_air + a4 = L_vap * L_subl / Rᵥ / T^2 / cp_air + a5 = L_vap * L_fus / Rᵥ / cp_air / (T^2) + + # Cloudy condnsational growth / evaporation + dmom_ce = CMF.condensation(clinfo, aps, tps, T, Sₗ) + @show dmom_ce + + # ... water mass budget + dqₗ_dt_v2l = dmom_ce[2] + dmom_ce[4] + + # Update the tendecies + dqₗ_dt = dqₗ_dt_v2l + dqᵥ_dt = -dqₗ_dt + + dSₗ_dt = + a1 * w * Sₗ - (a2 + a3) * Sₗ * dqₗ_dt_v2l + + dp_air_dt = -p_air * grav / R_air / T * w + + dT_dt = + -grav / cp_air * w + + L_vap / cp_air * dqₗ_dt_v2l + + # Set tendencies + dY[1] = dSₗ_dt # saturation ratio over liquid water + dY[2] = dp_air_dt # pressure + dY[3] = dT_dt # temperature + dY[4] = dqᵥ_dt # vapor specific humidity + dY[5:end] = dmom_ce +end + +function run_parcel_cloudy(Yinit, clinfo, t_0, t_end, pp) + FT = typeof(t_0) + + println(" ") + println("Size distributions: ", clinfo.pdists) + println("Condensation growth only ") + + # Parameters for the ODE solver + p = ( + wps = pp.wps, + aps = pp.aps, + tps = pp.tps, + w = pp.w, + clinfo = clinfo + ) + + problem = ODE.ODEProblem(parcel_model_cloudy, Yinit, (FT(t_0), FT(t_end)), p) + return ODE.solve( + problem, + ODE.Euler(), + dt = pp.const_dt, + reltol = 100 * eps(FT), + abstol = 100 * eps(FT), + ) +end + +# Get free parameters +tps = TD.Parameters.ThermodynamicsParameters(FT) +wps = CMP.WaterProperties(FT) +aps = CMP.AirProperties(FT) +# Constants +ρₗ = wps.ρw +ρᵢ = wps.ρi +R_v = TD.Parameters.R_v(tps) +R_d = TD.Parameters.R_d(tps) + +# Initial conditions +dist_init = [ + CPD.ExponentialPrimitiveParticleDistribution(FT(100 * 1e6), FT(1e5*1e-18)), # 100/cm^3; 10^5 µm^3 + CPD.GammaPrimitiveParticleDistribution(FT(1 * 1e6), FT(1e6*1e-18), FT(1)), # 1/cm^3; 10^6 µm^3; k=1 +] +moments_init = FT.([100.0 * 1e6, 1e-5, 10.0 * 1e6, 1e-6, 2e-18]) +p₀ = FT(800 * 1e2) +T₀ = FT(273.15 + 7.0) +e = TD.saturation_vapor_pressure(tps, T₀, TD.Liquid()) +Sₗ = FT(1) +md_v = (p₀ - e) / R_d / T₀ +mv_v = e / R_v / T₀ +ml_v = moments_init[2] + moments_init[4] +qᵥ = mv_v / (md_v + mv_v + ml_v) +qₗ = ml_v / (md_v + mv_v + ml_v) +qᵢ = FT(0) + +# Simulation parameters passed into ODE solver +w = FT(10) # updraft speed +const_dt = FT(0.5) # model timestep +t_max = FT(20) +clinfo = CMF.CLSetup{FT}( + pdists = dist_init, + mom = moments_init +) + +Y0 = [Sₗ, p₀, T₀, qᵥ, moments_init...] +dY = zeros(FT,9) +p = ( + wps = wps, + aps = aps, + tps = tps, + w = w, + clinfo = clinfo, + const_dt = const_dt +) + +# Setup the plots +fig = MK.Figure(size = (800, 600)) +ax1 = MK.Axis(fig[1, 1], ylabel = "Supersaturation [%]") +ax2 = MK.Axis(fig[3, 1], xlabel = "Time [s]", ylabel = "Temperature [K]") +ax3 = MK.Axis(fig[2, 1], ylabel = "q_vap [g/kg]") +ax4 = MK.Axis(fig[2, 2], xlabel = "Time [s]", ylabel = "q_liq [g/kg]") +ax5 = MK.Axis(fig[1, 2], ylabel = "radius [μm]") + +# solve ODE +sol = run_parcel_cloudy(Y0, clinfo, FT(0), t_max, p) +@show sol.u + +# plot results - time series +MK.lines!(ax1, sol.t, (sol[1, :] .- 1) * 100.0) +MK.lines!(ax2, sol.t, sol[3, :]) +MK.lines!(ax3, sol.t, sol[4, :] * 1e3) +ρ_air = sol[2, :] / R_v ./ sol[3, :] +M_l = sol[6, :] + sol[8, :] # kg / m^3 air +N_l = sol[5, :] + sol[7, :] # number / m^3 air +r_l = (M_l ./ N_l / ρₗ / 4 / π * 3).^(1/3) * 1e6 +MK.lines!(ax4, sol.t, M_l ./ ρ_air * 1e3) +MK.lines!(ax5, sol.t, r_l) + +MK.save("cloudy_parcel.svg", fig) \ No newline at end of file diff --git a/parcel/Project.toml b/parcel/Project.toml index 0129945f4..0d19bba40 100644 --- a/parcel/Project.toml +++ b/parcel/Project.toml @@ -1,7 +1,8 @@ [deps] -ClimaParams = "5c42b081-d73a-476f-9059-fd94b934656c" CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" +ClimaParams = "5c42b081-d73a-476f-9059-fd94b934656c" CloudMicrophysics = "6a9e3e04-43cd-43ba-94b9-e8782df3c71b" +Cloudy = "9e3b23bb-e7cc-4b94-886c-65de2234ba87" Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" RootSolvers = "7181ea78-2dcb-4de3-ab41-2b8ab5a31e74" From 299095ed42c70354bd2f1b5b9a540fb29284d15f Mon Sep 17 00:00:00 2001 From: Emily de Jong Date: Fri, 8 Mar 2024 12:00:04 -0800 Subject: [PATCH 06/12] Add compat entry for Cloudy --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index f5806e08c..41e88acaa 100644 --- a/Project.toml +++ b/Project.toml @@ -5,7 +5,6 @@ version = "0.18.0" [deps] ClimaParams = "5c42b081-d73a-476f-9059-fd94b934656c" -Cloudy = "9e3b23bb-e7cc-4b94-886c-65de2234ba87" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" RootSolvers = "7181ea78-2dcb-4de3-ab41-2b8ab5a31e74" @@ -20,6 +19,7 @@ CloudyExt = "Cloudy" [compat] ClimaParams = "0.10" +Cloudy = "0.3.4" DocStringExtensions = "0.8, 0.9" ForwardDiff = "0.10" RootSolvers = "0.3, 0.4" From 0bb5ce05f1021dac81435bcdbe1b4df50d7eaac7 Mon Sep 17 00:00:00 2001 From: edejong-caltech Date: Sat, 9 Mar 2024 12:30:22 -0800 Subject: [PATCH 07/12] Update Cloudy version # --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index d16dba727..32532a7ce 100644 --- a/Project.toml +++ b/Project.toml @@ -22,7 +22,7 @@ EmulatorModelsExt = ["DataFrames", "MLJ"] [compat] ClimaParams = "0.10" -Cloudy = "0.3.4" +Cloudy = "0.4.0" DataFrames = "1.6" DocStringExtensions = "0.8, 0.9" ForwardDiff = "0.10" From 888648660a066cef392efa0ed0cf80d8eb0ba132 Mon Sep 17 00:00:00 2001 From: Emily de Jong Date: Mon, 11 Mar 2024 13:38:04 -0700 Subject: [PATCH 08/12] Rename sedimentation + fix condensation tests --- docs/src/API.md | 2 +- ext/CloudyExt.jl | 9 +++++---- src/MicrophysicsFlexible.jl | 4 ++-- test/microphysicsflexible_tests.jl | 15 ++++++--------- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/docs/src/API.md b/docs/src/API.md index d41d6b02b..3fbf1c802 100644 --- a/docs/src/API.md +++ b/docs/src/API.md @@ -59,7 +59,7 @@ MicrophysicsFlexible MicrophysicsFlexible.CLSetup MicrophysicsFlexible.coalescence MicrophysicsFlexible.condensation -MicrophysicsFlexible.sedimentation +MicrophysicsFlexible.weighted_vt ``` # P3 scheme diff --git a/ext/CloudyExt.jl b/ext/CloudyExt.jl index e116ff7f7..061778c07 100644 --- a/ext/CloudyExt.jl +++ b/ext/CloudyExt.jl @@ -14,7 +14,7 @@ import Thermodynamics.Parameters as TDP import CloudMicrophysics.Common as CO import CloudMicrophysics.Parameters as CMP import CloudMicrophysics.MicrophysicsFlexible: - CLSetup, coalescence, condensation, sedimentation + CLSetup, coalescence, condensation, weighted_vt import Cloudy as CL import Cloudy.ParticleDistributions as CPD @@ -123,16 +123,17 @@ function condensation( end """ - sedimentation(clinfo) + weighted_vt(clinfo) - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters Returns the integrated fall speeds corresponding to the rate of change of prognostic moments """ -function sedimentation(clinfo::CLSetup{FT}) where {FT} - return CL.Sedimentation.get_sedimentation_flux((; +function weighted_vt(clinfo::CLSetup{FT}) where {FT} + sed_flux = CL.Sedimentation.get_sedimentation_flux((; pdists = clinfo.pdists, vel = clinfo.vel, )) + return -sed_flux ./ clinfo.mom end diff --git a/src/MicrophysicsFlexible.jl b/src/MicrophysicsFlexible.jl index a2edc798f..8caed9b50 100644 --- a/src/MicrophysicsFlexible.jl +++ b/src/MicrophysicsFlexible.jl @@ -60,11 +60,11 @@ Returns a vector of moment tendencies due to condensation/evaporation function condensation end """ - sedimentation(clinfo) + weighted_vt(clinfo) - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters Returns the integrated fall speeds corresponding to the rate of change of prognostic moments """ -function sedimentation end +function weighted_vt end end diff --git a/test/microphysicsflexible_tests.jl b/test/microphysicsflexible_tests.jl index 3cde08205..c67534bf2 100644 --- a/test/microphysicsflexible_tests.jl +++ b/test/microphysicsflexible_tests.jl @@ -52,11 +52,9 @@ function test_microphysics_flexible(FT) TT.@test all(dmom[(end - 1):end] .> FT(0)) # Test evaporation - ρ = FT(1.1) T = FT(288.15) - q_tot = FT(1e-3) - q = TD.PhasePartition(q_tot) - cond_evap = CMF.condensation(clinfo, aps, tps, q, ρ, T) + S = 0.99 + cond_evap = CMF.condensation(clinfo, aps, tps, T, S) # conserve number density TT.@test cond_evap[1] == FT(0) TT.@test cond_evap[clinfo.NProgMoms[1] + 1] == FT(0) @@ -65,9 +63,8 @@ function test_microphysics_flexible(FT) TT.@test cond_evap[clinfo.NProgMoms[1] + 2] < FT(0) # Test condensation - q_tot = FT(2e-2) - q = TD.PhasePartition(q_tot) - cond_evap = CMF.condensation(clinfo, aps, tps, q, ρ, T) + S = 1.01 + cond_evap = CMF.condensation(clinfo, aps, tps, T, S) # conserve number density TT.@test cond_evap[1] == FT(0) TT.@test cond_evap[clinfo.NProgMoms[1] + 1] == FT(0) @@ -75,8 +72,8 @@ function test_microphysics_flexible(FT) TT.@test cond_evap[2] > FT(0) # Test sedimentation - sed_int = CMF.sedimentation(clinfo) - TT.@test all(sed_int .< FT(0)) + weighted_vt = CMF.weighted_vt(clinfo) + TT.@test all(weighted_vt .> FT(0)) end end From 67979fdb8e66f18f9e476e29f2b48ed2ad627efc Mon Sep 17 00:00:00 2001 From: Emily de Jong Date: Mon, 11 Mar 2024 14:14:01 -0700 Subject: [PATCH 09/12] Put units into SI --- ext/CloudyExt.jl | 18 +++++++++--------- parcel/Example_NMoment_condensation.jl | 6 +++--- test/microphysicsflexible_tests.jl | 1 - 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/ext/CloudyExt.jl b/ext/CloudyExt.jl index 061778c07..47f88293d 100644 --- a/ext/CloudyExt.jl +++ b/ext/CloudyExt.jl @@ -27,21 +27,21 @@ condensation/evaporation """ function CLSetup{FT}(; pdists::Vector{<:CPD.PrimitiveParticleDistribution{FT}} = Vector([ - CPD.ExponentialPrimitiveParticleDistribution(FT(0), FT(1.0)), - CPD.GammaPrimitiveParticleDistribution(FT(0), FT(1.0), FT(1.0)), + CPD.ExponentialPrimitiveParticleDistribution(FT(100 * 1e6), FT(1e5*1e-18*1e3)), # 100/cm^3; 10^5 µm^3 = 1e-10 kg + CPD.GammaPrimitiveParticleDistribution(FT(1 * 1e6), FT(1e6*1e-18*1e3), FT(1)), # 1/cm^3; 10^6 µm^3 = 1e-9 kg; k=1 ]), - mom::Vector{FT} = [FT(0) for i in 1:5], + mom::Vector{FT} = FT.([100.0 * 1e6, 1e-2, 1.0 * 1e6, 1e-3, 2e-12]), NProgMoms::Vector{Int} = [Integer(CPD.nparams(dist)) for dist in pdists], KernelFunc::CLK.KernelFunction{FT} = CLK.LongKernelFunction( - FT(0.5236), - FT(9.44e-3), - FT(5.78e-3), + FT(5.236e-10), # 5.236e-10 kg; + FT(9.44e9), # 9.44e9 m^3/kg^2/s; + FT(5.78), # 5.78 m^3/kg/s ), - mass_thresholds::Vector{FT} = [FT(10.0), FT(Inf)], + mass_thresholds::Vector{FT} = [FT(1e-5), FT(Inf)], # TODO: ~100µm Diam kernel_order::Int = 1, - kernel_limit::FT = FT(500), + kernel_limit::FT = FT(1 * 1e-9 * 1e3), # ~1mm Diam ~ 1 mm^3 volume coal_data = nothing, - vel::Vector{Tuple{FT, FT}} = [(FT(2.0), FT(1.0 / 6))], + vel::Vector{Tuple{FT, FT}} = [(FT(50.0), FT(1.0 / 6))], # 50 m/s/kg^(1/6) ) where {FT <: AbstractFloat} CLSetup{FT}( diff --git a/parcel/Example_NMoment_condensation.jl b/parcel/Example_NMoment_condensation.jl index 0c76a77dd..bece42d90 100644 --- a/parcel/Example_NMoment_condensation.jl +++ b/parcel/Example_NMoment_condensation.jl @@ -121,10 +121,10 @@ R_d = TD.Parameters.R_d(tps) # Initial conditions dist_init = [ - CPD.ExponentialPrimitiveParticleDistribution(FT(100 * 1e6), FT(1e5*1e-18)), # 100/cm^3; 10^5 µm^3 - CPD.GammaPrimitiveParticleDistribution(FT(1 * 1e6), FT(1e6*1e-18), FT(1)), # 1/cm^3; 10^6 µm^3; k=1 + CPD.ExponentialPrimitiveParticleDistribution(FT(100 * 1e6), FT(1e5*1e-18*1e3)), # 100/cm^3; 10^5 µm^3 + CPD.GammaPrimitiveParticleDistribution(FT(1 * 1e6), FT(1e6*1e-18*1e3), FT(1)), # 1/cm^3; 10^6 µm^3; k=1 ] -moments_init = FT.([100.0 * 1e6, 1e-5, 10.0 * 1e6, 1e-6, 2e-18]) +moments_init = FT.([100.0 * 1e6, 1e-2, 1.0 * 1e6, 1e-3, 2e-12]) p₀ = FT(800 * 1e2) T₀ = FT(273.15 + 7.0) e = TD.saturation_vapor_pressure(tps, T₀, TD.Liquid()) diff --git a/test/microphysicsflexible_tests.jl b/test/microphysicsflexible_tests.jl index c67534bf2..7a92fb527 100644 --- a/test/microphysicsflexible_tests.jl +++ b/test/microphysicsflexible_tests.jl @@ -44,7 +44,6 @@ function test_microphysics_flexible(FT) TT.@test length(clinfo.NProgMoms) == 2 # Test coalescence - clinfo.mom = [100.0, 10.0, 1.0, 10.0, 20.0] TT.@test isnothing(clinfo.coal_data) dmom = CMF.coalescence(clinfo) TT.@test ~isnothing(clinfo.coal_data) From 0db2f2e7d76fcd5b85fa060008cfe04c99f58107 Mon Sep 17 00:00:00 2001 From: Emily de Jong Date: Mon, 11 Mar 2024 14:15:09 -0700 Subject: [PATCH 10/12] Julia Formatter --- ext/CloudyExt.jl | 11 ++++-- parcel/Example_NMoment_condensation.jl | 50 ++++++++++++-------------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/ext/CloudyExt.jl b/ext/CloudyExt.jl index 47f88293d..faec9ddba 100644 --- a/ext/CloudyExt.jl +++ b/ext/CloudyExt.jl @@ -27,8 +27,15 @@ condensation/evaporation """ function CLSetup{FT}(; pdists::Vector{<:CPD.PrimitiveParticleDistribution{FT}} = Vector([ - CPD.ExponentialPrimitiveParticleDistribution(FT(100 * 1e6), FT(1e5*1e-18*1e3)), # 100/cm^3; 10^5 µm^3 = 1e-10 kg - CPD.GammaPrimitiveParticleDistribution(FT(1 * 1e6), FT(1e6*1e-18*1e3), FT(1)), # 1/cm^3; 10^6 µm^3 = 1e-9 kg; k=1 + CPD.ExponentialPrimitiveParticleDistribution( + FT(100 * 1e6), + FT(1e5 * 1e-18 * 1e3), + ), # 100/cm^3; 10^5 µm^3 = 1e-10 kg + CPD.GammaPrimitiveParticleDistribution( + FT(1 * 1e6), + FT(1e6 * 1e-18 * 1e3), + FT(1), + ), # 1/cm^3; 10^6 µm^3 = 1e-9 kg; k=1 ]), mom::Vector{FT} = FT.([100.0 * 1e6, 1e-2, 1.0 * 1e6, 1e-3, 2e-12]), NProgMoms::Vector{Int} = [Integer(CPD.nparams(dist)) for dist in pdists], diff --git a/parcel/Example_NMoment_condensation.jl b/parcel/Example_NMoment_condensation.jl index bece42d90..19dfdfe02 100644 --- a/parcel/Example_NMoment_condensation.jl +++ b/parcel/Example_NMoment_condensation.jl @@ -34,7 +34,7 @@ function parcel_model_cloudy(dY, Y, p, t) q = TD.PhasePartition(qᵥ, FT(0), FT(0)) # use dry air density to compute ql ts = TD.PhaseNonEquil_pTq(tps, p_air, T, q) ρ_air = TD.air_density(tps, ts) - + qᵢ = FT(0.0) qₗ = CPD.get_standard_N_q(clinfo.pdists).M_liq * ρₗ / ρ_air q = TD.PhasePartition(qᵥ + qₗ + qᵢ, qₗ, qᵢ) @@ -66,14 +66,11 @@ function parcel_model_cloudy(dY, Y, p, t) dqₗ_dt = dqₗ_dt_v2l dqᵥ_dt = -dqₗ_dt - dSₗ_dt = - a1 * w * Sₗ - (a2 + a3) * Sₗ * dqₗ_dt_v2l + dSₗ_dt = a1 * w * Sₗ - (a2 + a3) * Sₗ * dqₗ_dt_v2l dp_air_dt = -p_air * grav / R_air / T * w - dT_dt = - -grav / cp_air * w + - L_vap / cp_air * dqₗ_dt_v2l + dT_dt = -grav / cp_air * w + L_vap / cp_air * dqₗ_dt_v2l # Set tendencies dY[1] = dSₗ_dt # saturation ratio over liquid water @@ -91,15 +88,10 @@ function run_parcel_cloudy(Yinit, clinfo, t_0, t_end, pp) println("Condensation growth only ") # Parameters for the ODE solver - p = ( - wps = pp.wps, - aps = pp.aps, - tps = pp.tps, - w = pp.w, - clinfo = clinfo - ) + p = (wps = pp.wps, aps = pp.aps, tps = pp.tps, w = pp.w, clinfo = clinfo) - problem = ODE.ODEProblem(parcel_model_cloudy, Yinit, (FT(t_0), FT(t_end)), p) + problem = + ODE.ODEProblem(parcel_model_cloudy, Yinit, (FT(t_0), FT(t_end)), p) return ODE.solve( problem, ODE.Euler(), @@ -121,8 +113,15 @@ R_d = TD.Parameters.R_d(tps) # Initial conditions dist_init = [ - CPD.ExponentialPrimitiveParticleDistribution(FT(100 * 1e6), FT(1e5*1e-18*1e3)), # 100/cm^3; 10^5 µm^3 - CPD.GammaPrimitiveParticleDistribution(FT(1 * 1e6), FT(1e6*1e-18*1e3), FT(1)), # 1/cm^3; 10^6 µm^3; k=1 + CPD.ExponentialPrimitiveParticleDistribution( + FT(100 * 1e6), + FT(1e5 * 1e-18 * 1e3), + ), # 100/cm^3; 10^5 µm^3 + CPD.GammaPrimitiveParticleDistribution( + FT(1 * 1e6), + FT(1e6 * 1e-18 * 1e3), + FT(1), + ), # 1/cm^3; 10^6 µm^3; k=1 ] moments_init = FT.([100.0 * 1e6, 1e-2, 1.0 * 1e6, 1e-3, 2e-12]) p₀ = FT(800 * 1e2) @@ -140,20 +139,17 @@ qᵢ = FT(0) w = FT(10) # updraft speed const_dt = FT(0.5) # model timestep t_max = FT(20) -clinfo = CMF.CLSetup{FT}( - pdists = dist_init, - mom = moments_init -) +clinfo = CMF.CLSetup{FT}(pdists = dist_init, mom = moments_init) Y0 = [Sₗ, p₀, T₀, qᵥ, moments_init...] -dY = zeros(FT,9) +dY = zeros(FT, 9) p = ( wps = wps, aps = aps, - tps = tps, - w = w, - clinfo = clinfo, - const_dt = const_dt + tps = tps, + w = w, + clinfo = clinfo, + const_dt = const_dt, ) # Setup the plots @@ -175,8 +171,8 @@ MK.lines!(ax3, sol.t, sol[4, :] * 1e3) ρ_air = sol[2, :] / R_v ./ sol[3, :] M_l = sol[6, :] + sol[8, :] # kg / m^3 air N_l = sol[5, :] + sol[7, :] # number / m^3 air -r_l = (M_l ./ N_l / ρₗ / 4 / π * 3).^(1/3) * 1e6 +r_l = (M_l ./ N_l / ρₗ / 4 / π * 3) .^ (1 / 3) * 1e6 MK.lines!(ax4, sol.t, M_l ./ ρ_air * 1e3) MK.lines!(ax5, sol.t, r_l) -MK.save("cloudy_parcel.svg", fig) \ No newline at end of file +MK.save("cloudy_parcel.svg", fig) From e9b7671d8217d4fc4dff56176b816c8e76256cbc Mon Sep 17 00:00:00 2001 From: Emily de Jong Date: Mon, 11 Mar 2024 14:50:10 -0700 Subject: [PATCH 11/12] Add normalization factors --- ext/CloudyExt.jl | 31 +++++++++++++++++--------- parcel/Example_NMoment_condensation.jl | 2 +- src/MicrophysicsFlexible.jl | 2 ++ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/ext/CloudyExt.jl b/ext/CloudyExt.jl index faec9ddba..65aa4d572 100644 --- a/ext/CloudyExt.jl +++ b/ext/CloudyExt.jl @@ -44,11 +44,12 @@ function CLSetup{FT}(; FT(9.44e9), # 9.44e9 m^3/kg^2/s; FT(5.78), # 5.78 m^3/kg/s ), - mass_thresholds::Vector{FT} = [FT(1e-5), FT(Inf)], # TODO: ~100µm Diam + mass_thresholds::Vector{FT} = [FT(1e-9), FT(Inf)], kernel_order::Int = 1, kernel_limit::FT = FT(1 * 1e-9 * 1e3), # ~1mm Diam ~ 1 mm^3 volume coal_data = nothing, - vel::Vector{Tuple{FT, FT}} = [(FT(50.0), FT(1.0 / 6))], # 50 m/s/kg^(1/6) + vel::Vector{Tuple{FT, FT}} = [(FT(50.0), FT(1.0 / 6))], # 50 m/s/kg^(1/6), + norms::Vector{FT} = [1e6, 1e-9], # 1e6 / m^3; 1e-9 kg ) where {FT <: AbstractFloat} CLSetup{FT}( @@ -61,6 +62,7 @@ function CLSetup{FT}(; kernel_limit, coal_data, vel, + norms, ) end @@ -83,20 +85,24 @@ function coalescence(clinfo::CLSetup{FT}) where {FT} CL.EquationTypes.AnalyticalCoalStyle(), kernel_tensor, clinfo.NProgMoms, + norms = clinfo.norms, dist_thresholds = clinfo.mass_thresholds, ) end + mom_norms = + CL.get_moments_normalizing_factors(clinfo.NProgMoms, clinfo.norms) + mom_normalized = clinfo.mom ./ mom_norms # first, update the particle distributions for (i, dist) in enumerate(clinfo.pdists) ind_rng = CL.get_dist_moments_ind_range(clinfo.NProgMoms, i) - CPD.update_dist_from_moments!(dist, clinfo.mom[ind_rng]) + CPD.update_dist_from_moments!(dist, mom_normalized[ind_rng]) #clinfo.mom[ind_rng]) end CL.Coalescence.update_coal_ints!( CL.EquationTypes.AnalyticalCoalStyle(), clinfo.pdists, clinfo.coal_data, ) - return clinfo.coal_data.coal_ints + return clinfo.coal_data.coal_ints .* mom_norms end """ @@ -118,15 +124,18 @@ function condensation( ) where {FT} ξ = CO.G_func(aps, tps, T, TD.Liquid()) + mom_norms = + CL.get_moments_normalizing_factors(clinfo.NProgMoms, clinfo.norms) + mom_normalized = clinfo.mom ./ mom_norms # first, update the particle distributions for (i, dist) in enumerate(clinfo.pdists) ind_rng = CL.get_dist_moments_ind_range(clinfo.NProgMoms, i) - CPD.update_dist_from_moments!(dist, clinfo.mom[ind_rng]) + CPD.update_dist_from_moments!(dist, mom_normalized[ind_rng]) end return CL.Condensation.get_cond_evap( S - 1, (; ξ = ξ, pdists = clinfo.pdists), - ) + ) .* mom_norms end """ @@ -136,10 +145,12 @@ end Returns the integrated fall speeds corresponding to the rate of change of prognostic moments """ function weighted_vt(clinfo::CLSetup{FT}) where {FT} - sed_flux = CL.Sedimentation.get_sedimentation_flux((; - pdists = clinfo.pdists, - vel = clinfo.vel, - )) + for (i, dist) in enumerate(clinfo.pdists) + ind_rng = CL.get_dist_moments_ind_range(clinfo.NProgMoms, i) + CPD.update_dist_from_moments!(dist, clinfo.mom[ind_rng]) + end + sed_flux = + CL.Sedimentation.get_sedimentation_flux(clinfo.pdists, clinfo.vel) return -sed_flux ./ clinfo.mom end diff --git a/parcel/Example_NMoment_condensation.jl b/parcel/Example_NMoment_condensation.jl index 19dfdfe02..8326d508a 100644 --- a/parcel/Example_NMoment_condensation.jl +++ b/parcel/Example_NMoment_condensation.jl @@ -136,7 +136,7 @@ qₗ = ml_v / (md_v + mv_v + ml_v) qᵢ = FT(0) # Simulation parameters passed into ODE solver -w = FT(10) # updraft speed +w = FT(1) # updraft speed const_dt = FT(0.5) # model timestep t_max = FT(20) clinfo = CMF.CLSetup{FT}(pdists = dist_init, mom = moments_init) diff --git a/src/MicrophysicsFlexible.jl b/src/MicrophysicsFlexible.jl index 8caed9b50..81cea0c78 100644 --- a/src/MicrophysicsFlexible.jl +++ b/src/MicrophysicsFlexible.jl @@ -34,6 +34,8 @@ mutable struct CLSetup{FT} coal_data::Any "Sedimentation rate parameters" vel::Vector{Tuple{FT, FT}} + "Normalizing factors for number density and particle mass" + norms::Vector{FT} end """ From bd821e2be061515d34852d9fd6c53145beeb0e92 Mon Sep 17 00:00:00 2001 From: edejong-caltech Date: Tue, 12 Mar 2024 17:47:43 -0700 Subject: [PATCH 12/12] Fix documentation errors --- Project.toml | 2 +- docs/make.jl | 1 + docs/src/MicrophysicsFlexible.md | 28 +++++++++++++++------------- src/MicrophysicsFlexible.jl | 3 ++- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Project.toml b/Project.toml index 32532a7ce..249ac8029 100644 --- a/Project.toml +++ b/Project.toml @@ -22,7 +22,7 @@ EmulatorModelsExt = ["DataFrames", "MLJ"] [compat] ClimaParams = "0.10" -Cloudy = "0.4.0" +Cloudy = "0.4" DataFrames = "1.6" DocStringExtensions = "0.8, 0.9" ForwardDiff = "0.10" diff --git a/docs/make.jl b/docs/make.jl index f937a1a4d..876fccbe6 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -37,6 +37,7 @@ Parameterizations = [ "0-moment precipitation microphysics" => "Microphysics0M.md", "1-moment precipitation microphysics" => "Microphysics1M.md", "2-moment precipitation microphysics" => "Microphysics2M.md", + "N-moment precipitation microphysics" => "MicrophysicsFlexible.md", "P3 Scheme" => "P3Scheme.md", "Non-equilibrium cloud formation" => "MicrophysicsNonEq.md", "Smooth transition at thresholds" => "ThresholdsTransition.md", diff --git a/docs/src/MicrophysicsFlexible.md b/docs/src/MicrophysicsFlexible.md index cebb35575..28374db7d 100644 --- a/docs/src/MicrophysicsFlexible.md +++ b/docs/src/MicrophysicsFlexible.md @@ -1,6 +1,6 @@ # Microphysics Flexible -The `MicrophysicsFlexible.jl` module relies on the extension defined in `ext/CloudyExt.jl`, based on a flexible N-moment microphysics scheme built in the external package `Cloudy.jl`. This option currently handles warm-rain processes including coalescence, condensation/evaporation, and sedimentation (terminal velocity). Unlike typical moment-based schemes which distinguish between categories such as rain and cloud, and which determine rates of conversion between categories (the canonical autoconversion, accretion, and self-collection), this option gives the user the flexibility to define as many or as few moments as they please, with these coalescence-based processes being solved directly without relying on conversion rates. Likewise, the rate of condensation/evaporation is defined through the rate of diffusion of water vapor to/from the surface of droplets defined by the subdistributions which underpin the method. The user has not only the flexibility to specify the number of moments (and therefore the complexity/accuracy) to use, but also the assumed size distributions corresponding to these moments. For instance, one might define a 5-moment implementation using an Exponential mode for smaller cloud droplets, plus a Gamma mode for larger rain droplets. Or, more creatively, perhaps a 12-moment implementation comprised of four Gamma modes. +The `MicrophysicsFlexible.jl` module relies on the extension defined in `ext/CloudyExt.jl`, based on a flexible N-moment microphysics scheme built in the external package [Cloudy.jl](https://github.com/CliMA/Cloudy.jl). This option currently handles warm-rain processes including coalescence, condensation/evaporation, and sedimentation (terminal velocity). Unlike typical moment-based schemes which distinguish between categories such as rain and cloud, and which determine rates of conversion between categories (the canonical autoconversion, accretion, and self-collection), this option gives the user the flexibility to define as many or as few moments as they please, with these coalescence-based processes being solved directly without relying on conversion rates. Likewise, the rate of condensation/evaporation is defined through the rate of diffusion of water vapor to/from the surface of droplets defined by the subdistributions which underpin the method. The user has not only the flexibility to specify the number of moments (and therefore the complexity/accuracy) to use, but also the assumed size distributions corresponding to these moments. For instance, one might define a 5-moment implementation using an Exponential mode for smaller cloud droplets, plus a Gamma mode for larger rain droplets. Or, more creatively, perhaps a 12-moment implementation comprised of four Gamma modes. Options for dynamics and size distributions are under continuous development in the `Cloudy.jl` package, thus only the default and suggested use cases are described in detail here. @@ -25,15 +25,17 @@ from the Julia REPL. Upon recognizing that `Cloudy.jl` is being loaded, the exte ## Setting up a system All the details from the number of moments and type of subdistributions, to the parameterizations of coalescence, condensation, and sedimentation are defined through the `CLSetup` (CLoudySetup) mutable struct. This struct is mutable specifically because certain of its components, such as backend-computed coalescence tendencies, are updated prior to being passed to the timestepper. The components of a `CLSetup` object and their defaults are further described below. -| component | description | default | -|---------------------|------------------------------------------|----------------------------| -| ``pdists`` | Vector of subdistributions corresponding | ``[Exponential, Gamma]`` | -| | to the moments | | -| ``mom`` | Prognostic moments, in the same order as | ``[0, 0, 0, 0, 0]`` | -| | the corresponding subdistributions | | -| ``KernelFunc`` | Form of the coalescence kernel function | ``LongKernelFunction`` | -| ``mass_thresholds`` | Particle size thresholds for coalescence | ``[10.0, Inf]`` | -| | integration | | -| ``kernel order`` | Polynomial order for the approx. kernel | ``1`` | -| ``kernel_limit`` | Size threshold for approx. kernel | ``500`` | -| ``vel`` | Power-series coefficients for velocity | ``[2.0, 1/6]`` | \ No newline at end of file +| component | description | default | +|---------------------|--------------------------------------------|----------------------------| +| ``pdists`` | Vector of subdistributions corresponding | ``[Exponential, Gamma]`` | +| | to the moments | | +| ``mom`` | Prognostic mass moments, in the same order |``[1e8 / m^3, 1e-2 kg/m^3,``| +| | as the corresponding subdistributions; |``1e6/m^3, 1e-3 kg/m^3,`` | +| | first 2 for Exp, next 3 for Gamma |``2e-12 kg^2/m^3]`` | +| ``KernelFunc`` | Form of the coalescence kernel function | ``LongKernelFunction`` | +| ``mass_thresholds`` | Particle size thresholds for coalescence | ``[10.0, Inf]`` | +| | integration | | +| ``kernel order`` | Polynomial order for the approx. kernel | ``1`` | +| ``kernel_limit`` | Size threshold for approx. kernel | ``500`` | +| ``vel`` | Power-series coefficients for velocity | ``[2.0, 1/6]`` | +| ``norms`` | Normalizing number density & mass | ``[1e6/m^3, 1e-9 kg]`` | \ No newline at end of file diff --git a/src/MicrophysicsFlexible.jl b/src/MicrophysicsFlexible.jl index 81cea0c78..519622179 100644 --- a/src/MicrophysicsFlexible.jl +++ b/src/MicrophysicsFlexible.jl @@ -65,7 +65,8 @@ function condensation end weighted_vt(clinfo) - `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters -Returns the integrated fall speeds corresponding to the rate of change of prognostic moments +Returns the moment-weighted terminal velocities corresponding to the rate of change of +prognostic moments """ function weighted_vt end