Skip to content

Commit 61a1c90

Browse files
committed
Implement local ARG aerosol activation with prescribed aerosol densities for 2M microphysics
1 parent 4c763d7 commit 61a1c90

File tree

10 files changed

+228
-94
lines changed

10 files changed

+228
-94
lines changed

.buildkite/Manifest-v1.11.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -461,10 +461,10 @@ uuid = "fb6a15b2-703c-40df-9091-08a04967cfa9"
461461
version = "0.1.13"
462462

463463
[[deps.CloudMicrophysics]]
464-
deps = ["ClimaParams", "DocStringExtensions", "ForwardDiff", "LazyArtifacts", "LogExpFunctions", "QuadGK", "RootSolvers", "SpecialFunctions", "Thermodynamics"]
465-
git-tree-sha1 = "cfc9decc96d616ab3b8d911f843eeb2aafd7d2ad"
464+
deps = ["ClimaParams", "DocStringExtensions", "ForwardDiff", "LazyArtifacts", "LogExpFunctions", "QuadGK", "RootSolvers", "SpecialFunctions", "StaticArrays", "Thermodynamics"]
465+
git-tree-sha1 = "8f7137d8bb3098d6c46b2c3f859fb873d5b28abd"
466466
uuid = "6a9e3e04-43cd-43ba-94b9-e8782df3c71b"
467-
version = "0.25.0"
467+
version = "0.26.1"
468468

469469
[deps.CloudMicrophysics.extensions]
470470
EmulatorModelsExt = ["DataFrames", "MLJ"]

Project.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ ClimaDiagnostics = "0.2.12"
4848
ClimaInterpolations = "0.1.0"
4949
ClimaParams = "0.10.35"
5050
ClimaTimeSteppers = "0.8.2"
51-
ClimaUtilities = "0.1.22"
52-
CloudMicrophysics = "0.25.0"
51+
ClimaUtilities = "0.1.23"
52+
CloudMicrophysics = "0.26.1"
5353
Dates = "1"
5454
ForwardDiff = "0.10.38, 1"
5555
Insolation = "0.9.2"
@@ -64,10 +64,10 @@ NullBroadcasts = "0.1"
6464
RRTMGP = "0.21.3"
6565
Random = "1"
6666
SciMLBase = "2.12"
67-
StaticArrays = "1.7"
67+
StaticArrays = "1.9"
6868
Statistics = "1"
6969
SurfaceFluxes = "0.11, 0.12"
70-
Thermodynamics = "0.12.13"
70+
Thermodynamics = "0.12.14"
7171
UnrolledUtilities = "0.1.9"
7272
YAML = "0.4"
7373
julia = "1.9"

config/model_configs/aquaplanet_nonequil_allsky_gw_res_2M.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ orographic_gravity_wave: "gfdl_restart"
2020
surface_setup: "DefaultMoninObukhov"
2121
prescribe_ozone: true
2222
reproducibility_test: true
23-
prescribed_aerosols: ["SO4", "CB1", "OC1", "DST01", "SSLT01"]
23+
prescribed_aerosols: ["SO4", "CB1", "OC1", "DST01", "SSLT01", "SSLT02", "SSLT03", "SSLT04", "SSLT05"]
2424
diagnostics:
2525
- short_name: [edt, evu, mmrso4, mmrbcpo, mmrocpo, mmrdust, mmrss, loadss, o3, od550aer, odsc550aer]
2626
reduction_time: average

docs/src/equations.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,3 +414,14 @@ The source terms functions treat negative specific humidities as zeros,
414414
so the simulations should be stable even with small negative numbers.
415415

