Skip to content

Commit 3804a45

Browse files
authored
Add Bryan/Fritsch warm bubble test case (#176)
1 parent 1542888 commit 3804a45

1 file changed

Lines changed: 178 additions & 0 deletions

File tree

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
using LibTrixi
2+
using OrdinaryDiffEq
3+
using Trixi
4+
5+
# Warm bubble test case from
6+
# - Wicker, L. J., and Skamarock, W. C. (1998)
7+
# A time-splitting scheme for the elastic equations incorporating
8+
# second-order Runge–Kutta time differencing
9+
# [DOI: 10.1175/1520-0493(1998)126%3C1992:ATSSFT%3E2.0.CO;2](https://doi.org/10.1175/1520-0493(1998)126%3C1992:ATSSFT%3E2.0.CO;2)
10+
# See also
11+
# - Bryan and Fritsch (2002)
12+
# A Benchmark Simulation for Moist Nonhydrostatic Numerical Models
13+
# [DOI: 10.1175/1520-0493(2002)130<2917:ABSFMN>2.0.CO;2](https://doi.org/10.1175/1520-0493(2002)130<2917:ABSFMN>2.0.CO;2)
14+
# - Carpenter, Droegemeier, Woodward, Hane (1990)
15+
# Application of the Piecewise Parabolic Method (PPM) to
16+
# Meteorological Modeling
17+
# [DOI: 10.1175/1520-0493(1990)118<0586:AOTPPM>2.0.CO;2](https://doi.org/10.1175/1520-0493(1990)118<0586:AOTPPM>2.0.CO;2)
18+
struct WarmBubbleSetup
19+
# Physical constants
20+
g::Float64 # gravity of earth
21+
c_p::Float64 # heat capacity for constant pressure (dry air)
22+
c_v::Float64 # heat capacity for constant volume (dry air)
23+
gamma::Float64 # heat capacity ratio (dry air)
24+
25+
function WarmBubbleSetup(; g = 9.81, c_p = 1004.0, c_v = 717.0, gamma = c_p / c_v)
26+
new(g, c_p, c_v, gamma)
27+
end
28+
end
29+
30+
# Initial condition
31+
function (setup::WarmBubbleSetup)(x, t, equations::CompressibleEulerEquations2D)
32+
@unpack g, c_p, c_v = setup
33+
34+
# center of perturbation
35+
center_x = 10000.0
36+
center_z = 2000.0
37+
# radius of perturbation
38+
radius = 2000.0
39+
# distance of current x to center of perturbation
40+
r = sqrt((x[1] - center_x)^2 + (x[2] - center_z)^2)
41+
42+
# perturbation in potential temperature
43+
potential_temperature_ref = 300.0
44+
potential_temperature_perturbation = 0.0
45+
if r <= radius
46+
potential_temperature_perturbation = 2 * cospi(0.5 * r / radius)^2
47+
end
48+
potential_temperature = potential_temperature_ref + potential_temperature_perturbation
49+
50+
# Exner pressure, solves hydrostatic equation for x[2]
51+
exner = 1 - g / (c_p * potential_temperature) * x[2]
52+
53+
# pressure
54+
p_0 = 100_000.0 # reference pressure
55+
R = c_p - c_v # gas constant (dry air)
56+
p = p_0 * exner^(c_p / R)
57+
58+
# temperature
59+
T = potential_temperature * exner
60+
61+
# density
62+
rho = p / (R * T)
63+
64+
v1 = 20.0
65+
v2 = 0.0
66+
E = c_v * T + 0.5 * (v1^2 + v2^2)
67+
return SVector(rho, rho * v1, rho * v2, rho * E)
68+
end
69+
70+
# Source terms
71+
@inline function (setup::WarmBubbleSetup)(u, x, t, equations::CompressibleEulerEquations2D)
72+
@unpack g = setup
73+
rho, _, rho_v2, _ = u
74+
return SVector(zero(eltype(u)), zero(eltype(u)), -g * rho, -g * rho_v2)
75+
end
76+
77+
78+
# The function to create the simulation state needs to be named `init_simstate`
79+
function init_simstate()
80+
81+
###############################################################################
82+
# semidiscretization of the compressible Euler equations
83+
warm_bubble_setup = WarmBubbleSetup()
84+
85+
equations = CompressibleEulerEquations2D(warm_bubble_setup.gamma)
86+
87+
boundary_conditions = (x_neg = boundary_condition_periodic,
88+
x_pos = boundary_condition_periodic,
89+
y_neg = boundary_condition_slip_wall,
90+
y_pos = boundary_condition_slip_wall)
91+
92+
polydeg = 3
93+
basis = LobattoLegendreBasis(polydeg)
94+
95+
# This is a good estimate for the speed of sound in this example.
96+
# Other values between 300 and 400 should work as well.
97+
surface_flux = FluxLMARS(340.0)
98+
99+
volume_flux = flux_kennedy_gruber
100+
volume_integral = VolumeIntegralFluxDifferencing(volume_flux)
101+
102+
solver = DGSEM(basis, surface_flux, volume_integral)
103+
104+
coordinates_min = (0.0, 0.0)
105+
coordinates_max = (20_000.0, 10_000.0)
106+
107+
# Same coordinates as in examples/structured_2d_dgsem/elixir_euler_warm_bubble.jl
108+
# However TreeMesh will generate a 20_000 x 20_000 square domain instead
109+
mesh = TreeMesh(coordinates_min, coordinates_max,
110+
initial_refinement_level = 6,
111+
n_cells_max = 100_000,
112+
periodicity = (true, false))
113+
114+
semi = SemidiscretizationHyperbolic(mesh, equations, warm_bubble_setup, solver,
115+
source_terms = warm_bubble_setup,
116+
boundary_conditions = boundary_conditions)
117+
118+
###############################################################################
119+
# ODE solvers, callbacks etc.
120+
121+
tspan = (0.0, 1000.0) # 1000 seconds final time
122+
123+
ode = semidiscretize(semi, tspan)
124+
125+
summary_callback = SummaryCallback()
126+
127+
analysis_interval = 1000
128+
129+
analysis_callback = AnalysisCallback(semi, interval = analysis_interval,
130+
extra_analysis_errors = (:entropy_conservation_error,))
131+
132+
alive_callback = AliveCallback(analysis_interval = analysis_interval)
133+
134+
save_solution = SaveSolutionCallback(interval = analysis_interval,
135+
save_initial_solution = true,
136+
save_final_solution = true,
137+
output_directory = "out_bubble",
138+
solution_variables = cons2prim)
139+
140+
@inline function Tpot(u, equations::CompressibleEulerEquations2D)
141+
rho, _, _, p = cons2prim(u, equations)
142+
exner = (p / 100_000)^(1-inv(warm_bubble_setup.gamma))
143+
T = p / rho / (warm_bubble_setup.c_p - warm_bubble_setup.c_v)
144+
return T / exner
145+
end
146+
amr_indicator = IndicatorLöhner(semi, variable = Tpot)
147+
amr_controller = ControllerThreeLevel(semi, amr_indicator,
148+
base_level = 4,
149+
med_level = 6, med_threshold = 0.0002,
150+
max_level = 8, max_threshold = 0.0005)
151+
amr_callback = AMRCallback(semi, amr_controller,
152+
interval = 50,
153+
adapt_initial_condition = true,
154+
adapt_initial_condition_only_refine = true)
155+
156+
stepsize_callback = StepsizeCallback(cfl = 1.0)
157+
158+
callbacks = CallbackSet(summary_callback,
159+
analysis_callback,
160+
alive_callback,
161+
save_solution,
162+
amr_callback,
163+
stepsize_callback)
164+
165+
###############################################################################
166+
# create OrdinaryDiffEq's time integrator
167+
integrator = init(ode,
168+
CarpenterKennedy2N54(williamson_condition=false),
169+
dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
170+
save_everystep=false,
171+
callback=callbacks);
172+
173+
###############################################################################
174+
# Create simulation state
175+
simstate = SimulationState(semi, integrator)
176+
177+
return simstate
178+
end

0 commit comments

Comments
 (0)