Skip to content

Commit b3696f7

Browse files
authored
Merge pull request #4124 from CliMA/zs/buoyancy_gradient2
use cloud fraction in buoyancy gradient
2 parents 866c94a + beafd42 commit b3696f7

File tree

12 files changed

+80
-102
lines changed

12 files changed

+80
-102
lines changed

docs/src/restarts.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ particularly useful for
2424
if that is not the case. When the warning is produced, it is your responsability
2525
to ensure that what you are doing makes sense.
2626

27+
!!! note
28+
29+
The simulation can only be restarted in a reproducible way when using `GridScaleCloud`
30+
for `cloud_model`.
31+
2732
### How Restarts Work
2833

2934
`ClimaAtmos` periodically saves the simulation state to a file called a "restart

reproducibility_tests/ref_counter.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
278
1+
279
22

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

2121

2222
#=
23+
279
24+
- Use partial cloud fraction in buoyancy gradient calculation
25+
2326
278
2427
- Add ∂/∂q elements to Jacobian
2528

src/cache/cloud_fraction.jl

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ end
2222
Compute the grid scale cloud fraction based on sub-grid scale properties
2323
"""
2424
NVTX.@annotate function set_cloud_fraction!(Y, p, ::DryModel, _)
25-
(; turbconv_model) = p.atmos
2625
FT = eltype(p.params)
2726

2827
p.precomputed.cloud_diagnostics_tuple .=
@@ -35,23 +34,9 @@ NVTX.@annotate function set_cloud_fraction!(
3534
::GridScaleCloud,
3635
)
3736
(; params) = p
38-
(; turbconv_model) = p.atmos
3937
(; ᶜts, cloud_diagnostics_tuple) = p.precomputed
4038
thermo_params = CAP.thermodynamics_params(params)
4139

42-
if isnothing(turbconv_model)
43-
if p.atmos.call_cloud_diagnostics_per_stage isa
44-
CallCloudDiagnosticsPerStage
45-
(; ᶜgradᵥ_θ_virt, ᶜgradᵥ_q_tot, ᶜgradᵥ_θ_liq_ice) = p.precomputed
46-
thermo_params = CAP.thermodynamics_params(p.params)
47-
@. ᶜgradᵥ_θ_virt =
48-
ᶜgradᵥ(ᶠinterp(TD.virtual_pottemp(thermo_params, ᶜts)))
49-
@. ᶜgradᵥ_q_tot =
50-
ᶜgradᵥ(ᶠinterp(TD.total_specific_humidity(thermo_params, ᶜts)))
51-
@. ᶜgradᵥ_θ_liq_ice =
52-
ᶜgradᵥ(ᶠinterp(TD.liquid_ice_pottemp(thermo_params, ᶜts)))
53-
end
54-
end
5540
if moist_model isa EquilMoistModel
5641
@. cloud_diagnostics_tuple = make_named_tuple(
5742
ifelse(TD.has_condensate(thermo_params, ᶜts), 1, 0),
@@ -83,10 +68,8 @@ NVTX.@annotate function set_cloud_fraction!(
8368
if isnothing(turbconv_model)
8469
if p.atmos.call_cloud_diagnostics_per_stage isa
8570
CallCloudDiagnosticsPerStage
86-
(; ᶜgradᵥ_θ_virt, ᶜgradᵥ_q_tot, ᶜgradᵥ_θ_liq_ice) = p.precomputed
71+
(; ᶜgradᵥ_q_tot, ᶜgradᵥ_θ_liq_ice) = p.precomputed
8772
thermo_params = CAP.thermodynamics_params(p.params)
88-
@. ᶜgradᵥ_θ_virt =
89-
ᶜgradᵥ(ᶠinterp(TD.virtual_pottemp(thermo_params, ᶜts)))
9073
@. ᶜgradᵥ_q_tot =
9174
ᶜgradᵥ(ᶠinterp(TD.total_specific_humidity(thermo_params, ᶜts)))
9275
@. ᶜgradᵥ_θ_liq_ice =

src/cache/diagnostic_edmf_precomputed_quantities.jl

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,14 +1339,9 @@ NVTX.@annotate function set_diagnostic_edmf_precomputed_quantities_env_closures!
13391339
p,
13401340
t,
13411341
)
1342-
(; moisture_model, turbconv_model, microphysics_model) = p.atmos
1343-
n = n_mass_flux_subdomains(turbconv_model)
1344-
ᶜz = Fields.coordinate_field(Y.c).z
1345-
ᶜdz = Fields.Δz_field(axes(Y.c))
13461342
(; params) = p
1347-
(; dt) = p
1348-
(; ᶜp, ᶜu, ᶜts, ᶠu³) = p.precomputed
1349-
(; ustar, obukhov_length) = p.precomputed.sfc_conditions
1343+
(; ᶜu, ᶜts, ᶠu³) = p.precomputed
1344+
(; ustar) = p.precomputed.sfc_conditions
13501345
(; ᶜlinear_buoygrad, ᶜstrain_rate_norm) = p.precomputed
13511346
(; ρatke_flux) = p.precomputed
13521347
turbconv_params = CAP.turbconv_params(params)
@@ -1364,12 +1359,11 @@ NVTX.@annotate function set_diagnostic_edmf_precomputed_quantities_env_closures!
13641359
@. ᶜlinear_buoygrad = buoyancy_gradients(
13651360
BuoyGradMean(),
13661361
thermo_params,
1367-
moisture_model,
13681362
ᶜts,
1363+
p.precomputed.cloud_diagnostics_tuple.cf,
13691364
C3,
1370-
p.precomputed.ᶜgradᵥ_θ_virt, # ∂θv∂z_unsat
1371-
p.precomputed.ᶜgradᵥ_q_tot, # ∂qt∂z_sat
1372-
p.precomputed.ᶜgradᵥ_θ_liq_ice, # ∂θl∂z_sat
1365+
p.precomputed.ᶜgradᵥ_q_tot,
1366+
p.precomputed.ᶜgradᵥ_θ_liq_ice,
13731367
ᶜlg,
13741368
)
13751369

@@ -1381,10 +1375,7 @@ NVTX.@annotate function set_diagnostic_edmf_precomputed_quantities_env_closures!
13811375

13821376
ρatke_flux_values = Fields.field_values(ρatke_flux)
13831377
ρ_int_values = Fields.field_values(Fields.level(Y.c.ρ, 1))
1384-
u_int_values = Fields.field_values(Fields.level(ᶜu, 1))
13851378
ustar_values = Fields.field_values(ustar)
1386-
int_local_geometry_values =
1387-
Fields.field_values(Fields.level(Fields.local_geometry_field(Y.c), 1))
13881379
sfc_local_geometry_values = Fields.field_values(
13891380
Fields.level(Fields.local_geometry_field(Y.f), half),
13901381
)

src/cache/precomputed_quantities.jl

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ function precomputed_quantities(Y, atmos)
8585
TST = thermo_state_type(atmos.moisture_model, FT)
8686
SCT = SurfaceConditions.surface_conditions_type(atmos, FT)
8787
cspace = axes(Y.c)
88-
fspace = axes(Y.f)
8988
n = n_mass_flux_subdomains(atmos.turbconv_model)
9089
n_prog = n_prognostic_mass_flux_subdomains(atmos.turbconv_model)
9190
@assert !(atmos.turbconv_model isa PrognosticEDMFX) || n_prog == 1
@@ -98,6 +97,8 @@ function precomputed_quantities(Y, atmos)
9897
)
9998
cloud_diagnostics_tuple =
10099
similar(Y.c, @NamedTuple{cf::FT, q_liq::FT, q_ice::FT})
100+
# Cloud fraction is used to calculate buoyancy gradient, so we initialize it to 0 here.
101+
@. cloud_diagnostics_tuple.cf = FT(0)
101102
surface_precip_fluxes = (;
102103
surface_rain_flux = zeros(axes(Fields.level(Y.f, half))),
103104
surface_snow_flux = zeros(axes(Fields.level(Y.f, half))),
@@ -200,7 +201,6 @@ function precomputed_quantities(Y, atmos)
200201
ᶜentrʲs = similar(Y.c, NTuple{n, FT}),
201202
ᶜdetrʲs = similar(Y.c, NTuple{n, FT}),
202203
ᶜturb_entrʲs = similar(Y.c, NTuple{n, FT}),
203-
ᶜgradᵥ_θ_virt = Fields.Field(C3{FT}, cspace),
204204
ᶜgradᵥ_q_tot = Fields.Field(C3{FT}, cspace),
205205
ᶜgradᵥ_θ_liq_ice = Fields.Field(C3{FT}, cspace),
206206
ᶠnh_pressure₃_buoyʲs = similar(Y.f, NTuple{n, C3{FT}}),
@@ -212,7 +212,6 @@ function precomputed_quantities(Y, atmos)
212212
(; ρatke_flux = similar(Fields.level(Y.f, half), C3{FT}),) : (;)
213213

214214
sgs_quantities = (;
215-
ᶜgradᵥ_θ_virt = Fields.Field(C3{FT}, cspace),
216215
ᶜgradᵥ_q_tot = Fields.Field(C3{FT}, cspace),
217216
ᶜgradᵥ_θ_liq_ice = Fields.Field(C3{FT}, cspace),
218217
)
@@ -548,8 +547,6 @@ NVTX.@annotate function set_explicit_precomputed_quantities_part1!(Y, p, t)
548547
end
549548

550549
if turbconv_model isa AbstractEDMF
551-
@. p.precomputed.ᶜgradᵥ_θ_virt =
552-
ᶜgradᵥ(ᶠinterp(TD.virtual_pottemp(thermo_params, ᶜts)))
553550
@. p.precomputed.ᶜgradᵥ_q_tot =
554551
ᶜgradᵥ(ᶠinterp(TD.total_specific_humidity(thermo_params, ᶜts)))
555552
@. p.precomputed.ᶜgradᵥ_θ_liq_ice =
@@ -568,6 +565,15 @@ NVTX.@annotate function set_explicit_precomputed_quantities_part2!(Y, p, t)
568565
(; turbconv_model, moisture_model, cloud_model) = p.atmos
569566
(; call_cloud_diagnostics_per_stage) = p.atmos
570567

568+
# The buoyancy gradient depends on the cloud fraction, and the cloud fraction
569+
# depends on the mixing length, which depends on the buoyancy gradient.
570+
# We break this circular dependency by using cloud fraction from the previous time step in the
571+
# buoyancy gradient calculation. This breaks reproducible restart in general,
572+
# but we support reproducible restart with grid-scale cloud by recalculating the cloud fraction here.
573+
if p.atmos.cloud_model isa GridScaleCloud
574+
set_cloud_fraction!(Y, p, p.atmos.moisture_model, p.atmos.cloud_model)
575+
end
576+
571577
if turbconv_model isa PrognosticEDMFX
572578
set_prognostic_edmf_precomputed_quantities_explicit_closures!(Y, p, t)
573579
set_prognostic_edmf_precomputed_quantities_precipitation!(

src/cache/prognostic_edmf_precomputed_quantities.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ NVTX.@annotate function set_prognostic_edmf_precomputed_quantities_explicit_clos
164164
t,
165165
)
166166

167-
(; moisture_model, turbconv_model) = p.atmos
167+
(; turbconv_model) = p.atmos
168168

169169
(; params) = p
170170
(; dt) = p
@@ -299,14 +299,14 @@ NVTX.@annotate function set_prognostic_edmf_precomputed_quantities_explicit_clos
299299
)
300300
end
301301

302-
(; ᶜgradᵥ_θ_virt, ᶜgradᵥ_q_tot, ᶜgradᵥ_θ_liq_ice) = p.precomputed
302+
(; ᶜgradᵥ_q_tot, ᶜgradᵥ_θ_liq_ice, cloud_diagnostics_tuple) =
303+
p.precomputed
303304
@. ᶜlinear_buoygrad = buoyancy_gradients( # TODO - do we need to modify buoyancy gradients based on NonEq + 1M tracers?
304305
BuoyGradMean(),
305306
thermo_params,
306-
moisture_model,
307307
ᶜts,
308+
cloud_diagnostics_tuple.cf,
308309
C3,
309-
ᶜgradᵥ_θ_virt,
310310
ᶜgradᵥ_q_tot,
311311
ᶜgradᵥ_θ_liq_ice,
312312
ᶜlg,

src/callbacks/callbacks.jl

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,12 @@ function external_driven_single_column!(integrator)
106106
evaluate!(ᶜls_subsidence, wa, t)
107107
end
108108

109-
110-
111109
NVTX.@annotate function cloud_fraction_model_callback!(integrator)
112110
Y = integrator.u
113111
p = integrator.p
114-
(; ᶜts, ᶜgradᵥ_θ_virt, ᶜgradᵥ_q_tot, ᶜgradᵥ_θ_liq_ice) = p.precomputed
112+
(; ᶜts, ᶜgradᵥ_q_tot, ᶜgradᵥ_θ_liq_ice) = p.precomputed
115113
thermo_params = CAP.thermodynamics_params(p.params)
116114
if isnothing(p.atmos.turbconv_model)
117-
@. ᶜgradᵥ_θ_virt =
118-
ᶜgradᵥ(ᶠinterp(TD.virtual_pottemp(thermo_params, ᶜts)))
119115
@. ᶜgradᵥ_q_tot =
120116
ᶜgradᵥ(ᶠinterp(TD.total_specific_humidity(thermo_params, ᶜts)))
121117
@. ᶜgradᵥ_θ_liq_ice =

src/prognostic_equations/eddy_diffusion_closures.jl

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@ import ClimaCore.Fields as Fields
1111
buoyancy_gradients(
1212
closure::AbstractEnvBuoyGradClosure,
1313
thermo_params,
14-
moisture_model,
14+
1515
# Arguments for the first method (most commonly called):
1616
ts::TD.ThermodynamicState,
1717
::Type{C3}, # Covariant3 vector type, for projecting gradients
18-
∂θv∂z_unsat::AbstractField, # Vertical gradient of virtual potential temperature in unsaturated part
19-
∂qt∂z_sat::AbstractField, # Vertical gradient of total specific humidity in saturated part
20-
∂θli∂z_sat::AbstractField, # Vertical gradient of liquid-ice potential temperature in saturated part
18+
∂qt∂z::AbstractField, # Vertical gradient of total specific humidity
19+
∂θli∂z::AbstractField, # Vertical gradient of liquid-ice potential temperature
2120
local_geometry::Fields.LocalGeometry,
2221
# Argument for the second method (internal use with precomputed EnvBuoyGradVars):
2322
# bg_model::EnvBuoyGradVars
@@ -47,12 +46,10 @@ variables.
4746
Arguments:
4847
- `closure`: The environmental buoyancy gradient closure type (e.g., `BuoyGradMean`).
4948
- `thermo_params`: Thermodynamic parameters from `CLIMAParameters`.
50-
- `moisture_model`: Moisture model (e.g., `EquilMoistModel`, `NonEquilMoistModel`).
5149
- `ts`: Center-level thermodynamic state of the environment.
5250
- `C3`: The `ClimaCore.Geometry.Covariant3Vector` type, used for projecting input vertical gradients.
53-
- `∂θv∂z_unsat`: Field of vertical gradients of virtual potential temperature in the unsaturated part.
54-
- `∂qt∂z_sat`: Field of vertical gradients of total specific humidity in the saturated part.
55-
- `∂θli∂z_sat`: Field of vertical gradients of liquid-ice potential temperature in the saturated part.
51+
- `∂qt∂z`: Field of vertical gradients of total specific humidity.
52+
- `∂θli∂z`: Field of vertical gradients of liquid-ice potential temperature.
5653
- `local_geometry`: Field of local geometry at cell centers, used for gradient projection.
5754
The second method takes a precomputed `EnvBuoyGradVars` object instead of `ts` and gradient fields.
5855
@@ -64,25 +61,23 @@ function buoyancy_gradients end
6461
function buoyancy_gradients(
6562
ebgc::AbstractEnvBuoyGradClosure,
6663
thermo_params,
67-
moisture_model,
6864
ts,
65+
cf,
6966
::Type{C3},
70-
∂θv∂z_unsat,
71-
∂qt∂z_sat,
72-
∂θli∂z_sat,
67+
∂qt∂z,
68+
∂θli∂z,
7369
ᶜlg,
7470
) where {C3}
7571
return buoyancy_gradients(
7672
ebgc,
7773
thermo_params,
78-
moisture_model,
7974
EnvBuoyGradVars(
8075
ts,
76+
cf,
8177
projected_vector_buoy_grad_vars(
8278
C3,
83-
∂θv∂z_unsat,
84-
∂qt∂z_sat,
85-
∂θli∂z_sat,
79+
∂qt∂z,
80+
∂θli∂z,
8681
ᶜlg,
8782
),
8883
),
@@ -92,43 +87,42 @@ end
9287
function buoyancy_gradients(
9388
ebgc::AbstractEnvBuoyGradClosure,
9489
thermo_params,
95-
moisture_model,
9690
bg_model::EnvBuoyGradVars,
9791
)
9892
FT = eltype(bg_model)
9993

10094
g = TDP.grav(thermo_params)
10195
Rv_over_Rd = TDP.Rv_over_Rd(thermo_params)
102-
R_d = TDP.R_d(thermo_params)
10396
R_v = TDP.R_v(thermo_params)
10497

10598
ts = bg_model.ts
106-
p = TD.air_pressure(thermo_params, ts)
107-
Π = TD.exner_given_pressure(thermo_params, p)
108-
∂b∂θv = g * (R_d * TD.air_density(thermo_params, ts) / p) * Π
99+
∂b∂θv = g / TD.virtual_pottemp(thermo_params, ts)
109100

110-
t_sat = TD.air_temperature(thermo_params, ts)
101+
T = TD.air_temperature(thermo_params, ts)
111102
λ = TD.liquid_fraction(thermo_params, ts)
112103
lh =
113-
λ * TD.latent_heat_vapor(thermo_params, t_sat) +
114-
(1 - λ) * TD.latent_heat_sublim(thermo_params, t_sat)
104+
λ * TD.latent_heat_vapor(thermo_params, T) +
105+
(1 - λ) * TD.latent_heat_sublim(thermo_params, T)
115106
cp_m = TD.cp_m(thermo_params, ts)
116-
qv_sat = TD.vapor_specific_humidity(thermo_params, ts)
117-
qt_sat = TD.total_specific_humidity(thermo_params, ts)
107+
q_sat = TD.q_vap_saturation(thermo_params, ts)
108+
q_tot = TD.total_specific_humidity(thermo_params, ts)
109+
θ = TD.dry_pottemp(thermo_params, ts)
110+
∂b∂θli_unsat = ∂b∂θv * (1 + (Rv_over_Rd - 1) * q_tot)
111+
∂b∂qt_unsat = ∂b∂θv * (Rv_over_Rd - 1) * θ
118112
∂b∂θli_sat = (
119113
∂b∂θv *
120-
(1 + Rv_over_Rd * (1 + lh / R_v / t_sat) * qv_sat - qt_sat) /
121-
(1 + lh * lh / cp_m / R_v / t_sat / t_sat * qv_sat)
114+
(1 + Rv_over_Rd * (1 + lh / R_v / T) * q_sat - q_tot) /
115+
(1 + lh^2 / cp_m / R_v / T^2 * q_sat)
122116
)
123117
∂b∂qt_sat =
124-
(lh / cp_m / t_sat * ∂b∂θli_sat - ∂b∂θv) *
125-
TD.dry_pottemp(thermo_params, ts)
118+
(lh / cp_m / T * ∂b∂θli_sat - ∂b∂θv) * θ
126119

127120
∂b∂z = buoyancy_gradient_chain_rule(
128121
ebgc,
129122
bg_model,
130123
thermo_params,
131-
∂b∂θv,
124+
∂b∂θli_unsat,
125+
∂b∂qt_unsat,
132126
∂b∂θli_sat,
133127
∂b∂qt_sat,
134128
)
@@ -140,7 +134,6 @@ end
140134
closure::AbstractEnvBuoyGradClosure,
141135
bg_model::EnvBuoyGradVars,
142136
thermo_params,
143-
∂b∂θv::FT,
144137
∂b∂θli_sat::FT,
145138
∂b∂qt_sat::FT,
146139
) where {FT}
@@ -165,7 +158,6 @@ Arguments:
165158
- `closure`: The environmental buoyancy gradient closure type.
166159
- `bg_model`: Precomputed environmental buoyancy gradient variables (`EnvBuoyGradVars`).
167160
- `thermo_params`: Thermodynamic parameters from `CLIMAParameters`.
168-
- `∂b∂θv`: Partial derivative of buoyancy w.r.t. virtual potential temperature (unsaturated part).
169161
- `∂b∂θli_sat`: Partial derivative of buoyancy w.r.t. liquid-ice potential temperature (saturated part).
170162
- `∂b∂qt_sat`: Partial derivative of buoyancy w.r.t. total specific humidity (saturated part).
171163
@@ -176,18 +168,19 @@ function buoyancy_gradient_chain_rule(
176168
::AbstractEnvBuoyGradClosure,
177169
bg_model::EnvBuoyGradVars,
178170
thermo_params,
179-
∂b∂θv,
171+
∂b∂θli_unsat,
172+
∂b∂qt_unsat,
180173
∂b∂θli_sat,
181174
∂b∂qt_sat,
182175
)
183-
en_cld_frac = ifelse(TD.has_condensate(thermo_params, bg_model.ts), 1, 0)
184-
185-
∂b∂z_θl_sat = ∂b∂θli_sat * bg_model.∂θli∂z_sat
186-
∂b∂z_qt_sat = ∂b∂qt_sat * bg_model.∂qt∂z_sat
176+
∂b∂z_θli_unsat = ∂b∂θli_unsat * bg_model.∂θli∂z
177+
∂b∂z_qt_unsat = ∂b∂qt_unsat * bg_model.∂qt∂z
178+
∂b∂z_unsat = ∂b∂z_θli_unsat + ∂b∂z_qt_unsat
179+
∂b∂z_θl_sat = ∂b∂θli_sat * bg_model.∂θli∂z
180+
∂b∂z_qt_sat = ∂b∂qt_sat * bg_model.∂qt∂z
187181
∂b∂z_sat = ∂b∂z_θl_sat + ∂b∂z_qt_sat
188-
∂b∂z_unsat = ∂b∂θv * bg_model.∂θv∂z_unsat
189182

190-
∂b∂z = (1 - en_cld_frac) * ∂b∂z_unsat + en_cld_frac * ∂b∂z_sat
183+
∂b∂z = (1 - bg_model.cf) * ∂b∂z_unsat + bg_model.cf * ∂b∂z_sat
191184

192185
return ∂b∂z
193186
end

0 commit comments

Comments
 (0)