416416
We do not apply hyperdiffusion for precipitation tracers.
417+
418+
### Aerosol Activation for 2-Moment Microphysics
419+
420+
Aerosol activation uses functions from the [CloudMicrophysics.jl](https://github.com/CliMA/CloudMicrophysics.jl) library, based on the Abdul-Razzak and Ghan (ARG) parameterization. ARG predicts the number of activated cloud droplets assuming a parcel of clear air rising adiabatically. This formulation is traditionally applied only at cloud base, where the maximum supersaturation typically occurs.
421+
422+
To enable ARG to be used locally (i.e., without explicitly identifying cloud base), CloudMicrophysics.jl implements a modified equation for the maximum supersaturation that accounts for the presence of pre-existing liquid and ice particles. This allows activation to be applied inside clouds. To ensure that activation occurs only where physically appropriate, we apply additional clipping logic:
423+
424+
- If the predicted maximum supersaturation is less than the local supersaturation (i.e., supersaturation is decreasing), aerosol activation is not applied.
425+
- If the predicted number of activated droplets is less than the existing local cloud droplet number concentration, activation is also suppressed.
426+
427+
This ensures that droplet activation occurs only in physically meaningful regions—typically near cloud base—even though the activation routine can be applied throughout the domain.

perf/flame.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ allocs_limit["flame_aquaplanet_progedmf"] = 774_712
4747
allocs_limit["flame_aquaplanet_progedmf_dense_autodiff"] = 774_712
4848
allocs_limit["flame_diffusion"] = 138_432
4949
allocs_limit["flame_threaded"] = 2047_736
50-
allocs_limit["flame_callbacks"] = 391_864
50+
allocs_limit["flame_callbacks"] = 391_942
5151
allocs_limit["flame_gravity_wave"] = 581_381_976
5252
# Ideally, we would like to track all the allocations, but this becomes too
5353
# expensive there is too many of them. Here, we set the default sample rate to

reproducibility_tests/ref_counter.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
249
1+
250
22

33
# **README**
44
#
@@ -20,6 +20,11 @@
2020

2121

2222
#=
23+
250
24+
- Add ARG aerosol activation for 2M microphysics; increase the allocation limit for `flame_callbacks`
25+
from 391864 to 391942 to account for additional allocations when constructing `ClimaAtmosParameters`
26+
with the new aerosol parameters in `microphysics_2m_parameters`.
27+
2328
249
2429
- Remove viscosity, diffusivity, and mixing length from precomputed quantities
2530

src/parameterized_tendencies/microphysics/cloud_condensate.jl

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ function cloud_condensate_tendency!(
7474
(; params, dt) = p
7575
FT = eltype(params)
7676
thp = CAP.thermodynamics_params(params)
77-
cmc = CAP.microphysics_cloud_params(params)
77+
cmp = CAP.microphysics_2m_params(params)
7878

7979
Tₐ = @. lazy(TD.air_temperature(thp, ᶜts))
8080

8181
@. Yₜ.c.ρq_liq +=
8282
Y.c.ρ * cloud_sources(
83-
cmc.liquid,
83+
cmp.liquid,
8484
thp,
8585
specific(Y.c.ρq_tot, Y.c.ρ),
8686
specific(Y.c.ρq_liq, Y.c.ρ),
@@ -93,7 +93,7 @@ function cloud_condensate_tendency!(
9393
)
9494
@. Yₜ.c.ρq_ice +=
9595
Y.c.ρ * cloud_sources(
96-
cmc.ice,
96+
cmp.ice,
9797
thp,
9898
specific(Y.c.ρq_tot, Y.c.ρ),
9999
specific(Y.c.ρq_liq, Y.c.ρ),
@@ -105,19 +105,68 @@ function cloud_condensate_tendency!(
105105
dt,
106106
)
107107

108-
@. Yₜ.c.ρn_liq +=
109-
Y.c.ρ * aerosol_activation_sources(
110-
cmc.liquid,
111-
thp,
112-
Y.c.ρ,
113-
Tₐ,
114-
specific(Y.c.ρq_tot, Y.c.ρ),
115-
specific(Y.c.ρq_liq, Y.c.ρ),
116-
specific(Y.c.ρq_ice, Y.c.ρ),
117-
specific(Y.c.ρq_rai, Y.c.ρ),
118-
specific(Y.c.ρq_sno, Y.c.ρ),
119-
specific(Y.c.ρn_liq, Y.c.ρ),
120-
cmc.N_cloud_liquid_droplets,
121-
dt,
122-
)
108+
# Aerosol activation using prescribed aerosol (Sea salt and sulfate)
109+
if !(:prescribed_aerosols_field in propertynames(p.tracers))
110+
return
111+
end
112+
seasalt_num = p.scratch.ᶜtemp_scalar
113+
seasalt_mean_radius = p.scratch.ᶜtemp_scalar_2
114+
sulfate_num = p.scratch.ᶜtemp_scalar_3
115+
@. seasalt_num = 0
116+
@. seasalt_mean_radius = 0
117+
@. sulfate_num = 0
118+
# Get aerosol concentrations if available
119+
seasalt_names = [:SSLT01, :SSLT02, :SSLT03, :SSLT04, :SSLT05]
120+
sulfate_names = [:SO4]
121+
aerosol_field = p.tracers.prescribed_aerosols_field
122+
for aerosol_name in propertynames(aerosol_field)
123+
if aerosol_name in seasalt_names
124+
seasalt_particle_radius = getproperty(
125+
cmp.aerosol,
126+
Symbol(string(aerosol_name) * "_radius"),
127+
)
128+
seasalt_particle_mass =
129+
FT(4 / 3 * pi) *
130+
seasalt_particle_radius^3 *
131+
cmp.aerosol.seasalt_density
132+
seasalt_mass = getproperty(aerosol_field, aerosol_name)
133+
@. seasalt_num += seasalt_mass / seasalt_particle_mass
134+
@. seasalt_mean_radius +=
135+
seasalt_mass / seasalt_particle_mass *
136+
log(seasalt_particle_radius)
137+
elseif aerosol_name in sulfate_names
138+
sulfate_particle_mass =
139+
FT(4 / 3 * pi) *
140+
cmp.aerosol.sulfate_radius^3 *
141+
cmp.aerosol.sulfate_density
142+
sulfate_mass = getproperty(aerosol_field, aerosol_name)
143+
@. sulfate_num += sulfate_mass / sulfate_particle_mass
144+
end
145+
end
146+
# Compute geometric mean radius of the log-normal distribution:
147+
# exp(weighted average of log(radius))
148+
@. seasalt_mean_radius =
149+
ifelse(seasalt_num == 0, 0, exp(seasalt_mean_radius / seasalt_num))
150+
151+
# Compute aerosol activation (ARG 2000)
152+
(; ᶜu) = p.precomputed
153+
ᶜw = p.scratch.ᶜtemp_scalar_4
154+
Snₗ = p.scratch.ᶜtemp_scalar_5
155+
@. ᶜw = max(0, w_component.(Geometry.WVector.(ᶜu)))
156+
@. Snₗ = aerosol_activation_sources(
157+
seasalt_num,
158+
seasalt_mean_radius,
159+
sulfate_num,
160+
specific(Y.c.ρq_tot, Y.c.ρ),
161+
specific(Y.c.ρq_liq + Y.c.ρq_rai, Y.c.ρ),
162+
specific(Y.c.ρq_ice + Y.c.ρq_sno, Y.c.ρ),
163+
specific(Y.c.ρn_liq + Y.c.ρn_rai, Y.c.ρ),
164+
Y.c.ρ,
165+
ᶜw,
166+
(cmp,),
167+
thp,
168+
ᶜts,
169+
dt,
170+
)
171+
@. Yₜ.c.ρn_liq += Y.c.ρ * Snₗ
123172
end

0 commit comments

Comments
 (0)