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 dry air warm bubble test case #1779

Merged
merged 36 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ed0e8dc
add LMARS flux in 2D
benegee Dec 18, 2023
77f0d46
add dry air warm bubble test case
benegee Dec 18, 2023
66db843
Merge branch 'main' into add-warm-bubble-test-case
benegee Dec 18, 2023
c8b5360
get formatting right
benegee Dec 18, 2023
786ccd9
remove +=
benegee Dec 19, 2023
62512dc
add DOI
benegee Dec 19, 2023
8043aaf
cleaned up variables (naming, scope)
benegee Dec 19, 2023
b6e527b
reduce run time of test case
benegee Dec 19, 2023
7cfe952
Revert "reduce run time of test case"
benegee Dec 21, 2023
03db8f1
change output folder
benegee Dec 21, 2023
78f22c3
change energy term in LMARS solver to use p_l/r
benegee Dec 21, 2023
700dd30
add lmars consistency checks
benegee Dec 21, 2023
e25f2dc
switched to kennedy gruber flux
benegee Dec 21, 2023
1151789
add euler warm bubble elixir to tests
benegee Dec 21, 2023
d91e0f6
adapt errors due to change flux
benegee Dec 21, 2023
b38d57e
add warm bubble test with TreeMesh
benegee Dec 21, 2023
e373c08
fix unit test
benegee Dec 21, 2023
285f09b
fix format
benegee Dec 21, 2023
fc5b8f6
Merge branch 'main' into add-warm-bubble-test-case
benegee Dec 21, 2023
0b4f400
Merge branch 'main' into add-warm-bubble-test-case
benegee Jan 9, 2024
63d66ce
adapt polynomial degree and CFL number
benegee Jan 9, 2024
82d1d76
fix format
benegee Jan 9, 2024
17e11a2
adapt tests due to changed parameters
benegee Jan 9, 2024
0c97c9f
Update src/equations/compressible_euler_2d.jl
benegee Jan 10, 2024
fc4da19
Update src/equations/compressible_euler_2d.jl
benegee Jan 10, 2024
8b8f1e2
Update src/equations/compressible_euler_3d.jl
benegee Jan 10, 2024
854eefd
Update src/equations/compressible_euler_3d.jl
benegee Jan 10, 2024
2d55dc9
correct test result
benegee Jan 10, 2024
54d7de0
use callable struct to hold parameters
benegee Jan 10, 2024
53c5d2b
Merge branch 'add-warm-bubble-test-case' of github.com:trixi-framewor…
benegee Jan 10, 2024
843173b
Update examples/structured_2d_dgsem/elixir_euler_warm_bubble.jl
benegee Jan 10, 2024
623dae3
Update examples/tree_2d_dgsem/elixir_euler_warm_bubble.jl
benegee Jan 10, 2024
bed6975
year of Wicker paper, comment on tspan
benegee Jan 11, 2024
cc43a50
add comment on speed of sound
benegee Jan 11, 2024
2bcaf97
Merge branch 'main' into add-warm-bubble-test-case
ranocha Jan 12, 2024
db447f8
Merge branch 'main' into add-warm-bubble-test-case
benegee Jan 12, 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
146 changes: 146 additions & 0 deletions examples/structured_2d_dgsem/elixir_euler_warm_bubble.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using OrdinaryDiffEq
using Trixi

# Warm bubble test case from
# - Wicker, L. J., and Skamarock, W. C. (1998)
# A time-splitting scheme for the elastic equations incorporating
# second-order Runge–Kutta time differencing
# [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)
# See also
# - Bryan and Fritsch (2002)
# A Benchmark Simulation for Moist Nonhydrostatic Numerical Models
# [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)
# - Carpenter, Droegemeier, Woodward, Hane (1990)
# Application of the Piecewise Parabolic Method (PPM) to
# Meteorological Modeling
# [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)
struct WarmBubbleSetup
# Physical constants
g::Float64 # gravity of earth
c_p::Float64 # heat capacity for constant pressure (dry air)
c_v::Float64 # heat capacity for constant volume (dry air)
gamma::Float64 # heat capacity ratio (dry air)

function WarmBubbleSetup(; g = 9.81, c_p = 1004.0, c_v = 717.0, gamma = c_p / c_v)
new(g, c_p, c_v, gamma)
end
end

