Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "Flexible Microphysics" option, based on Cloudy.jl #333

Merged
merged 17 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
Thermodynamics = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c"

[weakdeps]
Cloudy = "9e3b23bb-e7cc-4b94-886c-65de2234ba87"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
MLJ = "add582a8-e3ab-11e8-2d5e-e98b27df1bc7"

[extensions]
CloudyExt = "Cloudy"
EmulatorModelsExt = ["DataFrames", "MLJ"]

[compat]
ClimaParams = "0.10"
Cloudy = "0.4"
DataFrames = "1.6"
DocStringExtensions = "0.8, 0.9"
ForwardDiff = "0.10"
Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
9 changes: 9 additions & 0 deletions docs/src/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.weighted_vt
```

# P3 scheme

```@docs
Expand Down
41 changes: 41 additions & 0 deletions docs/src/MicrophysicsFlexible.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Microphysics Flexible

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also add the docs page you are creating to the docs make file. Maybe somewhere here:

"Non-equilibrium cloud formation" => "MicrophysicsNonEq.md",

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

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.
Copy link
Member

@trontrytel trontrytel Mar 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be good to add a link to Cloudy documentation, paper or repo somewhere? Especially since w don't want to explain the details of how the collisions or condensation are handled

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


## 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 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]`` |
158 changes: 158 additions & 0 deletions ext/CloudyExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
"""
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.Common as CO
import CloudMicrophysics.Parameters as CMP
import CloudMicrophysics.MicrophysicsFlexible:
CLSetup, coalescence, condensation, weighted_vt

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}(;

Check warning on line 28 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L28

Added line #L28 was not covered by tests
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
]),
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(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(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),
norms::Vector{FT} = [1e6, 1e-9], # 1e6 / m^3; 1e-9 kg
) where {FT <: AbstractFloat}

CLSetup{FT}(

Check warning on line 55 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L55

Added line #L55 was not covered by tests
pdists,
mom,
NProgMoms,
KernelFunc,
mass_thresholds,
kernel_order,
kernel_limit,
coal_data,
vel,
norms,
)
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(

Check warning on line 78 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L77-L78

Added lines #L77 - L78 were not covered by tests
clinfo.KernelFunc,
clinfo.kernel_order,
clinfo.kernel_limit,
)
if isnothing(clinfo.coal_data)
clinfo.coal_data = CL.Coalescence.initialize_coalescence_data(

Check warning on line 84 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L83-L84

Added lines #L83 - L84 were not covered by tests
CL.EquationTypes.AnalyticalCoalStyle(),
kernel_tensor,
clinfo.NProgMoms,
norms = clinfo.norms,
dist_thresholds = clinfo.mass_thresholds,
)
end
mom_norms =

Check warning on line 92 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L92

Added line #L92 was not covered by tests
CL.get_moments_normalizing_factors(clinfo.NProgMoms, clinfo.norms)
mom_normalized = clinfo.mom ./ mom_norms

Check warning on line 94 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L94

Added line #L94 was not covered by tests
# 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, mom_normalized[ind_rng]) #clinfo.mom[ind_rng])
end
CL.Coalescence.update_coal_ints!(

Check warning on line 100 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L96-L100

Added lines #L96 - L100 were not covered by tests
CL.EquationTypes.AnalyticalCoalStyle(),
clinfo.pdists,
clinfo.coal_data,
)
return clinfo.coal_data.coal_ints .* mom_norms

Check warning on line 105 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L105

Added line #L105 was not covered by tests
end

"""
condensation(clinfo, aps, tps, q, ρ, T)

- `clinfo` - kwarg structure containing pdists, moments, and coalescence parameters
- `aps` - air properties
- `tps` - thermodynamics parameters
- `T` - air temperature
- `S` - saturation ratio (supersaturation = S - 1)
Returns a vector of moment tendencies due to condensation/evaporation
"""
function condensation(

Check warning on line 118 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L118

Added line #L118 was not covered by tests
clinfo::CLSetup{FT},
aps::CMP.AirProperties{FT},
tps::TDP.ThermodynamicsParameters{FT},
T::FT,
S::FT,
) where {FT}
ξ = CO.G_func(aps, tps, T, TD.Liquid())

Check warning on line 125 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L125

Added line #L125 was not covered by tests

mom_norms =

Check warning on line 127 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L127

Added line #L127 was not covered by tests
CL.get_moments_normalizing_factors(clinfo.NProgMoms, clinfo.norms)
mom_normalized = clinfo.mom ./ mom_norms

Check warning on line 129 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L129

Added line #L129 was not covered by tests
# 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, mom_normalized[ind_rng])
end
return CL.Condensation.get_cond_evap(

Check warning on line 135 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L131-L135

Added lines #L131 - L135 were not covered by tests
S - 1,
(; ξ = ξ, pdists = clinfo.pdists),
) .* mom_norms
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
"""
function weighted_vt(clinfo::CLSetup{FT}) where {FT}
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 =

Check warning on line 152 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L147-L152

Added lines #L147 - L152 were not covered by tests
CL.Sedimentation.get_sedimentation_flux(clinfo.pdists, clinfo.vel)
return -sed_flux ./ clinfo.mom

Check warning on line 154 in ext/CloudyExt.jl

View check run for this annotation

Codecov / codecov/patch

ext/CloudyExt.jl#L154

Added line #L154 was not covered by tests
end


end #module
Loading
Loading