Skip to content

Commit 7160992

Browse files
committed
ft: Add Kinematic Driver (KiD) model configuration
1 parent f7b702b commit 7160992

File tree

11 files changed

+276
-1
lines changed

11 files changed

+276
-1
lines changed

config/default_configs/default_config.yml

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

docs/bibliography.bib

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

examples/hybrid/KiD_driver.jl

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import ClimaComms
2+
import ClimaAtmos as CA
3+
import ClimaCore: Fields
4+
import YAML
5+
import ClimaComms
6+
7+
import Random
8+
# Random.seed!(Random.MersenneTwister())
9+
Random.seed!(1234)
10+
11+
# --> get config
12+
configs_path = joinpath(pkgdir(CA), "config/model_configs/")
13+
pth = joinpath(configs_path, "kinematic_driver.yml");
14+
job_id = "kinematic_driver";
15+
config_dict = YAML.load_file(pth)
16+
# <--
17+
18+
19+
config = CA.AtmosConfig(config_dict; job_id)
20+
simulation = CA.get_simulation(config);
21+
22+
sol_res = CA.solve_atmos!(simulation); # solve!
23+
24+
(; integrator) = simulation;
25+
(; p) = integrator;
26+
(; atmos, params) = p;
27+
28+
# --> Make ci plots
29+
# ]add ClimaAnalysis, ClimaCoreSpectra
30+
include(joinpath(pkgdir(CA), "post_processing", "ci_plots.jl"))
31+
make_plots(Val(:kinematic_driver), simulation.output_dir)
32+
# <--
33+
34+
# --> ClimaAnalysis
35+
import ClimaAnalysis
36+
# using ClimaAnalysis.Visualize
37+
import ClimaAnalysis.Visualize as viz
38+
using ClimaAnalysis.Utils: kwargs
39+
using CairoMakie;
40+
CairoMakie.activate!();
41+
# using GLMakie; GLMakie.activate!()
42+
simdir = ClimaAnalysis.SimDir(simulation.output_dir);
43+
44+
# entr = get(simdir; short_name = "entr")
45+
# entr.dims # (time, x, y, z)
46+
47+
# fig = Figure();
48+
# viz.plot!(fig, entr, time=0, x=0, y=0, more_kwargs = Dict(:axis => kwargs(dim_on_y = true)))
49+
# viz.plot!(fig, entr, x=0, y=0);
50+
# fig
51+
# <--
52+
53+
54+
55+
#=
56+
%use upwinding for the rain -- yes!
57+
58+
qr, qs, all specific humidities,
59+
60+
take 30% acoustic Courant number c=300m/s, divided by vertical res
61+
- for ~200
62+
63+
ill make some plots
64+
65+
is smag applied to qr??? check this!
66+
67+
most likely precip would cause blow-ups.
68+
=#

post_processing/ci_plots.jl

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,3 +1689,35 @@ 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 = ["hus", "clw", "husra", "ta", "thetaa", "wa", "cli", "hussn"]
1703+
short_names = short_names collect(keys(simdirs[1].vars))
1704+
vars = map_comparison(simdirs, short_names) do simdir, short_name
1705+
var = slice(get(simdir; short_name), x = 0, y = 0)
1706+
if short_name in ["hus", "clw", "husra", "cli", "hussn"]
1707+
var.data .= var.data .* 1000
1708+
var.attributes["units"] = "g/kg"
1709+
end
1710+
return rescale_time_to_min(var)
1711+
end
1712+
file_contour = make_plots_generic(output_paths, vars;
1713+
output_name = "tmp_contour",
1714+
)
1715+
1716+
short_names_lines = ["lwp", "rwp", "pr"]
1717+
short_names_lines = short_names_lines collect(keys(simdirs[1].vars))
1718+
vars_lines = map_comparison(simdirs, short_names_lines) do simdir, short_name
1719+
var = slice(get(simdir; short_name), x = 0, y = 0)
1720+
return rescale_time_to_min(var)
1721+
end
1722+
make_plots_generic(output_paths, vars_lines; summary_files = [file_contour])
1723+
end

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 = PrecipStateMassNum(; q_rai = FT(0), q_sno = FT(0)),
1672+
)
1673+
end
1674+
return local_state
1675+
end

