Skip to content

Commit bde0afc

Browse files
jmarkJohannes MarkertbenegeeJoshuaLampertranocha
authored
Feature: t8code cubed spherical shell setups (trixi-framework#1918)
* Wrap from_abaqus routines. * Implement geometry data transfer from t8code to Trixi. * Updated examples. * Fixed typos. * Applied formatter. * cubed sphere test case, copied from p4est * add baroclinic instability (copy of p4est) * add cubed sphere constructor * Fix indentation. * Fix indentation. * Switching off formatter in two files. * Upgrading T8code.jl. * Fixed examples. * stress different meaning of first argument it refers to level of refinement in lat lon direction, not number of tree as in the p4est version * use lat lon levels * add t8code cubed sphere tests * Remove TODO comments * Relaxing T8code.jl version requirement. * Restricted t8code version requirement. * Removed cubed spherical shell related code. * Removed cubed spherical shell tests. * Including cubed spherical shell setup. * Modified GH ci such that it uses the latest T8code.jl package. * Included cubed spherical shell setups and tests. * Switching order of CI steps. * Changed line in CI. * Increasing code coverage. * Increasing code coverage. * Further increasing code coverage. * Re-introduced tests. * Removed workaround in github workflow. * Applied formatter. * adapt to merged t8code PR * missed calling constructor * Backup. * Add save callback to elixir. * Backup. * Refined code. Make it work in parallel. * Added support for parallelt8codemesh save solution callback. * Applied formatter. * Updated examples and tests. * Applied formatter. * Minor adjustments. * Code refinement. Enabled partitioning after mesh loading. * Applied formatter and fixed typos. * Removed commented out section. * Added missing union type member. * Switching from UInt64 to UInt128 in global interface/mortar id computation. * Switching from UInt64 to UInt128 in global interface/mortar id computation (II). * Adding more tests. * Applied formatter. * Removed Printf. * Incorporated review comments and code polish. * Applied formatter. * Fixed typos. * Update examples/t8code_2d_dgsem/elixir_advection_restart.jl Co-authored-by: Joshua Lampert <[email protected]> * Update examples/t8code_2d_dgsem/elixir_advection_restart_amr.jl Co-authored-by: Joshua Lampert <[email protected]> * Update examples/t8code_3d_dgsem/elixir_advection_restart.jl Co-authored-by: Joshua Lampert <[email protected]> * Update src/meshes/mesh_io.jl Co-authored-by: Joshua Lampert <[email protected]> * Update examples/t8code_3d_dgsem/elixir_advection_restart.jl Co-authored-by: Joshua Lampert <[email protected]> * Update src/meshes/t8code_mesh.jl Co-authored-by: Joshua Lampert <[email protected]> * Update src/meshes/t8code_mesh.jl Co-authored-by: Joshua Lampert <[email protected]> * Removing last test in t8code 2D MPI to investigate problems in Github CI. * Refactored a bit. * Applied formatter. * Removed commented code. * Added LOG_LEVEL variable. * Added t8code interface simplfication and stitched memory leak. * Applied formatter. * Simplifying finailze behavior for T8codeMesh. * Addeing finalize call to T8codeMesh examples. * Applied formatter. * Update test/test_t8code_3d.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update test/test_t8code_3d.jl Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * re-add test * remove duplicate * Fixing minor stuff. * bump t8code * Update test/test_t8code_3d.jl Co-authored-by: Joshua Lampert <[email protected]> --------- Co-authored-by: Johannes Markert <[email protected]> Co-authored-by: Benedict Geihe <[email protected]> Co-authored-by: Benedict <[email protected]> Co-authored-by: Joshua Lampert <[email protected]> Co-authored-by: Hendrik Ranocha <[email protected]> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 1f31fd4 commit bde0afc

File tree

6 files changed

+457
-4
lines changed

6 files changed

+457
-4
lines changed

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ StaticArrays = "1.5"
108108
StrideArrays = "0.1.26"
109109
StructArrays = "0.6.11"
110110
SummationByPartsOperators = "0.5.41"
111-
T8code = "0.7.2"
111+
T8code = "0.7.4"
112112
TimerOutputs = "0.5.7"
113113
Triangulate = "2.2"
114114
TriplotBase = "0.1"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using OrdinaryDiffEq
2+
using Trixi
3+
4+
###############################################################################
5+
# semidiscretization of the linear advection equation
6+
7+
advection_velocity = (0.2, -0.7, 0.5)
8+
equations = LinearScalarAdvectionEquation3D(advection_velocity)
9+
10+
# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
11+
solver = DGSEM(polydeg = 3, surface_flux = flux_lax_friedrichs)
12+
13+
initial_condition = initial_condition_convergence_test
14+
15+
boundary_condition = BoundaryConditionDirichlet(initial_condition)
16+
boundary_conditions = Dict(:inside => boundary_condition,
17+
:outside => boundary_condition)
18+
19+
# Note that the first argument refers to the level of refinement, unlike in for p4est
20+
mesh = Trixi.T8codeMeshCubedSphere(5, 3, 0.5, 0.5;
21+
polydeg = 3, initial_refinement_level = 0)
22+
23+
# A semidiscretization collects data structures and functions for the spatial discretization
24+
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
25+
boundary_conditions = boundary_conditions)
26+
27+
###############################################################################
28+
# ODE solvers, callbacks etc.
29+
30+
# Create ODE problem with time span from 0.0 to 1.0
31+
tspan = (0.0, 1.0)
32+
ode = semidiscretize(semi, tspan)
33+
34+
# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
35+
# and resets the timers
36+
summary_callback = SummaryCallback()
37+
38+
# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results
39+
analysis_callback = AnalysisCallback(semi, interval = 100)
40+
41+
# The SaveSolutionCallback allows to save the solution to a file in regular intervals
42+
save_solution = SaveSolutionCallback(interval = 100,
43+
solution_variables = cons2prim)
44+
45+
# The StepsizeCallback handles the re-calculation of the maximum Δt after each time step
46+
stepsize_callback = StepsizeCallback(cfl = 1.2)
47+
48+
# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
49+
callbacks = CallbackSet(summary_callback, analysis_callback, save_solution,
50+
stepsize_callback)
51+
52+
###############################################################################
53+
# run the simulation
54+
55+
# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
56+
sol = solve(ode, CarpenterKennedy2N54(williamson_condition = false),
57+
dt = 1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
58+
save_everystep = false, callback = callbacks);
59+
60+
# Print the timer summary
61+
summary_callback()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
# An idealized baroclinic instability test case
2+
# For optimal results consider increasing the resolution to 16x16x8 trees per cube face.
3+
#
4+
# Note that this elixir can take several hours to run.
5+
# Using 24 threads of an AMD Ryzen Threadripper 3990X (more threads don't speed it up further)
6+
# and `check-bounds=no`, this elixirs takes about one hour to run.
7+
# With 16x16x8 trees per cube face on the same machine, it takes about 28 hours.
8+
#
9+
# References:
10+
# - Paul A. Ullrich, Thomas Melvin, Christiane Jablonowski, Andrew Staniforth (2013)
11+
# A proposed baroclinic wave test case for deep- and shallow-atmosphere dynamical cores
12+
# https://doi.org/10.1002/qj.2241
13+
14+
using OrdinaryDiffEq
15+
using Trixi
16+
using LinearAlgebra
17+
18+
###############################################################################
19+
# Setup for the baroclinic instability test
20+
gamma = 1.4
21+
equations = CompressibleEulerEquations3D(gamma)
22+
23+
# Initial condition for an idealized baroclinic instability test
24+
# https://doi.org/10.1002/qj.2241, Section 3.2 and Appendix A
25+
function initial_condition_baroclinic_instability(x, t,
26+
equations::CompressibleEulerEquations3D)
27+
lon, lat, r = cartesian_to_sphere(x)
28+
radius_earth = 6.371229e6
29+
# Make sure that the r is not smaller than radius_earth
30+
z = max(r - radius_earth, 0.0)
31+
32+
# Unperturbed basic state
33+
rho, u, p = basic_state_baroclinic_instability_longitudinal_velocity(lon, lat, z)
34+
35+
# Stream function type perturbation
36+
u_perturbation, v_perturbation = perturbation_stream_function(lon, lat, z)
37+
38+
u += u_perturbation
39+
v = v_perturbation
40+
41+
# Convert spherical velocity to Cartesian
42+
v1 = -sin(lon) * u - sin(lat) * cos(lon) * v
43+
v2 = cos(lon) * u - sin(lat) * sin(lon) * v
44+
v3 = cos(lat) * v
45+
46+
return prim2cons(SVector(rho, v1, v2, v3, p), equations)
47+
end
48+
49+
# Steady state for RHS correction below
50+
function steady_state_baroclinic_instability(x, t, equations::CompressibleEulerEquations3D)
51+
lon, lat, r = cartesian_to_sphere(x)
52+
radius_earth = 6.371229e6
53+
# Make sure that the r is not smaller than radius_earth
54+
z = max(r - radius_earth, 0.0)
55+
56+
# Unperturbed basic state
57+
rho, u, p = basic_state_baroclinic_instability_longitudinal_velocity(lon, lat, z)
58+
59+
# Convert spherical velocity to Cartesian
60+
v1 = -sin(lon) * u
61+
v2 = cos(lon) * u
62+
v3 = 0.0
63+
64+
return prim2cons(SVector(rho, v1, v2, v3, p), equations)
65+
end
66+
67+
function cartesian_to_sphere(x)
68+
r = norm(x)
69+
lambda = atan(x[2], x[1])
70+
if lambda < 0
71+
lambda += 2 * pi
72+
end
73+
phi = asin(x[3] / r)
74+
75+
return lambda, phi, r
76+
end
77+
78+
# Unperturbed balanced steady-state.
79+
# Returns primitive variables with only the velocity in longitudinal direction (rho, u, p).
80+
# The other velocity components are zero.
81+
function basic_state_baroclinic_instability_longitudinal_velocity(lon, lat, z)
82+
# Parameters from Table 1 in the paper
83+
# Corresponding names in the paper are commented
84+
radius_earth = 6.371229e6 # a
85+
half_width_parameter = 2 # b
86+
gravitational_acceleration = 9.80616 # g
87+
k = 3 # k
88+
surface_pressure = 1e5 # p₀
89+
gas_constant = 287 # R
90+
surface_equatorial_temperature = 310.0 # T₀ᴱ
91+
surface_polar_temperature = 240.0 # T₀ᴾ
92+
lapse_rate = 0.005 # Γ
93+
angular_velocity = 7.29212e-5 # Ω
94+
95+
# Distance to the center of the Earth
96+
r = z + radius_earth
97+
98+
# In the paper: T₀
99+
temperature0 = 0.5 * (surface_equatorial_temperature + surface_polar_temperature)
100+
# In the paper: A, B, C, H
101+
const_a = 1 / lapse_rate
102+
const_b = (temperature0 - surface_polar_temperature) /
103+
(temperature0 * surface_polar_temperature)
104+
const_c = 0.5 * (k + 2) * (surface_equatorial_temperature - surface_polar_temperature) /
105+
(surface_equatorial_temperature * surface_polar_temperature)
106+
const_h = gas_constant * temperature0 / gravitational_acceleration
107+
108+
# In the paper: (r - a) / bH
109+
scaled_z = z / (half_width_parameter * const_h)
110+
111+
# Temporary variables
112+
temp1 = exp(lapse_rate / temperature0 * z)
113+
temp2 = exp(-scaled_z^2)
114+
115+
# In the paper: ̃τ₁, ̃τ₂
116+
tau1 = const_a * lapse_rate / temperature0 * temp1 +
117+
const_b * (1 - 2 * scaled_z^2) * temp2
118+
tau2 = const_c * (1 - 2 * scaled_z^2) * temp2
119+
120+
# In the paper: ∫τ₁(r') dr', ∫τ₂(r') dr'
121+
inttau1 = const_a * (temp1 - 1) + const_b * z * temp2
122+
inttau2 = const_c * z * temp2
123+
124+
# Temporary variables
125+
temp3 = r / radius_earth * cos(lat)
126+
temp4 = temp3^k - k / (k + 2) * temp3^(k + 2)
127+
128+
# In the paper: T
129+
temperature = 1 / ((r / radius_earth)^2 * (tau1 - tau2 * temp4))
130+
131+
# In the paper: U, u (zonal wind, first component of spherical velocity)
132+
big_u = gravitational_acceleration / radius_earth * k * temperature * inttau2 *
133+
(temp3^(k - 1) - temp3^(k + 1))
134+
temp5 = radius_earth * cos(lat)
135+
u = -angular_velocity * temp5 + sqrt(angular_velocity^2 * temp5^2 + temp5 * big_u)
136+
137+
# Hydrostatic pressure
138+
p = surface_pressure *
139+
exp(-gravitational_acceleration / gas_constant * (inttau1 - inttau2 * temp4))
140+
141+
# Density (via ideal gas law)
142+
rho = p / (gas_constant * temperature)
143+
144+
return rho, u, p
145+
end
146+
147+
# Perturbation as in Equations 25 and 26 of the paper (analytical derivative)
148+
function perturbation_stream_function(lon, lat, z)
149+
# Parameters from Table 1 in the paper
150+
# Corresponding names in the paper are commented
151+
perturbation_radius = 1 / 6 # d₀ / a
152+
perturbed_wind_amplitude = 1.0 # Vₚ
153+
perturbation_lon = pi / 9 # Longitude of perturbation location
154+
perturbation_lat = 2 * pi / 9 # Latitude of perturbation location
155+
pertz = 15000 # Perturbation height cap
156+
157+
# Great circle distance (d in the paper) divided by a (radius of the Earth)
158+
# because we never actually need d without dividing by a
159+
great_circle_distance_by_a = acos(sin(perturbation_lat) * sin(lat) +
160+
cos(perturbation_lat) * cos(lat) *
161+
cos(lon - perturbation_lon))
162+
163+
# In the first case, the vertical taper function is per definition zero.
164+
# In the second case, the stream function is per definition zero.
165+
if z > pertz || great_circle_distance_by_a > perturbation_radius
166+
return 0.0, 0.0
167+
end
168+
169+
# Vertical tapering of stream function
170+
perttaper = 1.0 - 3 * z^2 / pertz^2 + 2 * z^3 / pertz^3
171+
172+
# sin/cos(pi * d / (2 * d_0)) in the paper
173+
sin_, cos_ = sincos(0.5 * pi * great_circle_distance_by_a / perturbation_radius)
174+
175+
# Common factor for both u and v
176+
factor = 16 / (3 * sqrt(3)) * perturbed_wind_amplitude * perttaper * cos_^3 * sin_
177+
178+
u_perturbation = -factor * (-sin(perturbation_lat) * cos(lat) +
179+
cos(perturbation_lat) * sin(lat) * cos(lon - perturbation_lon)) /
180+
sin(great_circle_distance_by_a)
181+
182+
v_perturbation = factor * cos(perturbation_lat) * sin(lon - perturbation_lon) /
183+
sin(great_circle_distance_by_a)
184+
185+
return u_perturbation, v_perturbation
186+
end
187+
188+
@inline function source_terms_baroclinic_instability(u, x, t,
189+
equations::CompressibleEulerEquations3D)
190+
radius_earth = 6.371229e6 # a
191+
gravitational_acceleration = 9.80616 # g
192+
angular_velocity = 7.29212e-5 # Ω
193+
194+
r = norm(x)
195+
# Make sure that r is not smaller than radius_earth
196+
z = max(r - radius_earth, 0.0)
197+
r = z + radius_earth
198+
199+
du1 = zero(eltype(u))
200+
201+
# Gravity term
202+
temp = -gravitational_acceleration * radius_earth^2 / r^3
203+
du2 = temp * u[1] * x[1]
204+
du3 = temp * u[1] * x[2]
205+
du4 = temp * u[1] * x[3]
206+
du5 = temp * (u[2] * x[1] + u[3] * x[2] + u[4] * x[3])
207+
208+
# Coriolis term, -2Ω × ρv = -2 * angular_velocity * (0, 0, 1) × u[2:4]
209+
du2 -= -2 * angular_velocity * u[3]
210+
du3 -= 2 * angular_velocity * u[2]
211+
212+
return SVector(du1, du2, du3, du4, du5)
213+
end
214+
215+
###############################################################################
216+
# Start of the actual elixir, semidiscretization of the problem
217+
218+
initial_condition = initial_condition_baroclinic_instability
219+
220+
boundary_conditions = Dict(:inside => boundary_condition_slip_wall,
221+
:outside => boundary_condition_slip_wall)
222+
223+
# This is a good estimate for the speed of sound in this example.
224+
# Other values between 300 and 400 should work as well.
225+
surface_flux = FluxLMARS(340)
226+
volume_flux = flux_kennedy_gruber
227+
solver = DGSEM(polydeg = 5, surface_flux = surface_flux,
228+
volume_integral = VolumeIntegralFluxDifferencing(volume_flux))
229+
230+
# For optimal results, use (16, 8) here
231+
trees_per_cube_face = (8, 4)
232+
mesh = Trixi.T8codeMeshCubedSphere(trees_per_cube_face..., 6.371229e6, 30000.0,
233+
polydeg = 5, initial_refinement_level = 0)
234+
235+
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
236+
source_terms = source_terms_baroclinic_instability,
237+
boundary_conditions = boundary_conditions)
238+
239+
###############################################################################
240+
# ODE solvers, callbacks etc.
241+
242+
tspan = (0.0, 10 * 24 * 60 * 60.0) # time in seconds for 10 days
243+
244+
# Save RHS of the steady state and subtract it in every RHS evaluation.
245+
# This trick preserves the steady state exactly (to machine rounding errors, of course).
246+
# Otherwise, this elixir produces entirely unusable results for a resolution of 8x8x4 cells
247+
# per cube face with a polydeg of 3.
248+
# With this trick, even the polydeg 3 simulation produces usable (although badly resolved) results,
249+
# and most of the grid imprinting in higher polydeg simulation is eliminated.
250+
#
251+
# See https://github.com/trixi-framework/Trixi.jl/issues/980 for more information.
252+
u_steady_state = compute_coefficients(steady_state_baroclinic_instability, tspan[1], semi)
253+
# Use a `let` block for performance (otherwise du_steady_state will be a global variable)
254+
let du_steady_state = similar(u_steady_state)
255+
# Save RHS of the steady state
256+
Trixi.rhs!(du_steady_state, u_steady_state, semi, tspan[1])
257+
258+
global function corrected_rhs!(du, u, semi, t)
259+
# Normal RHS evaluation
260+
Trixi.rhs!(du, u, semi, t)
261+
# Correct by subtracting the steady-state RHS
262+
Trixi.@trixi_timeit Trixi.timer() "rhs correction" begin
263+
# Use Trixi.@threaded for threaded performance
264+
Trixi.@threaded for i in eachindex(du)
265+
du[i] -= du_steady_state[i]
266+
end
267+
end
268+
end
269+
end
270+
u0 = compute_coefficients(tspan[1], semi)
271+
ode = ODEProblem(corrected_rhs!, u0, tspan, semi)
272+
273+
summary_callback = SummaryCallback()
274+
275+
analysis_interval = 5000
276+
analysis_callback = AnalysisCallback(semi, interval = analysis_interval)
277+
278+
alive_callback = AliveCallback(analysis_interval = analysis_interval)
279+
280+
save_solution = SaveSolutionCallback(interval = 5000,
281+
save_initial_solution = true,
282+
save_final_solution = true,
283+
solution_variables = cons2prim)
284+
285+
callbacks = CallbackSet(summary_callback,
286+
analysis_callback,
287+
alive_callback,
288+
save_solution)
289+
290+
###############################################################################
291+
# run the simulation
292+
293+
# Use a Runge-Kutta method with automatic (error based) time step size control
294+
# Enable threading of the RK method for better performance on multiple threads
295+
sol = solve(ode, RDPK3SpFSAL49(thread = OrdinaryDiffEq.True()); abstol = 1.0e-6,
296+
reltol = 1.0e-6,
297+
ode_default_options()..., callback = callbacks);
298+
299+
summary_callback() # print the timer summary

0 commit comments

Comments
 (0)