# Initial condition
function (setup::WarmBubbleSetup)(x, t, equations::CompressibleEulerEquations2D)
@unpack g, c_p, c_v = setup

# center of perturbation
center_x = 10000.0
center_z = 2000.0
# radius of perturbation
radius = 2000.0
# distance of current x to center of perturbation
r = sqrt((x[1] - center_x)^2 + (x[2] - center_z)^2)

# perturbation in potential temperature
potential_temperature_ref = 300.0
potential_temperature_perturbation = 0.0
if r <= radius
potential_temperature_perturbation = 2 * cospi(0.5 * r / radius)^2
end
potential_temperature = potential_temperature_ref + potential_temperature_perturbation

# Exner pressure, solves hydrostatic equation for x[2]
exner = 1 - g / (c_p * potential_temperature) * x[2]

# pressure
p_0 = 100_000.0 # reference pressure
andrewwinters5000 marked this conversation as resolved.
Show resolved Hide resolved
R = c_p - c_v # gas constant (dry air)
p = p_0 * exner^(c_p / R)

# temperature
T = potential_temperature * exner

# density
rho = p / (R * T)

v1 = 20.0
v2 = 0.0
E = c_v * T + 0.5 * (v1^2 + v2^2)
return SVector(rho, rho * v1, rho * v2, rho * E)
end

# Source terms
@inline function (setup::WarmBubbleSetup)(u, x, t, equations::CompressibleEulerEquations2D)
@unpack g = setup
rho, _, rho_v2, _ = u
return SVector(zero(eltype(u)), zero(eltype(u)), -g * rho, -g * rho_v2)
benegee marked this conversation as resolved.
Show resolved Hide resolved
end

###############################################################################
# semidiscretization of the compressible Euler equations
warm_bubble_setup = WarmBubbleSetup()

equations = CompressibleEulerEquations2D(warm_bubble_setup.gamma)

boundary_conditions = (x_neg = boundary_condition_periodic,
x_pos = boundary_condition_periodic,
y_neg = boundary_condition_slip_wall,
y_pos = boundary_condition_slip_wall)

polydeg = 3
basis = LobattoLegendreBasis(polydeg)

# This is a good estimate for the speed of sound in this example.
# Other values between 300 and 400 should work as well.
surface_flux = FluxLMARS(340.0)
ranocha marked this conversation as resolved.
Show resolved Hide resolved

volume_flux = flux_kennedy_gruber
volume_integral = VolumeIntegralFluxDifferencing(volume_flux)

solver = DGSEM(basis, surface_flux, volume_integral)

coordinates_min = (0.0, 0.0)
coordinates_max = (20_000.0, 10_000.0)

cells_per_dimension = (64, 32)
mesh = StructuredMesh(cells_per_dimension, coordinates_min, coordinates_max)

semi = SemidiscretizationHyperbolic(mesh, equations, warm_bubble_setup, solver,
source_terms = warm_bubble_setup,
boundary_conditions = boundary_conditions)

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

tspan = (0.0, 1000.0) # 1000 seconds final time

ode = semidiscretize(semi, tspan)

summary_callback = SummaryCallback()

analysis_interval = 1000

analysis_callback = AnalysisCallback(semi, interval = analysis_interval,
extra_analysis_errors = (:entropy_conservation_error,))

alive_callback = AliveCallback(analysis_interval = analysis_interval)

save_solution = SaveSolutionCallback(interval = analysis_interval,
save_initial_solution = true,
save_final_solution = true,
output_directory = "out",
solution_variables = cons2prim)

stepsize_callback = StepsizeCallback(cfl = 1.0)

callbacks = CallbackSet(summary_callback,
analysis_callback,
alive_callback,
save_solution,
stepsize_callback)

###############################################################################
# run the simulation
sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
maxiters = 1.0e7,
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep = false, callback = callbacks);

summary_callback()
150 changes: 150 additions & 0 deletions examples/tree_2d_dgsem/elixir_euler_warm_bubble.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
using OrdinaryDiffEq
using Trixi

