Skip to content

Commit

Permalink
Add 3D Cartesian Shallow Water Equations (#36)
Browse files Browse the repository at this point in the history
Co-authored-by: Tristan Montoya <montoya.tristan@gmail.com>
  • Loading branch information
amrueda and tristanmontoya authored Nov 8, 2024
1 parent 30bdb23 commit 49ec206
Show file tree
Hide file tree
Showing 18 changed files with 1,218 additions and 68 deletions.
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ version = "0.1.0-DEV"
[deps]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221"
LoopVectorization = "bdcacae8-1622-11e9-2a5c-532679323890"
NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Static = "aedffcd0-7271-4cad-89d0-dc628f76c6d3"
Expand All @@ -17,6 +18,7 @@ Trixi = "a7f1ee26-1774-49b1-8366-f1abc58fbfcb"
[compat]
LinearAlgebra = "1"
MuladdMacro = "0.2.2"
LoopVectorization = "0.12.118"
NLsolve = "4.5.1"
Reexport = "1.0"
Static = "0.8, 1"
Expand Down
49 changes: 8 additions & 41 deletions examples/elixir_euler_spherical_advection_cartesian.jl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ end

# Source term function to transform the Euler equations into the linear advection equations with variable advection velocity
function source_terms_convert_to_linear_advection(u, du, x, t,
equations::CompressibleEulerEquations3D)
equations::CompressibleEulerEquations3D,
normal_direction)
v1 = u[2] / u[1]
v2 = u[3] / u[1]
v3 = u[4] / u[1]
Expand All @@ -70,45 +71,17 @@ function source_terms_convert_to_linear_advection(u, du, x, t,
return SVector(0.0, s2, s3, s4, s5)
end

# Custom RHS that applies a source term that depends on du (used to convert the 3D Euler equations into the 3D linear advection
# equations with position-dependent advection speed)
function rhs_semi_custom!(du_ode, u_ode, semi, t)
# Compute standard Trixi RHS
Trixi.rhs!(du_ode, u_ode, semi, t)

# Now apply the custom source term
Trixi.@trixi_timeit Trixi.timer() "custom source term" begin
@unpack solver, equations, cache = semi
@unpack node_coordinates = cache.elements

# Wrap the solution and RHS
u = Trixi.wrap_array(u_ode, semi)
du = Trixi.wrap_array(du_ode, semi)

Trixi.@threaded for element in eachelement(solver, cache)
for j in eachnode(solver), i in eachnode(solver)
u_local = Trixi.get_node_vars(u, equations, solver, i, j, element)
du_local = Trixi.get_node_vars(du, equations, solver, i, j, element)
x_local = Trixi.get_node_coords(node_coordinates, equations, solver,
i, j, element)
source = source_terms_convert_to_linear_advection(u_local, du_local,
x_local, t, equations)
Trixi.add_to_node_vars!(du, source, equations, solver, i, j, element)
end
end
end
end

initial_condition = initial_condition_advection_sphere

element_local_mapping = false

mesh = TrixiAtmo.P4estMeshCubedSphere2D(5, 1.0, polydeg = polydeg,
initial_refinement_level = 0,
element_local_mapping = element_local_mapping)
mesh = P4estMeshCubedSphere2D(5, 1.0, polydeg = polydeg,
initial_refinement_level = 0,
element_local_mapping = element_local_mapping)

# A semidiscretization collects data structures and functions for the spatial discretization
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
source_terms = source_terms_convert_to_linear_advection)

###############################################################################
# ODE solvers, callbacks etc.
Expand All @@ -117,12 +90,6 @@ semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)
tspan = (0.0, pi)
ode = semidiscretize(semi, tspan)

# Create custom discretization that runs with the custom RHS
ode_semi_custom = ODEProblem(rhs_semi_custom!,
ode.u0,
ode.tspan,
semi)

# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
# and resets the timers
summary_callback = SummaryCallback()
Expand All @@ -148,7 +115,7 @@ callbacks = CallbackSet(summary_callback, analysis_callback, save_solution,
# run the simulation

# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
sol = solve(ode_semi_custom, CarpenterKennedy2N54(williamson_condition = false),
sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep = false, callback = callbacks);

Expand Down
125 changes: 125 additions & 0 deletions examples/elixir_shallowwater_cubed_sphere_shell_EC_correction.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@

using OrdinaryDiffEq
using Trixi
using TrixiAtmo
###############################################################################
# Entropy conservation for the spherical shallow water equations in Cartesian
# form obtained through an entropy correction term

equations = ShallowWaterEquations3D(gravity_constant = 9.81)

# Create DG solver with polynomial degree = 3 and Wintemeyer et al.'s flux as surface flux
polydeg = 3
volume_flux = flux_fjordholm_etal #flux_wintermeyer_etal
surface_flux = flux_fjordholm_etal #flux_wintermeyer_etal #flux_lax_friedrichs
solver = DGSEM(polydeg = polydeg,
surface_flux = surface_flux,
volume_integral = VolumeIntegralFluxDifferencing(volume_flux))

# Initial condition for a Gaussian density profile with constant pressure
# and the velocity of a rotating solid body
function initial_condition_advection_sphere(x, t, equations::ShallowWaterEquations3D)
# Gaussian density
rho = 1.0 + exp(-20 * (x[1]^2 + x[3]^2))

# Spherical coordinates for the point x
if sign(x[2]) == 0.0
signy = 1.0
else
signy = sign(x[2])
end
# Co-latitude
colat = acos(x[3] / sqrt(x[1]^2 + x[2]^2 + x[3]^2))
# Latitude (auxiliary variable)
lat = -colat + 0.5 * pi
# Longitude
r_xy = sqrt(x[1]^2 + x[2]^2)
if r_xy == 0.0
phi = pi / 2
else
phi = signy * acos(x[1] / r_xy)
end

# Compute the velocity of a rotating solid body
# (alpha is the angle between the rotation axis and the polar axis of the spherical coordinate system)
v0 = 1.0 # Velocity at the "equator"
alpha = 0.0 #pi / 4
v_long = v0 * (cos(lat) * cos(alpha) + sin(lat) * cos(phi) * sin(alpha))
v_lat = -v0 * sin(phi) * sin(alpha)

# Transform to Cartesian coordinate system
v1 = -cos(colat) * cos(phi) * v_lat - sin(phi) * v_long
v2 = -cos(colat) * sin(phi) * v_lat + cos(phi) * v_long
v3 = sin(colat) * v_lat

return prim2cons(SVector(rho, v1, v2, v3, 0), equations)
end

# Source term function to apply the entropy correction term and the Lagrange multiplier to the semi-discretization.
# The vector contravariant_normal_vector contains the normal contravariant vectors scaled with the inverse Jacobian.
# The Lagrange multiplier is applied with the vector x, which contains the exact normal direction to the 2D manifold.
function source_terms_entropy_correction(u, du, x, t,
equations::ShallowWaterEquations3D,
contravariant_normal_vector)

# Entropy correction term
s2 = -contravariant_normal_vector[1] * equations.gravity * u[1]^2
s3 = -contravariant_normal_vector[2] * equations.gravity * u[1]^2
s4 = -contravariant_normal_vector[3] * equations.gravity * u[1]^2

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

# Apply Lagrange multipliers using the exact normal vector to the 2D manifold (x)
s = source_terms_lagrange_multiplier(u, new_du, x, t, equations, x)

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

initial_condition = initial_condition_advection_sphere

mesh = P4estMeshCubedSphere2D(5, 2.0, polydeg = polydeg,
initial_refinement_level = 0)

# A semidiscretization collects data structures and functions for the spatial discretization
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
metric_terms = MetricTermsInvariantCurl(),
source_terms = source_terms_entropy_correction)

###############################################################################
# ODE solvers, callbacks etc.

# Create ODE problem with time span from 0.0 to π
tspan = (0.0, pi)
ode = semidiscretize(semi, tspan)

# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
# and resets the timers
summary_callback = SummaryCallback()

# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results
analysis_callback = AnalysisCallback(semi, interval = 10,
save_analysis = true,
extra_analysis_errors = (:conservation_error,),
extra_analysis_integrals = (waterheight, energy_total))

# The SaveSolutionCallback allows to save the solution to a file in regular intervals
save_solution = SaveSolutionCallback(interval = 10,
solution_variables = cons2prim)

# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step
stepsize_callback = StepsizeCallback(cfl = 0.7)

# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
callbacks = CallbackSet(summary_callback, analysis_callback, save_solution,
stepsize_callback)

###############################################################################
# run the simulation

# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep = false, callback = callbacks);

# Print the timer summary
summary_callback()
120 changes: 120 additions & 0 deletions examples/elixir_shallowwater_cubed_sphere_shell_EC_projection.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@

using OrdinaryDiffEq
using Trixi
using TrixiAtmo

###############################################################################
# Entropy conservation for the spherical shallow water equations in Cartesian
# form obtained through the projection of the momentum onto the divergence-free
# tangential contravariant vectors

equations = ShallowWaterEquations3D(gravity_constant = 9.81)

# Create DG solver with polynomial degree = 3 and Wintemeyer et al.'s flux as surface flux
polydeg = 3
volume_flux = flux_wintermeyer_etal # flux_fjordholm_etal
surface_flux = flux_wintermeyer_etal # flux_fjordholm_etal #flux_lax_friedrichs
solver = DGSEM(polydeg = polydeg,
surface_flux = surface_flux,
volume_integral = VolumeIntegralFluxDifferencing(volume_flux))

# Initial condition for a Gaussian density profile with constant pressure
# and the velocity of a rotating solid body
function initial_condition_advection_sphere(x, t, equations::ShallowWaterEquations3D)
# Gaussian density
rho = 1.0 + exp(-20 * (x[1]^2 + x[3]^2))

# Spherical coordinates for the point x
if sign(x[2]) == 0.0
signy = 1.0
else
signy = sign(x[2])
end
# Co-latitude
colat = acos(x[3] / sqrt(x[1]^2 + x[2]^2 + x[3]^2))
# Latitude (auxiliary variable)
lat = -colat + 0.5 * pi
# Longitude
r_xy = sqrt(x[1]^2 + x[2]^2)
if r_xy == 0.0
phi = pi / 2
else
phi = signy * acos(x[1] / r_xy)
end

# Compute the velocity of a rotating solid body
# (alpha is the angle between the rotation axis and the polar axis of the spherical coordinate system)
v0 = 1.0 # Velocity at the "equator"
alpha = 0.0 #pi / 4
v_long = v0 * (cos(lat) * cos(alpha) + sin(lat) * cos(phi) * sin(alpha))
v_lat = -v0 * sin(phi) * sin(alpha)

# Transform to Cartesian coordinate system
v1 = -cos(colat) * cos(phi) * v_lat - sin(phi) * v_long
v2 = -cos(colat) * sin(phi) * v_lat + cos(phi) * v_long
v3 = sin(colat) * v_lat

return prim2cons(SVector(rho, v1, v2, v3, 0), equations)
end

initial_condition = initial_condition_advection_sphere

mesh = P4estMeshCubedSphere2D(5, 2.0, polydeg = polydeg,
initial_refinement_level = 0)

# A semidiscretization collects data structures and functions for the spatial discretization
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
metric_terms = MetricTermsInvariantCurl(),
source_terms = source_terms_lagrange_multiplier)

###############################################################################
# ODE solvers, callbacks etc.

# Create ODE problem with time span from 0.0 to π
tspan = (0.0, pi)
ode = semidiscretize(semi, tspan)

# Clean the initial condition
for element in eachelement(solver, semi.cache)
for j in eachnode(solver), i in eachnode(solver)
u0 = Trixi.wrap_array(ode.u0, semi)

contravariant_normal_vector = Trixi.get_contravariant_vector(3,
semi.cache.elements.contravariant_vectors,
i, j, element)
clean_solution_lagrange_multiplier!(u0[:, i, j, element], equations,
contravariant_normal_vector)
end
end

# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
# and resets the timers
summary_callback = SummaryCallback()

# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results
analysis_callback = AnalysisCallback(semi, interval = 10,
save_analysis = true,
extra_analysis_errors = (:conservation_error,),
extra_analysis_integrals = (waterheight, energy_total))

# The SaveSolutionCallback allows to save the solution to a file in regular intervals
save_solution = SaveSolutionCallback(interval = 10,
solution_variables = cons2prim)

# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step
stepsize_callback = StepsizeCallback(cfl = 0.7)

# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
callbacks = CallbackSet(summary_callback, analysis_callback, save_solution,
stepsize_callback)

###############################################################################
# run the simulation

# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep = false, callback = callbacks);

# Print the timer summary
summary_callback()
Loading

0 comments on commit 49ec206

Please sign in to comment.