Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/examples/dgmulti_2d/elixir_navierstokes_convergence_curved.jl
2055 views
1
using OrdinaryDiffEqLowStorageRK
2
using Trixi
3
4
###############################################################################
5
# semidiscretization of the ideal compressible Navier-Stokes equations
6
7
prandtl_number() = 0.72
8
mu() = 0.01
9
10
equations = CompressibleEulerEquations2D(1.4)
11
# Note: If you change the Navier-Stokes parameters here, also change them in the initial condition
12
# I really do not like this structure but it should work for now
13
equations_parabolic = CompressibleNavierStokesDiffusion2D(equations, mu = mu(),
14
Prandtl = prandtl_number(),
15
gradient_variables = GradientVariablesPrimitive())
16
17
# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
18
19
# Up to version 0.13.0, `max_abs_speed_naive` was used as the default wave speed estimate of
20
# `const flux_lax_friedrichs = FluxLaxFriedrichs(), i.e., `FluxLaxFriedrichs(max_abs_speed = max_abs_speed_naive)`.
21
# In the `StepsizeCallback`, though, the less diffusive `max_abs_speeds` is employed which is consistent with `max_abs_speed`.
22
# Thus, we exchanged in PR#2458 the default wave speed used in the LLF flux to `max_abs_speed`.
23
# To ensure that every example still runs we specify explicitly `FluxLaxFriedrichs(max_abs_speed_naive)`.
24
# We remark, however, that the now default `max_abs_speed` is in general recommended due to compliance with the
25
# `StepsizeCallback` (CFL-Condition) and less diffusion.
26
dg = DGMulti(polydeg = 3, element_type = Tri(), approximation_type = Polynomial(),
27
surface_integral = SurfaceIntegralWeakForm(FluxLaxFriedrichs(max_abs_speed_naive)),
28
volume_integral = VolumeIntegralWeakForm())
29
30
top_bottom(x, tol = 50 * eps()) = abs(abs(x[2]) - 1) < tol
31
is_on_boundary = Dict(:top_bottom => top_bottom)
32
33
function mapping(xi, eta)
34
x = xi + 0.1 * sin(pi * xi) * sin(pi * eta)
35
y = eta + 0.1 * sin(pi * xi) * sin(pi * eta)
36
return SVector(x, y)
37
end
38
cells_per_dimension = (16, 16)
39
mesh = DGMultiMesh(dg, cells_per_dimension, mapping; periodicity = (true, false),
40
is_on_boundary)
41
42
# This initial condition is taken from `examples/dgmulti_2d/elixir_navierstokes_convergence.jl`
43
44
# Note: the initial condition cannot be specialized to `CompressibleNavierStokesDiffusion2D`
45
# since it is called by both the parabolic solver (which passes in `CompressibleNavierStokesDiffusion2D`)
46
# and by the initial condition (which passes in `CompressibleEulerEquations2D`).
47
# This convergence test setup was originally derived by Andrew Winters (@andrewwinters5000)
48
function initial_condition_navier_stokes_convergence_test(x, t, equations)
49
# Amplitude and shift
50
A = 0.5
51
c = 2.0
52
53
# convenience values for trig. functions
54
pi_x = pi * x[1]
55
pi_y = pi * x[2]
56
pi_t = pi * t
57
58
rho = c + A * sin(pi_x) * cos(pi_y) * cos(pi_t)
59
v1 = sin(pi_x) * log(x[2] + 2.0) * (1.0 - exp(-A * (x[2] - 1.0))) * cos(pi_t)
60
v2 = v1
61
p = rho^2
62
63
return prim2cons(SVector(rho, v1, v2, p), equations)
64
end
65
66
@inline function source_terms_navier_stokes_convergence_test(u, x, t, equations)
67
y = x[2]
68
69
# TODO: parabolic
70
# we currently need to hardcode these parameters until we fix the "combined equation" issue
71
# see also https://github.com/trixi-framework/Trixi.jl/pull/1160
72
inv_gamma_minus_one = inv(equations.gamma - 1)
73
Pr = prandtl_number()
74
mu_ = mu()
75
76
# Same settings as in `initial_condition`
77
# Amplitude and shift
78
A = 0.5
79
c = 2.0
80
81
# convenience values for trig. functions
82
pi_x = pi * x[1]
83
pi_y = pi * x[2]
84
pi_t = pi * t
85
86
# compute the manufactured solution and all necessary derivatives
87
rho = c + A * sin(pi_x) * cos(pi_y) * cos(pi_t)
88
rho_t = -pi * A * sin(pi_x) * cos(pi_y) * sin(pi_t)
89
rho_x = pi * A * cos(pi_x) * cos(pi_y) * cos(pi_t)
90
rho_y = -pi * A * sin(pi_x) * sin(pi_y) * cos(pi_t)
91
rho_xx = -pi * pi * A * sin(pi_x) * cos(pi_y) * cos(pi_t)
92
rho_yy = -pi * pi * A * sin(pi_x) * cos(pi_y) * cos(pi_t)
93
94
v1 = sin(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * cos(pi_t)
95
v1_t = -pi * sin(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * sin(pi_t)
96
v1_x = pi * cos(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * cos(pi_t)
97
v1_y = sin(pi_x) *
98
(A * log(y + 2.0) * exp(-A * (y - 1.0)) +
99
(1.0 - exp(-A * (y - 1.0))) / (y + 2.0)) * cos(pi_t)
100
v1_xx = -pi * pi * sin(pi_x) * log(y + 2.0) * (1.0 - exp(-A * (y - 1.0))) * cos(pi_t)
101
v1_xy = pi * cos(pi_x) *
102
(A * log(y + 2.0) * exp(-A * (y - 1.0)) +
103
(1.0 - exp(-A * (y - 1.0))) / (y + 2.0)) * cos(pi_t)
104
v1_yy = (sin(pi_x) *
105
(2.0 * A * exp(-A * (y - 1.0)) / (y + 2.0) -
106
A * A * log(y + 2.0) * exp(-A * (y - 1.0)) -
107
(1.0 - exp(-A * (y - 1.0))) / ((y + 2.0) * (y + 2.0))) * cos(pi_t))
108
v2 = v1
109
v2_t = v1_t
110
v2_x = v1_x
111
v2_y = v1_y
112
v2_xx = v1_xx
113
v2_xy = v1_xy
114
v2_yy = v1_yy
115
116
p = rho * rho
117
p_t = 2.0 * rho * rho_t
118
p_x = 2.0 * rho * rho_x
119
p_y = 2.0 * rho * rho_y
120
p_xx = 2.0 * rho * rho_xx + 2.0 * rho_x * rho_x
121
p_yy = 2.0 * rho * rho_yy + 2.0 * rho_y * rho_y
122
123
# Note this simplifies slightly because the ansatz assumes that v1 = v2
124
E = p * inv_gamma_minus_one + 0.5 * rho * (v1^2 + v2^2)
125
E_t = p_t * inv_gamma_minus_one + rho_t * v1^2 + 2.0 * rho * v1 * v1_t
126
E_x = p_x * inv_gamma_minus_one + rho_x * v1^2 + 2.0 * rho * v1 * v1_x
127
E_y = p_y * inv_gamma_minus_one + rho_y * v1^2 + 2.0 * rho * v1 * v1_y
128
129
# Some convenience constants
130
T_const = equations.gamma * inv_gamma_minus_one / Pr
131
inv_rho_cubed = 1.0 / (rho^3)
132
133
# compute the source terms
134
# density equation
135
du1 = rho_t + rho_x * v1 + rho * v1_x + rho_y * v2 + rho * v2_y
136
137
# x-momentum equation
138
du2 = (rho_t * v1 + rho * v1_t + p_x + rho_x * v1^2
139
+ 2.0 * rho * v1 * v1_x
140
+ rho_y * v1 * v2
141
+ rho * v1_y * v2
142
+ rho * v1 * v2_y -
143
# stress tensor from x-direction
144
4.0 / 3.0 * v1_xx * mu_ +
145
2.0 / 3.0 * v2_xy * mu_ -
146
v1_yy * mu_ -
147
v2_xy * mu_)
148
# y-momentum equation
149
du3 = (rho_t * v2 + rho * v2_t + p_y + rho_x * v1 * v2
150
+ rho * v1_x * v2
151
+ rho * v1 * v2_x
152
+ rho_y * v2^2
153
+ 2.0 * rho * v2 * v2_y -
154
# stress tensor from y-direction
155
v1_xy * mu_ -
156
v2_xx * mu_ -
157
4.0 / 3.0 * v2_yy * mu_ +
158
2.0 / 3.0 * v1_xy * mu_)
159
# total energy equation
160
du4 = (E_t + v1_x * (E + p) + v1 * (E_x + p_x)
161
+ v2_y * (E + p) + v2 * (E_y + p_y) -
162
# stress tensor and temperature gradient terms from x-direction
163
4.0 / 3.0 * v1_xx * v1 * mu_ +
164
2.0 / 3.0 * v2_xy * v1 * mu_ -
165
4.0 / 3.0 * v1_x * v1_x * mu_ +
166
2.0 / 3.0 * v2_y * v1_x * mu_ -
167
v1_xy * v2 * mu_ -
168
v2_xx * v2 * mu_ -
169
v1_y * v2_x * mu_ -
170
v2_x * v2_x * mu_ -
171
T_const * inv_rho_cubed *
172
(p_xx * rho * rho -
173
2.0 * p_x * rho * rho_x +
174
2.0 * p * rho_x * rho_x -
175
p * rho * rho_xx) * mu_ -
176
# stress tensor and temperature gradient terms from y-direction
177
v1_yy * v1 * mu_ -
178
v2_xy * v1 * mu_ -
179
v1_y * v1_y * mu_ -
180
v2_x * v1_y * mu_ -
181
4.0 / 3.0 * v2_yy * v2 * mu_ +
182
2.0 / 3.0 * v1_xy * v2 * mu_ -
183
4.0 / 3.0 * v2_y * v2_y * mu_ +
184
2.0 / 3.0 * v1_x * v2_y * mu_ -
185
T_const * inv_rho_cubed *
186
(p_yy * rho * rho -
187
2.0 * p_y * rho * rho_y +
188
2.0 * p * rho_y * rho_y -
189
p * rho * rho_yy) * mu_)
190
191
return SVector(du1, du2, du3, du4)
192
end
193
194
initial_condition = initial_condition_navier_stokes_convergence_test
195
196
# BC types
197
velocity_bc_top_bottom = NoSlip() do x, t, equations_parabolic
198
u_cons = initial_condition_navier_stokes_convergence_test(x, t, equations_parabolic)
199
return SVector(u_cons[2] / u_cons[1], u_cons[3] / u_cons[1])
200
end
201
202
heat_bc_top_bottom = Adiabatic((x, t, equations_parabolic) -> 0.0)
203
boundary_condition_top_bottom = BoundaryConditionNavierStokesWall(velocity_bc_top_bottom,
204
heat_bc_top_bottom)
205
206
# define inviscid boundary conditions
207
boundary_conditions = (; :top_bottom => boundary_condition_slip_wall)
208
209
# define viscous boundary conditions
210
boundary_conditions_parabolic = (; :top_bottom => boundary_condition_top_bottom)
211
212
semi = SemidiscretizationHyperbolicParabolic(mesh, (equations, equations_parabolic),
213
initial_condition, dg;
214
boundary_conditions = (boundary_conditions,
215
boundary_conditions_parabolic),
216
source_terms = source_terms_navier_stokes_convergence_test)
217
218
###############################################################################
219
# ODE solvers, callbacks etc.
220
221
# Create ODE problem with time span `tspan`
222
tspan = (0.0, 0.5)
223
ode = semidiscretize(semi, tspan)
224
225
summary_callback = SummaryCallback()
226
alive_callback = AliveCallback(alive_interval = 10)
227
analysis_interval = 100
228
analysis_callback = AnalysisCallback(semi, interval = analysis_interval, uEltype = real(dg))
229
save_solution = SaveSolutionCallback(interval = analysis_interval,
230
solution_variables = cons2prim)
231
callbacks = CallbackSet(summary_callback, alive_callback, analysis_callback, save_solution)
232
233
###############################################################################
234
# run the simulation
235
236
time_int_tol = 1e-8
237
sol = solve(ode, RDPK3SpFSAL49(); abstol = time_int_tol, reltol = time_int_tol,
238
ode_default_options()..., callback = callbacks)
239
240