benegee marked this conversation as resolved.
Show resolved Hide resolved
# Warm bubble test case from
# - Wicker, L. J., and Skamarock, W. C. (1998)
# A time-splitting scheme for the elastic equations incorporating
# second-order Runge–Kutta time differencing
# [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)
# See also
# - Bryan and Fritsch (2002)
# A Benchmark Simulation for Moist Nonhydrostatic Numerical Models
# [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)
# - Carpenter, Droegemeier, Woodward, Hane (1990)
# Application of the Piecewise Parabolic Method (PPM) to
# Meteorological Modeling
# [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)
struct WarmBubbleSetup
# Physical constants
g::Float64 # gravity of earth
c_p::Float64 # heat capacity for constant pressure (dry air)
c_v::Float64 # heat capacity for constant volume (dry air)
gamma::Float64 # heat capacity ratio (dry air)

function WarmBubbleSetup(; g = 9.81, c_p = 1004.0, c_v = 717.0, gamma = c_p / c_v)
new(g, c_p, c_v, gamma)
end
end

# Initial condition
function (setup::WarmBubbleSetup)(x, t, equations::CompressibleEulerEquations2D)
@unpack g, c_p, c_v = setup

# center of perturbation
center_x = 10000.0
center_z = 2000.0
# radius of perturbation
radius = 2000.0
# distance of current x to center of perturbation
r = sqrt((x[1] - center_x)^2 + (x[2] - center_z)^2)

# perturbation in potential temperature
potential_temperature_ref = 300.0
potential_temperature_perturbation = 0.0
if r <= radius
potential_temperature_perturbation = 2 * cospi(0.5 * r / radius)^2
end
potential_temperature = potential_temperature_ref + potential_temperature_perturbation

# Exner pressure, solves hydrostatic equation for x[2]
exner = 1 - g / (c_p * potential_temperature) * x[2]

# pressure
p_0 = 100_000.0 # reference pressure
R = c_p - c_v # gas constant (dry air)
p = p_0 * exner^(c_p / R)

# temperature
T = potential_temperature * exner

# density
rho = p / (R * T)

v1 = 20.0
v2 = 0.0
E = c_v * T + 0.5 * (v1^2 + v2^2)
return SVector(rho, rho * v1, rho * v2, rho * E)
end

# Source terms
@inline function (setup::WarmBubbleSetup)(u, x, t, equations::CompressibleEulerEquations2D)
@unpack g = setup
rho, _, rho_v2, _ = u
return SVector(zero(eltype(u)), zero(eltype(u)), -g * rho, -g * rho_v2)
end

###############################################################################
# semidiscretization of the compressible Euler equations
warm_bubble_setup = WarmBubbleSetup()

equations = CompressibleEulerEquations2D(warm_bubble_setup.gamma)

boundary_conditions = (x_neg = boundary_condition_periodic,
x_pos = boundary_condition_periodic,
y_neg = boundary_condition_slip_wall,
y_pos = boundary_condition_slip_wall)

polydeg = 3
basis = LobattoLegendreBasis(polydeg)

# This is a good estimate for the speed of sound in this example.
# Other values between 300 and 400 should work as well.
surface_flux = FluxLMARS(340.0)
ranocha marked this conversation as resolved.
Show resolved Hide resolved

volume_flux = flux_kennedy_gruber
volume_integral = VolumeIntegralFluxDifferencing(volume_flux)

solver = DGSEM(basis, surface_flux, volume_integral)

coordinates_min = (0.0, 0.0)
coordinates_max = (20_000.0, 10_000.0)

# Same coordinates as in examples/structured_2d_dgsem/elixir_euler_warm_bubble.jl
# However TreeMesh will generate a 20_000 x 20_000 square domain instead
mesh = TreeMesh(coordinates_min, coordinates_max,
initial_refinement_level = 6,
n_cells_max = 10_000,
periodicity = (true, false))

semi = SemidiscretizationHyperbolic(mesh, equations, warm_bubble_setup, solver,
source_terms = warm_bubble_setup,
boundary_conditions = boundary_conditions)

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

tspan = (0.0, 1000.0) # 1000 seconds final time

ode = semidiscretize(semi, tspan)

summary_callback = SummaryCallback()

analysis_interval = 1000

analysis_callback = AnalysisCallback(semi, interval = analysis_interval,
extra_analysis_errors = (:entropy_conservation_error,))