src/prognostic_equations/dss.jl

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,42 @@ 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

7980
NVTX.@annotate function dss!(Y, p, t)
8081
if do_dss(axes(Y.c))
8182
Spaces.weighted_dss!(Y.c => p.ghost_buffer.c, Y.f => p.ghost_buffer.f)
8283
end
84+
prescribe_flow!(Y, p, t, p.atmos.prescribed_flow)
8385
return nothing
8486
end
87+
88+
prescribe_flow!(Y, p, t, ::Nothing) = nothing
89+
function prescribe_flow!(Y, p, t, flow::PrescribedFlow)
90+
FT = eltype(p.params)
91+
(; prescribed_u₃) = flow
92+
ᶠlg = Fields.local_geometry_field(Y.f)
93+
@. Y.f.u₃ = C3(Geometry.WVector(prescribed_u₃(FT, t)), ᶠlg)
94+
95+
return nothing # comment out to try fixing energy
96+
97+
### Fix energy to initial temperature
98+
ᶜlg = Fields.local_geometry_field(Y.c)
99+
local_state = InitialConditions.ShipwayHill2012()(p.params)
100+
get_ρ_init(ls) = TD.air_density(ls.thermo_params, ls.thermo_state)
101+
get_T_init(ls) = TD.air_temperature(ls.thermo_params, ls.thermo_state)
102+
ᶜρ_init = @. lazy(get_ρ_init(local_state(ᶜlg)))
103+
ᶜT_init = @. lazy(get_T_init(local_state(ᶜlg)))
104+
105+
thermo_params = CAP.thermodynamics_params(p.params)
106+
grav = CAP.grav(p.params)
107+
z = Fields.coordinate_field(Y.c).z
108+
109+
@. Y.c.ρ = ᶜρ_init + Y.c.ρq_tot
110+
ᶜts = @. lazy(TD.PhaseEquil_ρTq(thermo_params, Y.c.ρ, ᶜT_init, Y.c.ρq_tot / Y.c.ρ))
111+
ᶜe_kin = compute_kinetic(Y.c.uₕ, Y.f.u₃)
112+
ᶜe_pot = @. lazy(grav * z)
113+
@. Y.c.ρe_tot = Y.c.ρ * TD.total_energy(thermo_params, ᶜts, ᶜe_kin, ᶜe_pot)
114+
return nothing
115+
end

src/solver/type_getters.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ function get_atmos(config::AtmosConfig, params)
114114
FT,
115115
)
116116

