Skip to content

Commit 9d4b9f4

Browse files
committed
Add topography types
1 parent cf4677e commit 9d4b9f4

File tree

5 files changed

+149
-129
lines changed

5 files changed

+149
-129
lines changed

docs/src/api.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ ClimaAtmos.AutoDenseJacobian
5959
ClimaAtmos.AutoSparseJacobian
6060
```
6161

62+
## Topography
63+
64+
```@docs
65+
ClimaAtmos.CosineTopography
66+
ClimaAtmos.AgnesiTopography
67+
ClimaAtmos.ScharTopography
68+
ClimaAtmos.EarthTopography
69+
ClimaAtmos.DCMIP200Topography
70+
ClimaAtmos.Hughes2023Topography
71+
```
72+
6273
### Internals
6374

6475
```@docs

src/solver/type_getters.jl

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -429,36 +429,37 @@ function get_initial_condition(parsed_args, atmos)
429429
end
430430
end
431431

432+
function get_topography(FT, parsed_args)
433+
topo_str = parsed_args["topography"]
434+
topo_types = Dict("NoWarp" => NoTopography(),
435+
"Cosine2D" => CosineTopography{2, FT}(),
436+
"Cosine3D" => CosineTopography{3, FT}(),
437+
"Agnesi" => AgnesiTopography{FT}(),
438+
"Schar" => ScharTopography{FT}(),
439+
"Earth" => EarthTopography(),
440+
"Hughes2023" => Hughes2023Topography(),
441+
"DCMIP200" => DCMIP200Topography(),
442+
)
443+
444+
@assert topo_str in keys(topo_types)
445+
return topo_types[topo_str]
446+
end
447+
432448
function get_steady_state_velocity(params, Y, parsed_args)
433449
parsed_args["check_steady_state"] || return nothing
434450
parsed_args["initial_condition"] == "ConstantBuoyancyFrequencyProfile" &&
435451
parsed_args["mesh_warp_type"] == "Linear" ||
436452
error("The steady-state velocity can currently be computed only for a \
437453
ConstantBuoyancyFrequencyProfile with Linear mesh warping")
438-
topography = parsed_args["topography"]
439-
steady_state_velocity = if topography == "NoWarp"
440-
steady_state_velocity_no_warp
441-
elseif topography == "Cosine2D"
442-
steady_state_velocity_cosine_2d
443-
elseif topography == "Cosine3D"
444-
steady_state_velocity_cosine_3d
445-
elseif topography == "Agnesi"
446-
steady_state_velocity_agnesi
447-
elseif topography == "Schar"
448-
steady_state_velocity_schar
449-
else
450-
error("The steady-state velocity for $topography topography cannot \
451-
be computed analytically")
452-
end
454+
topo = get_topography(eltype(params), parsed_args)
453455
top_level = Spaces.nlevels(axes(Y.c)) + Fields.half
454456
z_top = Fields.level(Fields.coordinate_field(Y.f).z, top_level)
455457

456-
# TODO: This can be very expensive! It should be moved to a separate CI job.
457458
@info "Approximating steady-state velocity"
458459
s = @timed_str begin
459-
ᶜu = steady_state_velocity.(params, Fields.coordinate_field(Y.c), z_top)
460+
ᶜu = steady_state_velocity.(topo, params, Fields.coordinate_field(Y.c), z_top)
460461
ᶠu =
461-
steady_state_velocity.(params, Fields.coordinate_field(Y.f), z_top)
462+
steady_state_velocity.(topo, params, Fields.coordinate_field(Y.f), z_top)
462463
end
463464
@info "Steady-state velocity approximation completed: $s"
464465
return (; ᶜu, ᶠu)
@@ -813,6 +814,7 @@ function get_simulation(config::AtmosConfig)
813814
end
814815

815816
tracers = get_tracers(config.parsed_args)
817+
816818
steady_state_velocity =
817819
get_steady_state_velocity(params, Y, config.parsed_args)
818820

src/topography/steady_state_solutions.jl

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import StaticArrays: @SVector
2-
32
background_u(::Type{FT}) where {FT} = FT(10)
43
background_N(::Type{FT}) where {FT} = FT(0.01) # This needs to be a small value.
54
background_T_sfc(::Type{FT}) where {FT} = FT(288)
@@ -167,29 +166,26 @@ end
167166
## Steady-state solutions for periodic topography
168167
##
169168

170-
function steady_state_velocity_no_warp(params, coord, z_top)
169+
function steady_state_velocity(::NoTopography, params, coord, z_top)
171170
FT = eltype(params)
172171
u = background_u(FT)
173172
return UVW(u, FT(0), FT(0))
174173
end
175174

176-
function steady_state_velocity_cosine_2d(params, coord, z_top)
175+
function steady_state_velocity(t::CosineTopography{2}, params, coord, z_top)
177176
FT = eltype(params)
178-
(; λ) = cosine_params(FT)
179177
(; x, z) = coord
180-
return steady_state_velocity_cosine(params, x, FT(0), z, λ, FT(Inf), z_top)
178+
return steady_state_velocity_cosine(params, x, FT(0), z, t.λ, oftype(t.λ, Inf), z_top, t.h_max)
181179
end
182180

183-
function steady_state_velocity_cosine_3d(params, coord, z_top)
181+
function steady_state_velocity(t::CosineTopography{3}, params, coord, z_top)
184182
FT = eltype(params)
185-
(; λ) = cosine_params(FT)
186183
(; x, y, z) = coord
187-
return steady_state_velocity_cosine(params, x, y, z, λ, λ, z_top)
184+
return steady_state_velocity_cosine(params, x, y, z, t.λ, t.λ, z_top, t.h_max)
188185
end
189186

190-
function steady_state_velocity_cosine(params, x, y, z, λ_x, λ_y, z_top)
187+
function steady_state_velocity_cosine(params, x, y, z, λ_x, λ_y, z_top, h_max)
191188
FT = eltype(params)
192-
(; h_max) = cosine_params(FT)
193189
u = background_u(FT)
194190
h = topography_cosine(x, y, λ_x, λ_y, h_max)
195191
η = (z - h) / (1 - h / z_top)
@@ -253,14 +249,14 @@ function steady_state_velocity_mountain_2d(
253249
return UVW(u + Δu, Δv, Δw)
254250
end
255251

256-
function steady_state_velocity_agnesi(params, coord, z_top)
252+
function steady_state_velocity(t::AgnesiTopography, params, coord, z_top)
257253
FT = eltype(params)
258-
(; h_max, x_center, a) = agnesi_params(FT)
254+
(; h_max, x_center, a) = t
259255
topography_agnesi_Fh(k_x) = h_max * a / 2 * exp(-a * abs(k_x))
260256
n_efolding_intervals = -log(eps(FT))
261257
k_x_max = n_efolding_intervals / a
262258
return steady_state_velocity_mountain_2d(
263-
topography_agnesi,
259+
topography_function(t),
264260
topography_agnesi_Fh,
265261
params,
266262
coord,
@@ -270,9 +266,9 @@ function steady_state_velocity_agnesi(params, coord, z_top)
270266
)
271267
end
272268

273-
function steady_state_velocity_schar(params, coord, z_top)
269+
function steady_state_velocity(t::ScharTopography, params, coord, z_top)
274270
FT = eltype(params)
275-
(; h_max, x_center, λ, a) = schar_params(FT)
271+
(; h_max, x_center, λ, a) = t
276272
k_peak = 2 * FT(π) / λ
277273
Fh_coef = h_max * a / (8 * sqrt(FT(π)))
278274
topography_schar_Fh(k_x) =
@@ -284,7 +280,7 @@ function steady_state_velocity_schar(params, coord, z_top)
284280
n_efolding_intervals = -log(eps(FT))
285281
k_x_max = k_peak + 2 * sqrt(n_efolding_intervals) / a
286282
return steady_state_velocity_mountain_2d(
287-
topography_schar,
283+
topography_function(t),
288284
topography_schar_Fh,
289285
params,
290286
coord,

src/topography/topography.jl

Lines changed: 99 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,101 @@
1+
using ClimaCore: Geometry, Spaces, Fields
2+
3+
##
4+
## Topography profiles for 2D and 3D boxes
5+
##
6+
7+
# The parameters of these profiles should be defined separately so that they
8+
# can also be used to compute analytic solutions.
9+
10+
abstract type AbstractTopography end
11+
Base.broadcastable(t::AbstractTopography) = tuple(t)
12+
topography_function(topo) = Base.Fix1(topography_function, topo)
13+
14+
struct NoTopography <: AbstractTopography end
15+
16+
# Analytical topography types for idealized test cases
17+
18+
"""
19+
CosineTopography{D, FT}(; h_max = 25, λ = 25e3)
20+
21+
Cosine hill topography in 2D or 3D.
22+
23+
# Arguments
24+
- `h_max::FT`: Maximum elevation (m)
25+
- `λ::FT`: Wavelength of the cosine hills (m)
26+
"""
27+
Base.@kwdef struct CosineTopography{D, FT} <: AbstractTopography
28+
h_max::FT = 25.0
29+
λ::FT = 25e3
30+
end
31+
32+
topography_function(t::CosineTopography{2}, coord) =
33+
topography_cosine(coord.x, zero(coord.x), t.λ, oftype(t.λ, Inf), t.h_max)
34+
35+
topography_function(t::CosineTopography{3}, coord) =
36+
topography_cosine(coord.x, coord.y, t.λ, t.λ, t.h_max)
37+
38+
topography_cosine(x, y, λ_x, λ_y, h_max) =
39+
h_max * cospi(2 * x / λ_x) * cospi(2 * y / λ_y)
40+
41+
"""
42+
AgnesiTopography{FT}(; h_max = 25, x_center = 50e3, a = 5e3)
43+
44+
Witch of Agnesi mountain topography for 2D simulations.
45+
46+
# Arguments
47+
- `h_max`: Maximum elevation (m)
48+
- `x_center`: Center position (m)
49+
- `a`: Mountain width parameter (m)
50+
"""
51+
Base.@kwdef struct AgnesiTopography{FT} <: AbstractTopography
52+
h_max::FT = 25.0
53+
x_center::FT = 50e3
54+
a::FT = 5e3
55+
end
56+
57+
topography_function((; h_max, x_center, a)::AgnesiTopography, (; x)) =
58+
h_max / (1 + ((x - x_center) / a)^2)
59+
60+
"""
61+
ScharTopography{FT}(; h_max = 25, x_center = 50e3, λ = 4e3, a = 5e3)
62+
63+
Schar mountain topography for 2D simulations.
64+
65+
# Arguments
66+
- `h_max`: Maximum elevation (m)
67+
- `x_center`: Center position (m)
68+
- `λ`: Wavelength parameter (m)
69+
- `a`: Mountain width parameter (m)
70+
"""
71+
Base.@kwdef struct ScharTopography{FT} <: AbstractTopography
72+
h_max::FT = 25.0
73+
x_center::FT = 50e3
74+
λ::FT = 4e3
75+
a::FT = 5e3
76+
end
77+
78+
topography_function((; h_max, x_center, λ, a)::ScharTopography, (; x)) =
79+
h_max * exp(-(x - x_center)^2 / a^2) * cospi((x - x_center) / λ)^2
80+
81+
# Data-based topography types
82+
83+
"""
84+
EarthTopography()
85+
86+
Earth topography from ETOPO2022 data files.
87+
"""
88+
struct EarthTopography <: AbstractTopography end
89+
190
"""
2-
topography_dcmip200(coord)
91+
DCMIP200Topography()
392
493
Surface elevation for the DCMIP-2-0-0 test problem.
594
"""
95+
struct DCMIP200Topography <: AbstractTopography end
96+
97+
topography_function(::DCMIP200Topography, coord) = topography_dcmip200(coord)
98+
699
function topography_dcmip200(coord)
7100
FT = Geometry.float_type(coord)
8101
λ, ϕ = coord.long, coord.lat
@@ -21,10 +114,14 @@ function topography_dcmip200(coord)
21114
end
22115

23116
"""
24-
topography_hughes2023(coord)
117+
Hughes2023Topography()
25118
26119
Surface elevation for baroclinic wave test from Hughes and Jablonowski (2023).
27120
"""
121+
struct Hughes2023Topography <: AbstractTopography end
122+
123+
topography_function(::Hughes2023Topography, coord) = topography_hughes2023(coord)
124+
28125
function topography_hughes2023(coord)
29126
FT = Geometry.float_type(coord)
30127
λ, ϕ = coord.long, coord.lat
@@ -50,66 +147,3 @@ function topography_hughes2023(coord)
50147
),
51148
)
52149
end
53-
54-
##
55-
## Topography profiles for 2D and 3D boxes
56-
##
57-
58-
# The parameters of these profiles should be defined separately so that they
59-
# can also be used to compute analytic solutions.
60-
61-
"""
62-
topography_agnesi(coord)
63-
64-
Surface elevation for a 2D Witch of Agnesi mountain, centered at `x = 50 km`.
65-
"""
66-
function topography_agnesi(coord)
67-
FT = Geometry.float_type(coord)
68-
(; x) = coord
69-
(; h_max, x_center, a) = agnesi_params(FT)
70-
return h_max / (1 + ((x - x_center) / a)^2)
71-
end
72-
agnesi_params(::Type{FT}) where {FT} =
73-
(; h_max = FT(25), x_center = FT(50e3), a = FT(5e3))
74-
75-
"""
76-
topography_schar(coord)
77-
78-
Surface elevation for a 2D Schar mountain, centered at `x = 50 km`.
79-
"""
80-
function topography_schar(coord)
81-
FT = Geometry.float_type(coord)
82-
(; x) = coord
83-
(; h_max, x_center, λ, a) = schar_params(FT)
84-
return h_max * exp(-(x - x_center)^2 / a^2) * cospi((x - x_center) / λ)^2
85-
end
86-
schar_params(::Type{FT}) where {FT} =
87-
(; h_max = FT(25), x_center = FT(50e3), λ = FT(4e3), a = FT(5e3))
88-
89-
"""
90-
topography_cosine_2d(coord)
91-
92-
Surface elevation for 2D cosine hills.
93-
"""
94-
function topography_cosine_2d(coord)
95-
FT = Geometry.float_type(coord)
96-
(; x) = coord
97-
(; h_max, λ) = cosine_params(FT)
98-
return topography_cosine(x, FT(0), λ, FT(Inf), h_max)
99-
end
100-
101-
"""
102-
topography_cosine_3d(coord)
103-
104-
Surface elevation for 3D cosine hills.
105-
"""
106-
function topography_cosine_3d(coord)
107-
FT = Geometry.float_type(coord)
108-
(; x, y) = coord
109-
(; h_max, λ) = cosine_params(FT)
110-
return topography_cosine(x, y, λ, λ, h_max)
111-
end
112-
113-
topography_cosine(x, y, λ_x, λ_y, h_max) =
114-
h_max * cospi(2 * x / λ_x) * cospi(2 * y / λ_y)
115-
cosine_params(::Type{FT}) where {FT} = (; h_max = FT(25), λ = FT(25e3))

0 commit comments

Comments
 (0)