alive_callback = AliveCallback(analysis_interval = analysis_interval)

save_solution = SaveSolutionCallback(interval = analysis_interval,
save_initial_solution = true,
save_final_solution = true,
output_directory = "out",
solution_variables = cons2prim)

stepsize_callback = StepsizeCallback(cfl = 1.0)

callbacks = CallbackSet(summary_callback,
analysis_callback,
alive_callback,
save_solution,
stepsize_callback)

###############################################################################
# run the simulation
sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
maxiters = 1.0e7,
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep = false, callback = callbacks);

summary_callback()
92 changes: 92 additions & 0 deletions src/equations/compressible_euler_2d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,98 @@ end
return SVector(f1m, f2m, f3m, f4m)
end

"""
FluxLMARS(c)(u_ll, u_rr, orientation_or_normal_direction,
equations::CompressibleEulerEquations2D)

Low Mach number approximate Riemann solver (LMARS) for atmospheric flows using
an estimate `c` of the speed of sound.

References:
- Xi Chen et al. (2013)
A Control-Volume Model of the Compressible Euler Equations with a Vertical
Lagrangian Coordinate
[DOI: 10.1175/MWR-D-12-00129.1](https://doi.org/10.1175/mwr-d-12-00129.1)
"""
struct FluxLMARS{SpeedOfSound}
# Estimate for the speed of sound
speed_of_sound::SpeedOfSound
end

@inline function (flux_lmars::FluxLMARS)(u_ll, u_rr, orientation::Integer,
equations::CompressibleEulerEquations2D)
c = flux_lmars.speed_of_sound

# Unpack left and right state
rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations)
rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations)

if orientation == 1
v_ll = v1_ll
v_rr = v1_rr
else # orientation == 2
v_ll = v2_ll
v_rr = v2_rr
end

rho = 0.5 * (rho_ll + rho_rr)
p = 0.5 * (p_ll + p_rr) - 0.5 * c * rho * (v_rr - v_ll)
v = 0.5 * (v_ll + v_rr) - 1 / (2 * c * rho) * (p_rr - p_ll)

# We treat the energy term analogous to the potential temperature term in the paper by
# Chen et al., i.e. we use p_ll and p_rr, and not p
if v >= 0
f1, f2, f3, f4 = v * u_ll
f4 = f4 + p_ll * v
else
f1, f2, f3, f4 = v * u_rr
f4 = f4 + p_rr * v
end

if orientation == 1
f2 = f2 + p
else # orientation == 2
f3 = f3 + p
end

return SVector(f1, f2, f3, f4)
end

@inline function (flux_lmars::FluxLMARS)(u_ll, u_rr, normal_direction::AbstractVector,
equations::CompressibleEulerEquations2D)
c = flux_lmars.speed_of_sound

# Unpack left and right state
rho_ll, v1_ll, v2_ll, p_ll = cons2prim(u_ll, equations)
rho_rr, v1_rr, v2_rr, p_rr = cons2prim(u_rr, equations)

v_ll = v1_ll * normal_direction[1] + v2_ll * normal_direction[2]
v_rr = v1_rr * normal_direction[1] + v2_rr * normal_direction[2]

# Note that this is the same as computing v_ll and v_rr with a normalized normal vector
# and then multiplying v by `norm_` again, but this version is slightly faster.
norm_ = norm(normal_direction)

rho = 0.5 * (rho_ll + rho_rr)
p = 0.5 * (p_ll + p_rr) - 0.5 * c * rho * (v_rr - v_ll) / norm_
v = 0.5 * (v_ll + v_rr) - 1 / (2 * c * rho) * (p_rr - p_ll) * norm_

# We treat the energy term analogous to the potential temperature term in the paper by
# Chen et al., i.e. we use p_ll and p_rr, and not p
if v >= 0
f1, f2, f3, f4 = u_ll * v
f4 = f4 + p_ll * v
else
f1, f2, f3, f4 = u_rr * v
f4 = f4 + p_rr * v
end

return SVector(f1,
f2 + p * normal_direction[1],
f3 + p * normal_direction[2],
f4)
end

"""
splitting_vanleer_haenel(u, orientation::Integer,
equations::CompressibleEulerEquations2D)
Expand Down
Loading
Loading