Skip to content

Commit 64a57cb

Browse files
Add bottom topography and Coriolis terms to 3D Cartesian SWE, and incorporate additional test cases (#53)
* Added non-conservative terms to Cartesian SWE 3D * Added well-balancing test and corrected reference results for EC tests (already tested that EC works) * Added DissipationLaxFriedrichsEntropyVariables for the 3D Cartesian SWE * Added hack to enable the use of the weak form kernel for the spherical advection tests * Moved non-conservative fluxes below conservative fluxes * Renamed gravity_constant to gravity in elixir * Correected docstrings * Modify Cartesian well-balanced test to match with covariant test (Coriolis not yet implemented) * Added Coriolis source terms * Added geostrophic test for 3F SW equations * Updated reference values for standard DGSEM discretization of 3D SWE * Added isolated mountain test * Updated EC and ES tests to use the reference initial conditions of the code (and match the covariant tests) * Renamed elixirs for consistency * Add rotation rate to well-balanced elixir * Removed 'standard DGSEM' test (it was not adding anything) * Improve docstring * Minor improvements to docstring --------- Co-authored-by: Tristan Montoya <[email protected]>
1 parent bbe4872 commit 64a57cb

13 files changed

+670
-274
lines changed

examples/elixir_shallowwater_cubed_sphere_shell_advection.jl renamed to examples/elixir_shallowwater_cartesian_advection_cubed_sphere.jl

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ using TrixiAtmo
1717
# semidiscretization of the linear advection equation
1818

1919
initial_condition = initial_condition_gaussian
20+
polydeg = 3
2021
cells_per_dimension = (5, 5)
2122

2223
# We use the ShallowWaterEquations3D equations structure but modify the rhs! function to
2324
# convert it to a variable-coefficient advection equation
2425
equations = ShallowWaterEquations3D(gravity = 0.0)
2526

2627
# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
27-
solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs)
28+
solver = DGSEM(polydeg = polydeg,
29+
surface_flux = (flux_lax_friedrichs, flux_nonconservative_wintermeyer_etal))
2830