117+
if parsed_args["prescribed_flow"]
118+
function prescribed_u₃(FT::Type{<:Real}, _t)
119+
t = FT(_t)
120+
w1 = FT(2)
121+
t1 = FT(600) # 10 minutes
122+
return t < t1 ? w1 * sin* t / t1) : FT(0)
123+
end
124+
prescribed_flow = PrescribedFlow(; prescribed_u₃)
125+
else
126+
prescribed_flow = nothing
127+
end
128+
117129
atmos = AtmosModel(;
118130
# AtmosWater - Moisture, Precipitation & Clouds
119131
moisture_model,
@@ -130,6 +142,9 @@ function get_atmos(config::AtmosConfig, params)
130142
advection_test,
131143
scm_coriolis = get_scm_coriolis(parsed_args, FT),
132144

145+
# PrescribedFlow
146+
prescribed_flow,
147+
133148
# AtmosRadiation
134149
radiation_mode = final_radiation_mode,
135150
ozone,
@@ -433,6 +448,8 @@ function get_initial_condition(parsed_args, atmos)
433448
parsed_args["start_date"],
434449
parsed_args["era5_initial_condition_dir"],
435450
)
451+
elseif parsed_args["initial_condition"] == "ShipwayHill2012"
452+
return ICs.ShipwayHill2012()
436453
else
437454
error(
438455
"Unknown `initial_condition`: $(parsed_args["initial_condition"])",

src/solver/types.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ import Dates
66
import ClimaUtilities.ClimaArtifacts: @clima_artifact
77
import LazyArtifacts
88

9+
abstract type AbstractPrescribedFlow end
10+
@kwdef struct PrescribedFlow
11+
prescribed_u₃::Function
12+
end
13+
914
abstract type AbstractMoistureModel end
1015
abstract type AbstractMoistModel <: AbstractMoistureModel end
1116
struct DryModel <: AbstractMoistureModel end
@@ -681,11 +686,12 @@ Base.broadcastable(x::AtmosGravityWave) = tuple(x)
681686
Base.broadcastable(x::AtmosSponge) = tuple(x)
682687
Base.broadcastable(x::AtmosSurface) = tuple(x)
683688

684-
struct AtmosModel{W, SCM, R, TC, GW, VD, SP, SU, NU}
689+
struct AtmosModel{W, SCM, R, TC, PF, GW, VD, SP, SU, NU}
685690
water::W
686691
scm_setup::SCM
687692
radiation::R
688693
turbconv::TC
694+
prescribed_flow::PF
689695
gravity_wave::GW
690696
vertical_diffusion::VD
691697
sponge::SP
@@ -701,6 +707,7 @@ const ATMOS_MODEL_GROUPS = (
701707
(AtmosWater, :water),
702708
(AtmosRadiation, :radiation),
703709
(AtmosTurbconv, :turbconv),
710+
(PrescribedFlow, :prescribed_flow),
704711
(AtmosGravityWave, :gravity_wave),
705712
(AtmosSponge, :sponge),
706713
(AtmosSurface, :surface),
@@ -918,11 +925,14 @@ function AtmosModel(; kwargs...)
918925
disable_surface_flux_tendency =
919926
get(atmos_model_kwargs, :disable_surface_flux_tendency, false)
920927

928+
prescribed_flow = get(atmos_model_kwargs, :prescribed_flow, nothing)
929+
921930
return AtmosModel{
922931
typeof(water),
923932
typeof(scm_setup),
924933
typeof(radiation),
925934
typeof(turbconv),
935+
typeof(prescribed_flow),
926936
typeof(gravity_wave),
927937
typeof(vertical_diffusion),
928938
typeof(sponge),
@@ -933,6 +943,7 @@ function AtmosModel(; kwargs...)
933943
scm_setup,
934944
radiation,
935945
turbconv,
946+
prescribed_flow,
936947
gravity_wave,
937948
vertical_diffusion,
938949
sponge,

src/surface_conditions/surface_setups.jl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,27 @@ function (::TRMM_LBA)(params)
242242
return surface_state
243243
end
244244

245+
struct ShipwayHill2012 end
246+
function (::ShipwayHill2012)(params)
247+
function surface_state(surface_coordinates, interior_z, t)
248+
FT = eltype(surface_coordinates)
249+
T = FT(297.9) # surface temperature (K)
250+
p = FT(100700) # surface pressure (Pa)
251+
rv₀ = FT(0.015) # water vapour mixing ratio at surface (kg/kg)
252+
q_vap = rv₀ / (1 + rv₀) # specific humidity at surface, assuming unsaturated surface air (kg/kg)
253+
# Exchange coefficients, from Rico, above >>>
254+
# Adjust the coefficients from 20 m to the actual value of z.
255+
z0 = FT(1.5e-4)
256+
adjustment = (log(20 / z0) / log(interior_z / z0))^2
257+
Cd = FT(0.001229) * adjustment
258+
Ch = FT(0.001094) * adjustment
259+
parameterization = ExchangeCoefficients(; Cd, Ch)
260+
# <<<
261+
return SurfaceState(; parameterization, T, p, q_vap)
262+
end
263+
return surface_state
264+
end
265+
245266
struct SimplePlume end
246267
function (::SimplePlume)(params)
247268
FT = eltype(params)

0 commit comments

Comments
 (0)