Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 3D Cartesian Shallow Water Equations #36

Merged
merged 20 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
368b91e
Added curl invariant metrics
amrueda Sep 16, 2024
56a04d0
Fixed specialization of create_cache for semidiscretization_hyperbolic
amrueda Sep 17, 2024
3f78c43
Specialized the computation of the time-step size for 3D equations in…
amrueda Sep 17, 2024
6307055
Updated test results and formatted
amrueda Sep 17, 2024
d39bb66
Added 3D Cartesian Shallow Water equations and entropy-consistent dis…
amrueda Sep 17, 2024
a6c5ec9
Fixed the computation of the metric terms for 2D manifolds in 3D with…
amrueda Sep 17, 2024
e16e866
Added test for 3D Cartesian Shallow Water Equations
amrueda Sep 17, 2024
16b8034
Use Fjordholm flux in EC through correction test to improve coverage
amrueda Sep 17, 2024
a6cd055
Changes to fix the Documenter action
amrueda Sep 17, 2024
111fa45
Fixed equation renderung for documenter and removed max_abs_speed_nai…
amrueda Sep 17, 2024
b13d5c9
Merge branch 'main' into arr/spherical_shallow_water_es
amrueda Nov 4, 2024
37ef0bf
Merge branch 'main' into arr/spherical_shallow_water_es
amrueda Nov 6, 2024
ea64024
Changed name of MetricTermsCurlInvariant to MetricTermsInvariantCurl …
amrueda Nov 6, 2024
23e46bd
Updated test reference results (due to new time-step size routine)
amrueda Nov 6, 2024
8a9ef3d
Moved the Lagrange multiplier functions to shallow_water_3d.jl and im…
amrueda Nov 7, 2024
9ef87f7
Removed the custom rhs routine from the Cartesian spherical shell eli…
amrueda Nov 7, 2024
58aa42b
Apply suggestions from code review
amrueda Nov 8, 2024
9bb4970
Adapted elixirs to new variables that are exported by TrixiAtmo
amrueda Nov 8, 2024
be72d3e
Merge branch 'main' into arr/spherical_shallow_water_es
amrueda Nov 8, 2024
7a42b0f
Format
amrueda Nov 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
168 changes: 168 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,168 @@

using OrdinaryDiffEq
using Trixi

###############################################################################
# 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 Lagrange multiplier to the semi-discretization
# The vector contravariant_normal_vector contains the normal contravariant vectors scaled with the inverse Jacobian
function source_terms_lagrange_multiplier(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

# Lagrange multipliers in the x direction
x_dot_div_f = (x[1] * (du[2] + s2) + x[2] * (du[3] + s3) + x[3] * (du[4] + s4)) /
sum(x .^ 2)
s2 += -x[1] * x_dot_div_f
s3 += -x[2] * x_dot_div_f
s4 += -x[3] * x_dot_div_f

return SVector(0.0, s2, s3, s4, 0.0)
end

# Custom RHS that applies a source term that depends on du (used to convert apply Lagrange multiplier)
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, contravariant_vectors, inverse_jacobian = 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)
contravariant_normal_vector = Trixi.get_contravariant_vector(3,
contravariant_vectors,
i, j,
element) *
inverse_jacobian[i, j, element]

source = source_terms_lagrange_multiplier(u_local, du_local,
x_local, t, equations,
contravariant_normal_vector)

Trixi.add_to_node_vars!(du, source, equations, solver, i, j, element)
end
end
end
end

initial_condition = initial_condition_advection_sphere

mesh = TrixiAtmo.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 = TrixiAtmo.MetricTermsCurlInvariant())

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

# Create ODE problem with time span from 0.0 to π
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()

# 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_semi_custom, 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()
188 changes: 188 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,188 @@

using OrdinaryDiffEq
using Trixi

###############################################################################
# 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

# Source term function to apply Lagrange multiplier to the semi-discretization
# The vector contravariant_normal_vector contains the normal contravariant vectors (no scaling is needed)
function source_terms_lagrange_multiplier(u, du, x, t,
equations::ShallowWaterEquations3D,
contravariant_normal_vector)
x_dot_div_f = (contravariant_normal_vector[1] * du[2] +
contravariant_normal_vector[2] * du[3] +
contravariant_normal_vector[3] * du[4]) /
sum(contravariant_normal_vector .^ 2)

s2 = -contravariant_normal_vector[1] * x_dot_div_f
s3 = -contravariant_normal_vector[2] * x_dot_div_f
s4 = -contravariant_normal_vector[3] * x_dot_div_f

return SVector(0.0, s2, s3, s4, 0.0)
end

# Function to apply Lagrange multiplier discretely to the solution
# The vector contravariant_normal_vector contains the normal contravariant vectors (no scaling is needed)
function clean_solution!(u, equations::ShallowWaterEquations3D, contravariant_normal_vector)
x_dot_div_f = (contravariant_normal_vector[1] * u[2] +
contravariant_normal_vector[2] * u[3] +
contravariant_normal_vector[3] * u[4]) /
sum(contravariant_normal_vector .^ 2)

u[2] -= contravariant_normal_vector[1] * x_dot_div_f
u[3] -= contravariant_normal_vector[2] * x_dot_div_f
u[4] -= contravariant_normal_vector[3] * x_dot_div_f
end

# Custom RHS that applies a source term that depends on du (used to convert apply Lagrange multiplier)
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, contravariant_vectors = 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)
contravariant_normal_vector = Trixi.get_contravariant_vector(3,
contravariant_vectors,
i, j, element)

source = source_terms_lagrange_multiplier(u_local, du_local,
x_local, t, equations,
contravariant_normal_vector)

Trixi.add_to_node_vars!(du, source, equations, solver, i, j, element)
end
end
end
end

initial_condition = initial_condition_advection_sphere

mesh = TrixiAtmo.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 = TrixiAtmo.MetricTermsCurlInvariant())

###############################################################################
# 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!(u0[:, i, j, element], equations, contravariant_normal_vector)
end
end

# 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()

# 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_semi_custom, 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