Skip to content

Commit 6ddd2b2

Browse files
committed
ft: Add Kinematic Driver (KiD) model configuration
1 parent 61ae2da commit 6ddd2b2

File tree

14 files changed

+280
-6
lines changed

14 files changed

+280
-6
lines changed

config/default_configs/default_config.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,3 +425,6 @@ use_itime:
425425
1. Surface conditions that explicitly depend on time (e.g. LifeCycleTan2018, TRMM_LBA, etc.),
426426
2. Time dependent forcing/tendencies use time rounded to the nearest unit of time for dt"
427427
value: false
428+
prescribed_flow:
429+
help: "Prescribe a flow field [`nothing` (default), `true`]"
430+
value: ~
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
## Model
2+
prescribed_flow: true
3+
initial_condition: "ShipwayHill2012"
4+
surface_setup: "ShipwayHill2012"
5+
energy_q_tot_upwinding: first_order
6+
tracer_upwinding: first_order
7+
# moist: "nonequil"
8+
# precip_model: "1M"
9+
moist: "equil"
10+
precip_model: "0M"
11+
cloud_model: "grid_scale"
12+
call_cloud_diagnostics_per_stage: true
13+
config: "column"
14+
hyperdiff: "false"
15+
## Simulation
16+
z_max: 2e3
17+
z_elem: 128
18+
z_stretch: false
19+
use_auto_jacobian: true # Needed!! (+only 1 Newton iteration, which is the default)
20+
# ode_algo: "ARS111" # try: Explicit Euler
21+
dt: "1secs"
22+
# t_end: "1hours"
23+
t_end: "20mins"
24+
dt_save_state_to_disk: "1hours"
25+
toml: [toml/kinematic_driver.toml]
26+
check_nan_every: 1
27+
## Diagnostics
28+
output_default_diagnostics: false
29+
diagnostics:
30+
- short_name: [
31+
# (z,t) fields
32+
ta, thetaa, ha, rhoa, wa, hur, hus, cl, clw, cli, ua, va, ke,
33+
# (t,) fields
34+
lwp, pr,
35+
# 1M
36+
# (z,t) fields
37+
# husra, hussn,
38+
# (t,) fields
39+
# rwp,
40+
]
41+
period: 10secs

