Skip to content

Commit 4014bca

Browse files
committed
This commit adds a new diagnostic which computes the convective
available potential energy based on the buoyancy difference between a parcel lifted adiabatically from the surface and the envrionment. CAPE is useful for diagnosing the potential for extreme weather like thunderstorms, tornados, etc. It is accessed by including `cape` in the diagnostics of any config file. Typically cape is computed by integrating between the LFC and the LNB, however here we simply integrate the positive buoyancy between the surface and 20km as this was significantly simpler to implement in ClimaCore. Future work should revisit this.
1 parent 50b0b53 commit 4014bca

File tree

5 files changed

+67
-2
lines changed

5 files changed

+67
-2
lines changed

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ ClimaAtmos.jl Release Notes
44
main
55
-------
66

7+
### Add diagnostic for CAPE
8+
PR [#3820](https://github.com/CliMA/ClimaAtmos.jl/pull/3820) adds support for computing convective available potential energy (CAPE), or the vertical integral of the buoyancy differential between a parcel lifted from the surface and the environment. Exemplified in the TRMM deep convection case.
9+
710
v0.30.2
811
-------
912

config/model_configs/prognostic_edmfx_trmm_column_0M.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,10 @@ z_stretch: false
3030
dz_bottom: 30
3131
dt: 150secs
3232
t_end: 6hours
33-
dt_save_state_to_disk: 10mins
3433
toml: [toml/prognostic_edmfx_implicit_scm_calibrated_5_cases_shallow_deep_v1.toml]
3534
netcdf_interpolation_num_points: [8, 8, 82]
3635
diagnostics:
37-
- short_name: [ts, ta, thetaa, ha, pfull, rhoa, ua, va, wa, hur, hus, cl, clw, cli, hussfc, evspsbl, pr]
36+
- short_name: [ts, ta, thetaa, ha, pfull, rhoa, ua, va, wa, hur, hus, cl, clw, cli, hussfc, evspsbl, pr, cape]
3837
period: 10mins
3938
- short_name: [arup, waup, taup, thetaaup, haup, husup, hurup, clwup, cliup, waen, taen, thetaaen, haen, husen, huren, clwen, clien, tke]
4039
period: 10mins

src/diagnostics/Diagnostics.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import ClimaCore:
1111
import ClimaCore.Utilities: half
1212
import Thermodynamics as TD
1313

14+
# compute lazily to reduce allocations
15+
import ..lazy
16+
1417
import ..AtmosModel
1518
import ..AtmosCallback
1619
import ..EveryNSteps

src/diagnostics/core_diagnostics.jl

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,3 +1349,62 @@ add_diagnostic_variable!(
13491349
end
13501350
end,
13511351
)
1352+
1353+
###
1354+
# Convective Available Potential Energy (2d)
1355+
###
1356+
function compute_cape!(out, state, cache, time)
1357+
thermo_params = lazy.(CAP.thermodynamics_params(cache.params))
1358+
g = lazy.(TD.Parameters.grav(thermo_params))
1359+
1360+
# Get surface parcel properties
1361+
surface_thermal_state = lazy.(cache.precomputed.sfc_conditions.ts)
1362+
surface_q =
1363+
lazy.(TD.total_specific_humidity.(thermo_params, surface_thermal_state))
1364+
surface_θ_liq_ice =
1365+
lazy.(TD.liquid_ice_pottemp.(thermo_params, surface_thermal_state))
1366+
1367+
# Create parcel thermodynamic states at each level based on energy & moisture at surface
1368+
parcel_ts_moist =
1369+
lazy.(
1370+
TD.PhaseEquil_pθq.(
1371+
thermo_params,
1372+
cache.precomputed.ᶜp,
1373+
surface_θ_liq_ice,
1374+
surface_q,
1375+
)
1376+
)
1377+
1378+
# Calculate virtual temperatures for parcel & environment
1379+
parcel_Tv = lazy.(TD.virtual_temperature.(thermo_params, parcel_ts_moist))
1380+
env_Tv =
1381+
lazy.(TD.virtual_temperature.(thermo_params, cache.precomputed.ᶜts))
1382+
1383+
# Calculate buoyancy from the difference in virtual temperatures
1384+
ᶜbuoyancy = cache.scratch.ᶜtemp_scalar
1385+
ᶜbuoyancy .= g .* (parcel_Tv .- env_Tv) ./ env_Tv
1386+
1387+
# restrict to tropospheric buoyancy (generously below 20km) TODO: integrate from LFC to LNB
1388+
FT = Spaces.undertype(axes(ᶜbuoyancy))
1389+
ᶜbuoyancy .=
1390+
ᶜbuoyancy .*
1391+
ifelse.(
1392+
Fields.coordinate_field(state.c.ρ).z .< FT(20000.0),
1393+
FT(1.0),
1394+
FT(0.0),
1395+
)
1396+
1397+
# Integrate positive buoyancy to get CAPE
1398+
out′ = isnothing(out) ? zeros(axes(Fields.level(state.f, half))) : out
1399+
Operators.column_integral_definite!(out′, lazy.(max.(ᶜbuoyancy, 0)))
1400+
return out′
1401+
end
1402+
1403+
add_diagnostic_variable!(
1404+
short_name = "cape",
1405+
long_name = "Convective Available Potential Energy",
1406+
standard_name = "convective_available_potential_energy",
1407+
units = "J kg^-1",
1408+
comments = "Energy available to a parcel lifted moist adiabatically from the surface. We assume fully reversible phase changes and no precipitation.",
1409+
compute! = compute_cape!,
1410+
)

src/diagnostics/default_diagnostics.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ function default_diagnostics(
209209
"lwp",
210210
"clwvi",
211211
"clivi",
212+
"cape",
212213
]
213214
average_func = frequency_averages(duration)
214215
return [average_func(moist_diagnostics...; output_writer, start_date)...]

0 commit comments

Comments
 (0)