Skip to content

Commit 93dc527

Browse files
committed
Add topography module and types
1 parent cf4677e commit 93dc527

File tree

5 files changed

+173
-58
lines changed

5 files changed

+173
-58
lines changed

docs/src/api.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,23 @@ 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+
ClimaAtmos.topography_schar
72+
ClimaAtmos.topography_cosine_3d
73+
ClimaAtmos.topography_agnesi
74+
ClimaAtmos.topography_hughes2023
75+
ClimaAtmos.topography_dcmip200
76+
ClimaAtmos.topography_cosine_2d
77+
```
78+
6279
### Internals
6380

6481
```@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(parsed_args)
433+
topo_str = parsed_args["topography"]
434+
topo_types = Dict("NoWarp" => NoTopography(),
435+
"Cosine2D" => CosineTopography2D(),
436+
"Cosine3D" => CosineTopography3D(),
437+
"Agnesi" => AgnesiTopography(),
438+
"Schar" => ScharTopography(),
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(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+
# Main.@infiltrate
816818
steady_state_velocity =
817819
get_steady_state_velocity(params, Y, config.parsed_args)
818820

src/topography/steady_state_solutions.jl

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import StaticArrays: @SVector
1+
import StaticArrays: SVector, @SVector
2+
# import .Topography: NoTopography, AgnesiTopography, ScharTopography, CosineTopography
3+
# import .Topography: topography_agnesi, topography_schar, topography_cosine
4+
# import .Topography: agnesi_params, schar_params, cosine_params
5+
26

37
background_u(::Type{FT}) where {FT} = FT(10)
48
background_N(::Type{FT}) where {FT} = FT(0.01) # This needs to be a small value.
@@ -167,23 +171,26 @@ end
167171
## Steady-state solutions for periodic topography
168172
##
169173

170-
function steady_state_velocity_no_warp(params, coord, z_top)
174+
function steady_state_velocity(::NoTopography, params, coord, z_top)
171175
FT = eltype(params)
172176
u = background_u(FT)
173177
return UVW(u, FT(0), FT(0))
174178
end
175179

176-
function steady_state_velocity_cosine_2d(params, coord, z_top)
180+
function steady_state_velocity(t::CosineTopography{2}, params, coord, z_top)
177181
FT = eltype(params)
178182
(; λ) = cosine_params(FT)
179-
(; x, z) = coord
180-
return steady_state_velocity_cosine(params, x, FT(0), z, λ, FT(Inf), z_top)
183+
x = coord.x
184+
z = coord.z
185+
return steady_state_velocity_cosine(params, x, FT(0), z, λ, λ, z_top)
181186
end
182187

183-
function steady_state_velocity_cosine_3d(params, coord, z_top)
188+
function steady_state_velocity(t::CosineTopography{3}, params, coord, z_top)
184189
FT = eltype(params)
185190
(; λ) = cosine_params(FT)
186-
(; x, y, z) = coord
191+
x = coord.x
192+
y = coord.y
193+
z = coord.z
187194
return steady_state_velocity_cosine(params, x, y, z, λ, λ, z_top)
188195
end
189196

@@ -253,7 +260,7 @@ function steady_state_velocity_mountain_2d(
253260
return UVW(u + Δu, Δv, Δw)
254261
end
255262

256-
function steady_state_velocity_agnesi(params, coord, z_top)
263+
function steady_state_velocity(::AgnesiTopography, params, coord, z_top)
257264
FT = eltype(params)
258265
(; h_max, x_center, a) = agnesi_params(FT)
259266
topography_agnesi_Fh(k_x) = h_max * a / 2 * exp(-a * abs(k_x))
@@ -270,7 +277,7 @@ function steady_state_velocity_agnesi(params, coord, z_top)
270277
)
271278
end
272279

273-
function steady_state_velocity_schar(params, coord, z_top)
280+
function steady_state_velocity(::ScharTopography, params, coord, z_top)
274281
FT = eltype(params)
275282
(; h_max, x_center, λ, a) = schar_params(FT)
276283
k_peak = 2 * FT(π) / λ

src/topography/topography.jl

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
# module Topography
2+
3+
using ClimaCore: Geometry, Spaces, Fields
4+
5+
# export NoTopography, AgnesiTopography, ScharTopography, CosineTopography
6+
# export EarthTopography, DCMIP200Topography, Hughes2023Topography
7+
8+
# export topography_dcmip200, topography_hughes2023
9+
# export topography_agnesi, agnesi_params
10+
# export topography_schar, schar_params
11+
# export topography_cosine_2d, topography_cosine_3d
12+
# export topography_cosine, cosine_params
13+
114
"""
215
topography_dcmip200(coord)
316
@@ -105,11 +118,109 @@ Surface elevation for 3D cosine hills.
105118
"""
106119
function topography_cosine_3d(coord)
107120
FT = Geometry.float_type(coord)
108-
(; x, y) = coord
109-
(; h_max, λ) = cosine_params(FT)
121+
x, y = coord.x, coord.y
122+
cos_params = cosine_params(FT)
123+
h_max, λ = cos_params.h_max, cos_params.λ
110124
return topography_cosine(x, y, λ, λ, h_max)
111125
end
112126

113127
topography_cosine(x, y, λ_x, λ_y, h_max) =
114128
h_max * cospi(2 * x / λ_x) * cospi(2 * y / λ_y)
115129
cosine_params(::Type{FT}) where {FT} = (; h_max = FT(25), λ = FT(25e3))
130+
131+
abstract type AbstractTopography end
132+
133+
topo_func(t::AbstractTopography) = error("No topography function for topography $t")
134+
135+
struct NoTopography <: AbstractTopography end
136+
137+
# Analytical topography types for idealized test cases
138+
139+
"""
140+
CosineTopography(; h_max = 25, λ = 25e3, dim = 2)
141+
142+
Cosine hill topography in 2D or 3D.
143+
144+
# Arguments
145+
- `h_max::FT`: Maximum elevation (m)
146+
- `λ::FT`: Wavelength of the cosine hills (m)
147+
- `dim::Int`: Spatial dimension (2 or 3)
148+
"""
149+
Base.@kwdef struct CosineTopography{D, FT} <: AbstractTopography
150+
h_max::FT = 25.0
151+
λ::FT = 25e3
152+
end
153+
154+
CosineTopography2D() = CosineTopography{2, Float64}()
155+
CosineTopography3D() = CosineTopography{3, Float64}()
156+
157+
topo_func(t::CosineTopography{2}) = topography_cosine_2d
158+
topo_func(t::CosineTopography{3}) = topography_cosine_3d
159+
160+
"""
161+
AgnesiTopography(; h_max = 25, x_center = 50e3, a = 5e3)
162+
163+
Witch of Agnesi mountain topography for 2D simulations.
164+
165+
# Arguments
166+
- `h_max`: Maximum elevation (m)
167+
- `x_center`: Center position (m)
168+
- `a`: Mountain width parameter (m)
169+
"""
170+
Base.@kwdef struct AgnesiTopography{FT} <: AbstractTopography
171+
h_max::FT = 25.0
172+
x_center::FT = 50e3
173+
a::FT = 5e3
174+
end
175+
176+
topo_func(::AgnesiTopography) = topography_agnesi
177+
178+
179+
"""
180+
ScharTopography(; h_max = 25, x_center = 50e3, λ = 4e3, a = 5e3)
181+
182+
Schar mountain topography for 2D simulations.
183+
184+
# Arguments
185+
- `h_max`: Maximum elevation (m)
186+
- `x_center`: Center position (m)
187+
- `λ`: Wavelength parameter (m)
188+
- `a`: Mountain width parameter (m)
189+
"""
190+
Base.@kwdef struct ScharTopography{FT} <: AbstractTopography
191+
h_max::FT = 25.0
192+
x_center::FT = 50e3
193+
λ::FT = 4e3
194+
a::FT = 5e3
195+
end
196+
topo_func(::ScharTopography) = topography_schar
197+
198+
199+
# Data-based topography types
200+
201+
"""
202+
EarthTopography()
203+
204+
Earth topography from ETOPO2022 data files.
205+
"""
206+
struct EarthTopography <: AbstractTopography end
207+
208+
"""
209+
DCMIP200Topography()
210+
211+
Surface elevation for the DCMIP-2-0-0 test problem.
212+
"""
213+
struct DCMIP200Topography <: AbstractTopography end
214+
215+
topo_func(::DCMIP200Topography) = topography_dcmip200
216+
217+
"""
218+
Hughes2023Topography()
219+
220+
Surface elevation for baroclinic wave test from Hughes and Jablonowski (2023).
221+
"""
222+
struct Hughes2023Topography <: AbstractTopography end
223+
224+
topo_func(::Hughes2023Topography) = topography_hughes2023
225+
226+
# end

src/utils/common_spaces.jl

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -96,21 +96,11 @@ function make_hybrid_spaces(
9696
)
9797
z_grid = Grids.FiniteDifferenceGrid(z_topology)
9898

99-
topography = parsed_args["topography"]
100-
@assert topography in (
101-
"NoWarp",
102-
"Earth",
103-
"DCMIP200",
104-
"Hughes2023",
105-
"Agnesi",
106-
"Schar",
107-
"Cosine2D",
108-
"Cosine3D",
109-
)
110-
if topography == "NoWarp"
99+
topography = get_topography(parsed_args)
100+
if topography isa NoTopography
111101
z_surface = zeros(h_space)
112102
@info "No surface orography warp applied"
113-
elseif topography == "Earth"
103+
elseif topography isa EarthTopography
114104
z_surface = SpaceVaryingInput(
115105
AA.earth_orography_file_path(;
116106
context = ClimaComms.context(h_space),
@@ -120,26 +110,14 @@ function make_hybrid_spaces(
120110
)
121111
@info "Remapping Earth orography from ETOPO2022 data onto horizontal space"
122112
else
123-
topography_function = if topography == "DCMIP200"
124-
topography_dcmip200
125-
elseif topography == "Hughes2023"
126-
topography_hughes2023
127-
elseif topography == "Agnesi"
128-
topography_agnesi
129-
elseif topography == "Schar"
130-
topography_schar
131-
elseif topography == "Cosine2D"
132-
topography_cosine_2d
133-
elseif topography == "Cosine3D"
134-
topography_cosine_3d
135-
end
113+
topography_function = topo_func(topography)
136114
z_surface = SpaceVaryingInput(topography_function, h_space)
137-
@info "Using $topography orography"
115+
@info "Using $(nameof(typeof(topography))) orography"
138116
end
139117

140-
if topography == "NoWarp"
118+
if topography isa NoTopography
141119
hypsography = Hypsography.Flat()
142-
elseif topography == "Earth"
120+
elseif topography isa EarthTopography
143121
mask(x::FT) where {FT} = x * FT(x > 0)
144122
z_surface = @. mask(z_surface)
145123
# diff_cfl = νΔt/Δx²

0 commit comments

Comments
 (0)