docs/bibliography.bib

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,3 +283,15 @@ @article{Wing2018
283283
url = {https://gmd.copernicus.org/articles/11/793/2018/},
284284
doi = {10.5194/gmd-11-793-2018}
285285
}
286+
287+
@article{ShipwayHill2012,
288+
title = {Diagnosis of systematic differences between multiple parametrizations of warm rain microphysics using a kinematic framework},
289+
volume = {138},
290+
url = {https://onlinelibrary.wiley.com/doi/abs/10.1002/qj.1913},
291+
doi = {10.1002/qj.1913},
292+
pages = {2196--2211},
293+
number = {669},
294+
journaltitle = {Quarterly Journal of the Royal Meteorological Society},
295+
author = {Shipway, B. J. and Hill, A. A.},
296+
date = {2012},
297+
}

post_processing/ci_plots.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,3 +1689,40 @@ function make_plots(
16891689
output_name = "summary_3D",
16901690
)
16911691
end
1692+
1693+
function make_plots(::Val{:kinematic_driver}, output_paths::Vector{<:AbstractString})
1694+
function rescale_time_to_min(var)
1695+
if haskey(var.dims, "time")
1696+
var.dims["time"] .= var.dims["time"] ./ 60
1697+
var.dim_attributes["time"]["units"] = "min"
1698+
end
1699+
return var
1700+
end
1701+
simdirs = SimDir.(output_paths)
1702+
short_names = [
1703+
"hus", "clw", "husra", "ta", #"thetaa", "rhoa",
1704+
"wa",
1705+
# "cli", "hussn",
1706+
# "ke",
1707+
]
1708+
short_names = short_names collect(keys(simdirs[1].vars))
1709+
vars = map_comparison(simdirs, short_names) do simdir, short_name
1710+
var = slice(get(simdir; short_name), x = 0, y = 0)
1711+
if short_name in ["hus", "clw", "husra", "cli", "hussn"]
1712+
var.data .= var.data .* 1000
1713+
var.attributes["units"] = "g/kg"
1714+
end
1715+
return rescale_time_to_min(var)
1716+
end
1717+
file_contour = make_plots_generic(output_paths, vars;
1718+
output_name = "tmp_contour",
1719+
)
1720+
1721+
short_names_lines = ["lwp", "rwp", "pr"]
1722+
short_names_lines = short_names_lines collect(keys(simdirs[1].vars))
1723+
vars_lines = map_comparison(simdirs, short_names_lines) do simdir, short_name
1724+
var = slice(get(simdir; short_name), x = 0, y = 0)
1725+
return rescale_time_to_min(var)
1726+
end
1727+
make_plots_generic(output_paths, vars_lines; summary_files = [file_contour])
1728+
end

src/cache/precomputed_quantities.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -480,8 +480,10 @@ NVTX.@annotate function set_implicit_precomputed_quantities_part1!(Y, p, t)
480480

481481
# TODO: We might want to move this to dss! (and rename dss! to something
482482
# like enforce_constraints!).
483-
set_velocity_at_surface!(Y, ᶠuₕ³, turbconv_model)
484-
set_velocity_at_top!(Y, turbconv_model)
483+
if !(p.atmos.prescribed_flow isa PrescribedFlow)
484+
set_velocity_at_surface!(Y, ᶠuₕ³, turbconv_model)
485+
set_velocity_at_top!(Y, turbconv_model)
486+
end
485487

486488
set_velocity_quantities!(ᶜu, ᶠu³, ᶜK, Y.f.u₃, Y.c.uₕ, ᶠuₕ³)
487489
ᶜJ = Fields.local_geometry_field(Y.c).J

src/initial_conditions/initial_conditions.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1633,3 +1633,43 @@ function (initial_condition::ISDAC)(params)
16331633
)
16341634
end
16351635
end
1636+
1637+
"""
1638+
ShipwayHill2012
1639+
1640+
The `InitialCondition` described in [ShipwayHill2012](@cite), but with a hydrostatically
1641+
balanced pressure profile.
1642+
1643+
B. J. Shipway and A. A. Hill.
1644+
Diagnosis of systematic differences between multiple parametrizations of warm rain microphysics using a kinematic framework.
1645+
Quarterly Journal of the Royal Meteorological Society 138, 2196-2211 (2012).
1646+
"""
1647+
struct ShipwayHill2012 <: InitialCondition end
1648+
function (initial_condition::ShipwayHill2012)(params)
1649+
FT = eltype(params)
1650+
1651+
## Initialize the profile
1652+
z_values = FT[0, 740, 3260]
1653+
rv_values = FT[0.015, 0.0138, 0.0024] # water vapor mixing ratio
1654+
θ_values = FT[297.9, 297.9, 312.66] # potential temperature
1655+
linear_profile(zs, vals) = Intp.extrapolate(
1656+
Intp.interpolate((zs,), vals, Intp.Gridded(Intp.Linear())), Intp.Linear(),
1657+
)
1658+
# profile of water vapour mixing ratio
1659+
rv(z) = linear_profile(z_values, rv_values)(z)
1660+
q_tot(z) = rv(z) / (1 + rv(z))
1661+
# profile of potential temperature
1662+
θ(z) = linear_profile(z_values, θ_values)(z)
1663+
## Hydrostatically balanced pressure profile
1664+
thermo_params = CAP.thermodynamics_params(params)
1665+
p_0 = FT(100_700) # Pa
1666+
p = hydrostatic_pressure_profile(; thermo_params, p_0, θ, q_tot)
1667+
function local_state(local_geometry)
1668+
(; z) = local_geometry.coordinates
1669+
return LocalState(; params, geometry = local_geometry,
1670+
thermo_state = TD.PhaseEquil_pθq(thermo_params, p(z), θ(z), q_tot(z)),
1671+
precip_state = NoPrecipState{typeof(z)}(),
1672+
)
1673+
end
1674+
return local_state
1675+
end

src/prognostic_equations/advection.jl

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,30 @@ NVTX.@annotate function horizontal_tracer_advection_tendency!(Yₜ, Y, p, t)
164164
return nothing
165165
end
166166

167+
"""
168+
ᶜρq_tot_vertical_transport_bc(flow, thermo_params, t, ᶠu³)
169+
170+
Computes the vertical transport of `ρq_tot` at the surface due to prescribed flow.
171+
172+
If the flow is not prescribed, this has no effect.
173+
174+
# Arguments
175+
- `flow`: The prescribed flow model, see [`PrescribedFlow`](@ref).
176+
- If `flow` is `nothing`, this has no effect.
177+
- `thermo_params`: The thermodynamic parameters, needed to compute surface air density.
178+
- `t`: The current time.
179+
- `ᶠu³`: The vertical velocity field.
180+
181+
# Returns
182+
- The vertical transport of `ρq_tot` at the surface due to prescribed flow.
183+
"""
184+
ᶜρq_tot_vertical_transport_bc(::Nothing, _, _, _) = NullBroadcasted()
185+
function ᶜρq_tot_vertical_transport_bc(flow::PrescribedFlow, thermo_params, t, ᶠu³)
186+
ρu₃qₜ_sfc_bc = get_ρu₃qₜ_surface(flow, thermo_params, t)
187+
ᶜadvdivᵥ = Operators.DivergenceF2C(; bottom = Operators.SetValue(ρu₃qₜ_sfc_bc))
188+
return @. lazy(-(ᶜadvdivᵥ(zero(ᶠu³))))
189+
end
190+
167191
"""
168192
explicit_vertical_advection_tendency!(Yₜ, Y, p, t)
169193
@@ -193,7 +217,7 @@ Modifies `Yₜ.c` (various tracers, `ρe_tot`, `ρq_tot`, `uₕ`), `Yₜ.f.u₃`
193217
`Yₜ.f.sgsʲs` (updraft `u₃`), and `Yₜ.c.sgs⁰.ρatke` as applicable.
194218
"""
195219
NVTX.@annotate function explicit_vertical_advection_tendency!(Yₜ, Y, p, t)
196-
(; turbconv_model) = p.atmos
220+
(; turbconv_model, prescribed_flow) = p.atmos
197221
n = n_prognostic_mass_flux_subdomains(turbconv_model)
198222
advect_tke = use_prognostic_tke(turbconv_model)
199223
point_type = eltype(Fields.coordinate_field(Y.c))
@@ -289,7 +313,11 @@ NVTX.@annotate function explicit_vertical_advection_tendency!(Yₜ, Y, p, t)
289313
ᶜq_tot = @. lazy(specific(Y.c.ρq_tot, Y.c.ρ))
290314
vtt = vertical_transport(ᶜρ, ᶠu³, ᶜq_tot, FT(dt), energy_q_tot_upwinding)
291315
vtt_central = vertical_transport(ᶜρ, ᶠu³, ᶜq_tot, FT(dt), Val(:none))
292-
@. Yₜ.c.ρq_tot += vtt - vtt_central
316+
vtt_bc = ᶜρq_tot_vertical_transport_bc(prescribed_flow, thermo_params, t, ᶠu³)
317+
if prescribed_flow isa PrescribedFlow
318+
vtt_central = NullBroadcasted() # turn off implicit advection
319+
end
320+
@. Yₜ.c.ρq_tot += vtt - vtt_central + vtt_bc
293321
end
294322

295323
if isnothing(ᶠf¹²)

src/prognostic_equations/dss.jl

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,37 @@ See also:
7474
dss!(Y_state, params, t_current)
7575
# The ClimaCore.Field objects within Y_state.c and Y_state.f are now updated
7676
# with DSS applied, ensuring continuity across distributed elements.
77+
```
7778
"""
7879

79-
NVTX.@annotate function dss!(Y, p, t)
80+
NVTX.@annotate function dss!(Y, p, t) # TODO: Rename to e.g. `apply_constraints!`
81+
prescribe_flow!(Y, p, t, p.atmos.prescribed_flow)
8082
if do_dss(axes(Y.c))
8183
Spaces.weighted_dss!(Y.c => p.ghost_buffer.c, Y.f => p.ghost_buffer.f)
8284
end
8385
return nothing
8486
end
87+
88+
prescribe_flow!(_, _, _, ::Nothing) = nothing
89+
function prescribe_flow!(Y, p, t, flow::PrescribedFlow)
90+
(; ᶜΦ) = p.core
91+
ᶠlg = Fields.local_geometry_field(Y.f)
92+
z = Fields.coordinate_field(Y.f).z
93+
@. Y.f.u₃ = C3(Geometry.WVector(flow(z, t)), ᶠlg)
94+
95+
### Fix energy to initial temperature
96+
ᶜlg = Fields.local_geometry_field(Y.c)
97+
local_state = InitialConditions.ShipwayHill2012()(p.params)
98+
get_ρ_init_dry(ls) = ls.thermo_state.ρ * (1 - ls.thermo_state.q_tot)
99+
get_T_init(ls) = TD.air_temperature(ls.thermo_params, ls.thermo_state)
100+
ᶜρ_init_dry = @. lazy(get_ρ_init_dry(local_state(ᶜlg)))
101+
ᶜT_init = @. lazy(get_T_init(local_state(ᶜlg)))
102+
103+
thermo_params = CAP.thermodynamics_params(p.params)
104+
105+
@. Y.c.ρ = ᶜρ_init_dry + Y.c.ρq_tot
106+
ᶜts = @. lazy(TD.PhaseEquil_ρTq(thermo_params, Y.c.ρ, ᶜT_init, Y.c.ρq_tot / Y.c.ρ))
107+
ᶜe_kin = compute_kinetic(Y.c.uₕ, Y.f.u₃)
108+
@. Y.c.ρe_tot = Y.c.ρ * TD.total_energy(thermo_params, ᶜts, ᶜe_kin, ᶜΦ)
109+
return nothing
110+
end

src/prognostic_equations/implicit/implicit_tendency.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ function implicit_vertical_advection_tendency!(Yₜ, Y, p, t)
131131
if !(moisture_model isa DryModel)
132132
ᶜq_tot = @. lazy(specific(Y.c.ρq_tot, Y.c.ρ))
133133
vtt = vertical_transport(Y.c.ρ, ᶠu³, ᶜq_tot, dt, Val(:none))
134+
if p.atmos.prescribed_flow isa PrescribedFlow
135+
vtt = NullBroadcasted() # Turn off implicit energy q_tot advection
136+
end
134137
@. Yₜ.c.ρq_tot += vtt
135138
end
136139

src/solver/model_getters.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,9 @@ function check_case_consistency(parsed_args)
658658
imp_vert_diff = parsed_args["implicit_diffusion"]
659659
vert_diff = parsed_args["vert_diff"]
660660
turbconv = parsed_args["turbconv"]
661+
topography = parsed_args["topography"]
662+
prescribed_flow = parsed_args["prescribed_flow"]
663+
auto_jacobian = parsed_args["use_auto_jacobian"]
661664

662665
ISDAC_mandatory = (ic, subs, surf, rad, extf)
663666
if "ISDAC" in ISDAC_mandatory
@@ -674,5 +677,14 @@ function check_case_consistency(parsed_args)
674677
"Implicit vertical diffusion is only supported when using a " *
675678
"turbulence convection model or vertical diffusion model.",
676679
)
680+
elseif prescribed_flow
681+
@assert(topography == "NoWarp",
682+
"Prescribed flow elides `set_velocity_at_surface!` and `set_velocity_at_top!` \
683+
which is needed for topography. Thus, prescribed flow must have flat surface."
684+
)
685+
@assert(auto_jacobian,
686+
"Prescribed flow modifies the implicit treatment of sound waves, \
687+
thus the manual sparse jacobian is not supported."
688+
)
677689
end
678690
end

0 commit comments

Comments
 (0)