2931
# Source term function to transform the Euler equations into a linear advection equation with variable advection velocity
3032
function source_terms_convert_to_linear_advection(u, du, x, t,
@@ -41,6 +43,21 @@ function source_terms_convert_to_linear_advection(u, du, x, t,
4143
return SVector(0.0, s2, s3, s4, 0.0)
4244
end
4345

46+
# Hack to use the weak form kernel with ShallowWaterEquations3D (a non-conservative equation).
47+
# This works only because we have a constant bottom topography, so the equations are effectively
48+
# conservative. Note that the weak form kernel is NOT equal to the flux differencing kernel
49+
# with central fluxes because of the curved geometry!
50+
@inline function Trixi.weak_form_kernel!(du, u,
51+
element,
52+
mesh::Union{StructuredMesh{2},
53+
UnstructuredMesh2D,
54+
P4estMesh{2}, T8codeMesh{2}},
55+
nonconservative_terms::Trixi.True,
56+
equations::Trixi.AbstractEquations{3},
57+
dg::DGSEM, cache, alpha = true)
58+
Trixi.weak_form_kernel!(du, u, element, mesh, Trixi.False(), equations, dg, cache)
59+
end
60+
4461
# Create a 2D cubed sphere mesh the size of the Earth
4562
mesh = P4estMeshCubedSphere2D(cells_per_dimension[1], EARTH_RADIUS, polydeg = 3,
4663
#initial_refinement_level = 0, # Comment to use cells_per_dimension in the convergence test

examples/elixir_shallowwater_quad_icosahedron_shell_advection.jl renamed to examples/elixir_shallowwater_cartesian_advection_quad_icosahedron.jl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ cells_per_dimension = (2, 2)
2424
equations = ShallowWaterEquations3D(gravity = 0.0)
2525

2626
# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
27-
solver = DGSEM(polydeg = polydeg, surface_flux = flux_lax_friedrichs)
27+
solver = DGSEM(polydeg = polydeg,
28+
surface_flux = (flux_lax_friedrichs, flux_nonconservative_wintermeyer_etal))
2829

2930
# Source term function to transform the Euler equations into a linear advection equation with variable advection velocity
3031
function source_terms_convert_to_linear_advection(u, du, x, t,
@@ -41,6 +42,21 @@ function source_terms_convert_to_linear_advection(u, du, x, t,
4142
return SVector(0.0, s2, s3, s4, 0.0)
4243
end
4344

45+
# Hack to use the weak form kernel with ShallowWaterEquations3D (a non-conservative equation).
46+
# This works only because we have a constant bottom topography, so the equations are effectively
47+
# conservative. Note that the weak form kernel is NOT equal to the flux differencing kernel
48+
# with central fluxes because of the curved geometry!
49+
@inline function Trixi.weak_form_kernel!(du, u,
50+
element,
51+
mesh::Union{StructuredMesh{2},
52+
UnstructuredMesh2D,
53+
P4estMesh{2}, T8codeMesh{2}},
54+
nonconservative_terms::Trixi.True,
55+
equations::Trixi.AbstractEquations{3},
56+
dg::DGSEM, cache, alpha = true)
57+
Trixi.weak_form_kernel!(du, u, element, mesh, Trixi.False(), equations, dg, cache)
58+
end
59+
4460
# Create a 2D quad-based icosahedral mesh the size of the Earth
4561
mesh = P4estMeshQuadIcosahedron2D(cells_per_dimension[1], EARTH_RADIUS,
4662
#initial_refinement_level = 0,
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
###############################################################################
2+
# Entropy-stable DGSEM for the 3D shallow water equations in Cartesian form on
3+
# the cubed sphere: Steady geostrophic balance (Case 2, Williamson et al., 1992)
4+
###############################################################################
5+
6+
using OrdinaryDiffEq, Trixi, TrixiAtmo
7+
8+
###############################################################################
9+
# Parameters
10+
11+
initial_condition = initial_condition_geostrophic_balance
12+
polydeg = 3
13+
cells_per_dimension = (5, 5)
14+
n_saves = 10
15+
tspan = (0.0, 5.0 * SECONDS_PER_DAY)
16+
17+
###############################################################################
18+
# Spatial discretization
19+
20+
mesh = P4estMeshCubedSphere2D(cells_per_dimension[1], EARTH_RADIUS, polydeg = polydeg,
21+
element_local_mapping = true)
22+
23+
equations = ShallowWaterEquations3D(gravity = EARTH_GRAVITATIONAL_ACCELERATION,
24+
rotation_rate = EARTH_ROTATION_RATE)
25+
26+
# Create DG solver with polynomial degree = polydeg
27+
volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
28+
surface_flux = (FluxPlusDissipation(flux_wintermeyer_etal, DissipationLocalLaxFriedrichs()),
29+
flux_nonconservative_wintermeyer_etal)
30+
31+
solver = DGSEM(polydeg = polydeg,
32+
surface_flux = surface_flux,
33+
volume_integral = VolumeIntegralFluxDifferencing(volume_flux))
34+
35+
# Transform the initial condition to the proper set of conservative variables
36+
initial_condition_transformed = transform_initial_condition(initial_condition, equations)
37+
38+
# A semidiscretization collects data structures and functions for the spatial discretization
39+
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_transformed, solver,
40+
source_terms = source_terms_coriolis_lagrange_multiplier)
41+
42+
###############################################################################
43+
# ODE solvers, callbacks etc.
44+
45+
# Create ODE problem with time span from 0 to T
46+
ode = semidiscretize(semi, tspan)
47+
48+
# Clean the initial condition
49+
for element in eachelement(solver, semi.cache)
50+
for j in eachnode(solver), i in eachnode(solver)
51+
u0 = Trixi.wrap_array(ode.u0, semi)
52+
53+
contravariant_normal_vector = Trixi.get_contravariant_vector(3,
54+
semi.cache.elements.contravariant_vectors,
55+
i, j, element)
56+
clean_solution_lagrange_multiplier!(u0[:, i, j, element], equations,
57+
contravariant_normal_vector)
58+
end
59+
end
60+
61+
# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation
62+
# setup and resets the timers
63+
summary_callback = SummaryCallback()
64+
65+
# The AnalysisCallback allows to analyse the solution in regular intervals and prints the
66+
# results
67+
analysis_callback = AnalysisCallback(semi, interval = 200,
68+
save_analysis = true,
69+
extra_analysis_errors = (:conservation_error,))
70+
71+
# The SaveSolutionCallback allows to save the solution to a file in regular intervals
72+
save_solution = SaveSolutionCallback(dt = (tspan[2] - tspan[1]) / n_saves,
73+
solution_variables = cons2cons)
74+
75+
# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step
76+
stepsize_callback = StepsizeCallback(cfl = 0.4)
77+
78+
# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE
79+
# solver
80+
callbacks = CallbackSet(summary_callback, analysis_callback, save_solution,
81+
stepsize_callback)
82+
83+
###############################################################################
84+
# run the simulation
85+
86+
# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed
87+
# callbacks
88+
sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
89+
dt = 100.0, save_everystep = false, callback = callbacks)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
###############################################################################
2+
# Entropy-stable DGSEM for the 3D shallow water equations in Cartesian form on
3+
# the cubed sphere: Zonal flow over an isolated mountain (Case 5, Williamson et
4+
# al., 1992)
5+
###############################################################################
6+
7+
using OrdinaryDiffEq, Trixi, TrixiAtmo
8+
9+
###############################################################################
10+
# Parameters
11+
12+
initial_condition = initial_condition_isolated_mountain
13+
polydeg = 5
14+
cells_per_dimension = (30, 30)
15+
n_saves = 10
16+
tspan = (0.0, 15.0 * SECONDS_PER_DAY)
17+
18+
###############################################################################
19+
# Spatial discretization
20+
21+
mesh = P4estMeshCubedSphere2D(cells_per_dimension[1], EARTH_RADIUS, polydeg = polydeg,
22+
element_local_mapping = true)
23+
24+
equations = ShallowWaterEquations3D(gravity = EARTH_GRAVITATIONAL_ACCELERATION,
25+
rotation_rate = EARTH_ROTATION_RATE)
26+
27+
# Use entropy-conservative two-point flux for volume terms, dissipative surface flux with
28+
# simplification for continuous bottom topography
29+
volume_flux = (flux_wintermeyer_etal, flux_nonconservative_wintermeyer_etal)
30+
surface_flux = (FluxPlusDissipation(flux_wintermeyer_etal,
31+
DissipationLaxFriedrichsEntropyVariables()),
32+
flux_nonconservative_wintermeyer_etal)
33+
34+
# Create DG solver with polynomial degree = polydeg
35+
solver = DGSEM(polydeg = polydeg, surface_flux = surface_flux,
36+
volume_integral = VolumeIntegralFluxDifferencing(volume_flux))
37+
38+
# Transform the initial condition to the proper set of conservative variables
39+
initial_condition_transformed = transform_initial_condition(initial_condition, equations)
40+
41+
# A semidiscretization collects data structures and functions for the spatial
42+
# discretization. Here, we pass in the additional keyword argument "auxiliary_field" to
43+
# specify the bottom topography.
44+
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_transformed, solver,
45+
source_terms = source_terms_coriolis_lagrange_multiplier)
46+
47+
###############################################################################
48+
# ODE solvers, callbacks etc.
49+
50+
# Create ODE problem with time span from 0 to T
51+
ode = semidiscretize(semi, tspan)
52+
53+
# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation
54+
# setup and resets the timers
55+
summary_callback = SummaryCallback()
56+
57+
# The AnalysisCallback allows to analyse the solution in regular intervals and prints the
58+
# results. Note that entropy should be conserved at the semi-discrete level.
59+
analysis_callback = AnalysisCallback(semi, interval = 200,
60+
save_analysis = true,
61+
extra_analysis_errors = (:conservation_error,),
62+
extra_analysis_integrals = (entropy,),
63+
analysis_polydeg = polydeg)
64+
65+
# The SaveSolutionCallback allows to save the solution to a file in regular intervals
66+
save_solution = SaveSolutionCallback(dt = (tspan[2] - tspan[1]) / n_saves,
67+
solution_variables = cons2prim_and_vorticity)
68+
69+
# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step
70+
stepsize_callback = StepsizeCallback(cfl = 0.4)
71+
72+
# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE
73+
# solver
74+
callbacks = CallbackSet(summary_callback, analysis_callback, save_solution,
75+
stepsize_callback)
76+
77+
###############################################################################
78+
# run the simulation
79+
80+
# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed
81+
# callbacks
82+
sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
83+
dt = 100.0, save_everystep = false, callback = callbacks)

examples/elixir_shallowwater_cubed_sphere_shell_EC_correction.jl renamed to examples/elixir_shallowwater_cartesian_unsteady_solid_body_rotation_EC_correction.jl

Lines changed: 27 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -6,55 +6,28 @@ using TrixiAtmo
66
# Entropy conservation for the spherical shallow water equations in Cartesian
77
# form obtained through an entropy correction term
88

9-
equations = ShallowWaterEquations3D(gravity = 9.81)
9+
###############################################################################
10+
# Parameters
1011

11-
# Create DG solver with polynomial degree = 3 and Wintemeyer et al.'s flux as surface flux
12+
initial_condition = initial_condition_unsteady_solid_body_rotation
1213
polydeg = 3
13-
volume_flux = flux_fjordholm_etal #flux_wintermeyer_etal
14-
surface_flux = flux_fjordholm_etal #flux_wintermeyer_etal #flux_lax_friedrichs
14+
cells_per_dimension = (16, 16)
15+
n_saves = 10
16+
tspan = (0.0, 15.0 * SECONDS_PER_DAY)
17+
18+
###############################################################################
19+
# Spatial discretization
20+
equations = ShallowWaterEquations3D(gravity = EARTH_GRAVITATIONAL_ACCELERATION,
21+
rotation_rate = EARTH_ROTATION_RATE)
22+
23+
# Create DG solver with polynomial degree = 3 and Fjordholm et al.'s flux as surface flux
24+
volume_flux = (flux_fjordholm_etal, flux_nonconservative_fjordholm_etal)
25+
surface_flux = (flux_fjordholm_etal, flux_nonconservative_fjordholm_etal)
26+
1527
solver = DGSEM(polydeg = polydeg,
1628
surface_flux = surface_flux,
1729
volume_integral = VolumeIntegralFluxDifferencing(volume_flux))
1830

19-
# Initial condition for a Gaussian density profile with constant pressure
20-
# and the velocity of a rotating solid body
21-
function initial_condition_advection_sphere(x, t, equations::ShallowWaterEquations3D)
22-
# Gaussian density
23-
rho = 1.0 + exp(-20 * (x[1]^2 + x[3]^2))
24-
25-
# Spherical coordinates for the point x
26-
if sign(x[2]) == 0.0
27-
signy = 1.0
28-
else
29-
signy = sign(x[2])
30-
end
31-
# Co-latitude
32-
colat = acos(x[3] / sqrt(x[1]^2 + x[2]^2 + x[3]^2))
33-
# Latitude (auxiliary variable)
34-
lat = -colat + 0.5 * pi
35-
# Longitude
36-
r_xy = sqrt(x[1]^2 + x[2]^2)
37-
if r_xy == 0.0
38-
phi = pi / 2
39-
else
40-
phi = signy * acos(x[1] / r_xy)
41-
end
42-
43-
# Compute the velocity of a rotating solid body
44-
# (alpha is the angle between the rotation axis and the polar axis of the spherical coordinate system)
45-
v0 = 1.0 # Velocity at the "equator"
46-
alpha = 0.0 #pi / 4
47-
v_long = v0 * (cos(lat) * cos(alpha) + sin(lat) * cos(phi) * sin(alpha))
48-
vlat = -v0 * sin(phi) * sin(alpha)
49-
50-
# Transform to Cartesian coordinate system
51-
v1 = -cos(colat) * cos(phi) * vlat - sin(phi) * v_long
52-
v2 = -cos(colat) * sin(phi) * vlat + cos(phi) * v_long
53-
v3 = sin(colat) * vlat
54-
55-
return prim2cons(SVector(rho, v1, v2, v3, 0), equations)
56-
end
57-
5831
# Source term function to apply the entropy correction term and the Lagrange multiplier to the semi-discretization.
5932
# The vector contravariant_normal_vector contains the normal contravariant vectors scaled with the inverse Jacobian.
6033
# The Lagrange multiplier is applied with the vector x, which contains the exact normal direction to the 2D manifold.
@@ -69,27 +42,28 @@ function source_terms_entropy_correction(u, du, x, t,
6942

7043
new_du = SVector(du[1], du[2] + s2, du[3] + s3, du[4] + s4, du[5])
7144

72-
# Apply Lagrange multipliers using the exact normal vector to the 2D manifold (x)
73-
s = source_terms_lagrange_multiplier(u, new_du, x, t, equations, x)
45+
# Apply Coriolis and Lagrange multipliers source terms
46+
# using the exact normal vector to the 2D manifold (x)
47+
s = source_terms_coriolis_lagrange_multiplier(u, new_du, x, t, equations, x)
7448

7549
return SVector(s[1], s[2] + s2, s[3] + s3, s[4] + s4, s[5])
7650
end
7751

78-
initial_condition = initial_condition_advection_sphere
52+
# Transform the initial condition to the proper set of conservative variables
53+
initial_condition_transformed = transform_initial_condition(initial_condition, equations)
7954

80-
mesh = P4estMeshCubedSphere2D(5, 2.0, polydeg = polydeg,
55+
mesh = P4estMeshCubedSphere2D(cells_per_dimension[1], EARTH_RADIUS, polydeg = polydeg,
8156
initial_refinement_level = 0)
8257

8358
# A semidiscretization collects data structures and functions for the spatial discretization
84-
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
59+
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition_transformed, solver,
8560
metric_terms = MetricTermsInvariantCurl(),
8661
source_terms = source_terms_entropy_correction)
8762

8863
###############################################################################
8964
# ODE solvers, callbacks etc.
9065

91-
# Create ODE problem with time span from 0.0 to π
92-
tspan = (0.0, pi)
66+
# Create ODE problem
9367
ode = semidiscretize(semi, tspan)
9468

9569
# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
@@ -100,10 +74,11 @@ summary_callback = SummaryCallback()
10074
analysis_callback = AnalysisCallback(semi, interval = 10,
10175
save_analysis = true,
10276
extra_analysis_errors = (:conservation_error,),
103-
extra_analysis_integrals = (waterheight, energy_total))
77+
extra_analysis_integrals = (waterheight, energy_total),
78+
analysis_polydeg = polydeg)
10479

10580
# The SaveSolutionCallback allows to save the solution to a file in regular intervals
106-
save_solution = SaveSolutionCallback(interval = 10,
81+
save_solution = SaveSolutionCallback(dt = (tspan[2] - tspan[1]) / n_saves,
10782
solution_variables = cons2prim)
10883

10984
# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step

0 commit comments

Comments
 (0)