Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/equations/compressible_navier_stokes_3d.jl
2055 views
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7
8
@doc raw"""
9
CompressibleNavierStokesDiffusion3D(equations; mu, Pr,
10
gradient_variables=GradientVariablesPrimitive())
11
12
Contains the diffusion (i.e. parabolic) terms applied
13
to mass, momenta, and total energy together with the advective terms from
14
the [`CompressibleEulerEquations3D`](@ref).
15
16
- `equations`: instance of the [`CompressibleEulerEquations3D`](@ref)
17
- `mu`: dynamic viscosity,
18
- `Pr`: Prandtl number,
19
- `gradient_variables`: which variables the gradients are taken with respect to.
20
Defaults to [`GradientVariablesPrimitive()`](@ref).
21
For an entropy stable formulation, use [`GradientVariablesEntropy()`](@ref).
22
23
Fluid properties such as the dynamic viscosity ``\mu`` can be provided in any consistent unit system, e.g.,
24
[``\mu``] = kg m⁻¹ s⁻¹.
25
The viscosity ``\mu`` may be a constant or a function of the current state, e.g.,
26
depending on temperature (Sutherland's law): ``\mu = \mu(T)``.
27
In the latter case, the function `mu` needs to have the signature `mu(u, equations)`.
28
29
The particular form of the compressible Navier-Stokes implemented is
30
```math
31
\frac{\partial}{\partial t}
32
\begin{pmatrix}
33
\rho \\ \rho \mathbf{v} \\ \rho e
34
\end{pmatrix}
35
+
36
\nabla \cdot
37
\begin{pmatrix}
38
\rho \mathbf{v} \\ \rho \mathbf{v}\mathbf{v}^T + p \underline{I} \\ (\rho e + p) \mathbf{v}
39
\end{pmatrix}
40
=
41
\nabla \cdot
42
\begin{pmatrix}
43
0 \\ \underline{\tau} \\ \underline{\tau}\mathbf{v} - \mathbf{q}
44
\end{pmatrix}
45
```
46
where the system is closed with the ideal gas assumption giving
47
```math
48
p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho (v_1^2+v_2^2+v_3^2) \right)
49
```
50
as the pressure. The value of the adiabatic constant `gamma` is taken from the [`CompressibleEulerEquations2D`](@ref).
51
The terms on the right hand side of the system above
52
are built from the viscous stress tensor
53
```math
54
\underline{\tau} = \mu \left(\nabla\mathbf{v} + \left(\nabla\mathbf{v}\right)^T\right) - \frac{2}{3} \mu \left(\nabla\cdot\mathbf{v}\right)\underline{I}
55
```
56
where ``\underline{I}`` is the ``3\times 3`` identity matrix and the heat flux is
57
```math
58
\mathbf{q} = -\kappa\nabla\left(T\right),\quad T = \frac{p}{R\rho}
59
```
60
where ``T`` is the temperature and ``\kappa`` is the thermal conductivity for Fick's law.
61
Under the assumption that the gas has a constant Prandtl number,
62
the thermal conductivity is
63
```math
64
\kappa = \frac{\gamma \mu R}{(\gamma - 1)\textrm{Pr}}.
65
```
66
From this combination of temperature ``T`` and thermal conductivity ``\kappa`` we see
67
that the gas constant `R` cancels and the heat flux becomes
68
```math
69
\mathbf{q} = -\kappa\nabla\left(T\right) = -\frac{\gamma \mu}{(\gamma - 1)\textrm{Pr}}\nabla\left(\frac{p}{\rho}\right)
70
```
71
which is the form implemented below in the [`flux`](@ref) function.
72
73
In two spatial dimensions we require gradients for three quantities, e.g.,
74
primitive quantities
75
```math
76
\nabla v_1,\, \nabla v_2,\, \nabla v_3,\, \nabla T
77
```
78
or the entropy variables
79
```math
80
\nabla w_2,\, \nabla w_3,\, \nabla w_4\, \nabla w_5
81
```
82
where
83
```math
84
w_2 = \frac{\rho v_1}{p},\, w_3 = \frac{\rho v_2}{p},\, w_4 = \frac{\rho v_3}{p},\, w_5 = -\frac{\rho}{p}
85
```
86
"""
87
struct CompressibleNavierStokesDiffusion3D{GradientVariables, RealT <: Real, Mu,
88
E <: AbstractCompressibleEulerEquations{3}} <:
89
AbstractCompressibleNavierStokesDiffusion{3, 5, GradientVariables}
90
# TODO: parabolic
91
# 1) For now save gamma and inv(gamma-1) again, but could potentially reuse them from the Euler equations
92
# 2) Add NGRADS as a type parameter here and in AbstractEquationsParabolic, add `ngradients(...)` accessor function
93
gamma::RealT # ratio of specific heats
94
inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications
95
96
mu::Mu # viscosity
97
Pr::RealT # Prandtl number
98
kappa::RealT # thermal diffusivity for Fick's law
99
100
equations_hyperbolic::E # CompressibleEulerEquations3D
101
gradient_variables::GradientVariables # GradientVariablesPrimitive or GradientVariablesEntropy
102
end
103
104
# default to primitive gradient variables
105
function CompressibleNavierStokesDiffusion3D(equations::CompressibleEulerEquations3D;
106
mu, Prandtl,
107
gradient_variables = GradientVariablesPrimitive())
108
gamma = equations.gamma
109
inv_gamma_minus_one = equations.inv_gamma_minus_one
110
111
# Under the assumption of constant Prandtl number the thermal conductivity
112
# constant is kappa = gamma μ / ((gamma-1) Prandtl).
113
# Important note! Factor of μ is accounted for later in `flux`.
114
# This avoids recomputation of kappa for non-constant μ.
115
kappa = gamma * inv_gamma_minus_one / Prandtl
116
117
CompressibleNavierStokesDiffusion3D{typeof(gradient_variables), typeof(gamma),
118
typeof(mu),
119
typeof(equations)}(gamma, inv_gamma_minus_one,
120
mu, Prandtl, kappa,
121
equations,
122
gradient_variables)
123
end
124
125
# TODO: parabolic
126
# This is the flexibility a user should have to select the different gradient variable types
127
# varnames(::typeof(cons2prim) , ::CompressibleNavierStokesDiffusion3D) = ("v1", "v2", "v3", "T")
128
# varnames(::typeof(cons2entropy), ::CompressibleNavierStokesDiffusion3D) = ("w2", "w3", "w4", "w5")
129
130
function varnames(variable_mapping,
131
equations_parabolic::CompressibleNavierStokesDiffusion3D)
132
varnames(variable_mapping, equations_parabolic.equations_hyperbolic)
133
end
134
135
# we specialize this function to compute gradients of primitive variables instead of
136
# conservative variables.
137
function gradient_variable_transformation(::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
138
cons2prim
139
end
140
function gradient_variable_transformation(::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
141
cons2entropy
142
end
143
144
# Explicit formulas for the diffusive Navier-Stokes fluxes are available, e.g., in Section 2
145
# of the paper by Rueda-Ramírez, Hennemann, Hindenlang, Winters, and Gassner
146
# "An Entropy Stable Nodal Discontinuous Galerkin Method for the resistive
147
# MHD Equations. Part II: Subcell Finite Volume Shock Capturing"
148
# where one sets the magnetic field components equal to 0.
149
function flux(u, gradients, orientation::Integer,
150
equations::CompressibleNavierStokesDiffusion3D)
151
# Here, `u` is assumed to be the "transformed" variables specified by `gradient_variable_transformation`.
152
_, v1, v2, v3, _ = convert_transformed_to_primitive(u, equations)
153
# Here `gradients` is assumed to contain the gradients of the primitive variables (rho, v1, v2, v3, T)
154
# either computed directly or reverse engineered from the gradient of the entropy variables
155
# by way of the `convert_gradient_variables` function.
156
_, dv1dx, dv2dx, dv3dx, dTdx = convert_derivative_to_primitive(u, gradients[1],
157
equations)
158
_, dv1dy, dv2dy, dv3dy, dTdy = convert_derivative_to_primitive(u, gradients[2],
159
equations)
160
_, dv1dz, dv2dz, dv3dz, dTdz = convert_derivative_to_primitive(u, gradients[3],
161
equations)
162
163
# Components of viscous stress tensor
164
165
# Diagonal parts
166
# (4 * (v1)_x / 3 - 2 * ((v2)_y + (v3)_z)) / 3)
167
tau_11 = 4 * dv1dx / 3 - 2 * (dv2dy + dv3dz) / 3
168
# (4 * (v2)_y / 3 - 2 * ((v1)_x + (v3)_z) / 3)
169
tau_22 = 4 * dv2dy / 3 - 2 * (dv1dx + dv3dz) / 3
170
# (4 * (v3)_z / 3 - 2 * ((v1)_x + (v2)_y) / 3)
171
tau_33 = 4 * dv3dz / 3 - 2 * (dv1dx + dv2dy) / 3
172
173
# Off diagonal parts, exploit that stress tensor is symmetric
174
# ((v1)_y + (v2)_x)
175
tau_12 = dv1dy + dv2dx # = tau_21
176
# ((v1)_z + (v3)_x)
177
tau_13 = dv1dz + dv3dx # = tau_31
178
# ((v2)_z + (v3)_y)
179
tau_23 = dv2dz + dv3dy # = tau_32
180
181
# Fick's law q = -kappa * grad(T) = -kappa * grad(p / (R rho))
182
# with thermal diffusivity constant kappa = gamma μ R / ((gamma-1) Pr)
183
# Note, the gas constant cancels under this formulation, so it is not present
184
# in the implementation
185
q1 = equations.kappa * dTdx
186
q2 = equations.kappa * dTdy
187
q3 = equations.kappa * dTdz
188
189
# In the simplest cases, the user passed in `mu` or `mu()`
190
# (which returns just a constant) but
191
# more complex functions like Sutherland's law are possible.
192
# `dynamic_viscosity` is a helper function that handles both cases
193
# by dispatching on the type of `equations.mu`.
194
mu = dynamic_viscosity(u, equations)
195
196
if orientation == 1
197
# viscous flux components in the x-direction
198
f1 = 0
199
f2 = tau_11 * mu
200
f3 = tau_12 * mu
201
f4 = tau_13 * mu
202
f5 = (v1 * tau_11 + v2 * tau_12 + v3 * tau_13 + q1) * mu
203
204
return SVector(f1, f2, f3, f4, f5)
205
elseif orientation == 2
206
# viscous flux components in the y-direction
207
# Note, symmetry is exploited for tau_12 = tau_21
208
g1 = 0
209
g2 = tau_12 * mu # tau_21 * mu
210
g3 = tau_22 * mu
211
g4 = tau_23 * mu
212
g5 = (v1 * tau_12 + v2 * tau_22 + v3 * tau_23 + q2) * mu
213
214
return SVector(g1, g2, g3, g4, g5)
215
else # if orientation == 3
216
# viscous flux components in the z-direction
217
# Note, symmetry is exploited for tau_13 = tau_31, tau_23 = tau_32
218
h1 = 0
219
h2 = tau_13 * mu # tau_31 * mu
220
h3 = tau_23 * mu # tau_32 * mu
221
h4 = tau_33 * mu
222
h5 = (v1 * tau_13 + v2 * tau_23 + v3 * tau_33 + q3) * mu
223
224
return SVector(h1, h2, h3, h4, h5)
225
end
226
end
227
228
# Convert conservative variables to primitive
229
@inline function cons2prim(u, equations::CompressibleNavierStokesDiffusion3D)
230
rho, rho_v1, rho_v2, rho_v3, _ = u
231
232
v1 = rho_v1 / rho
233
v2 = rho_v2 / rho
234
v3 = rho_v3 / rho
235
T = temperature(u, equations)
236
237
return SVector(rho, v1, v2, v3, T)
238
end
239
240
# Convert conservative variables to entropy
241
# TODO: parabolic. We can improve efficiency by not computing w_1, which involves logarithms
242
# This can be done by specializing `cons2entropy` and `entropy2cons` to `CompressibleNavierStokesDiffusion2D`,
243
# but this may be confusing to new users.
244
function cons2entropy(u, equations::CompressibleNavierStokesDiffusion3D)
245
cons2entropy(u, equations.equations_hyperbolic)
246
end
247
function entropy2cons(w, equations::CompressibleNavierStokesDiffusion3D)
248
entropy2cons(w, equations.equations_hyperbolic)
249
end
250
251
# the `flux` function takes in transformed variables `u` which depend on the type of the gradient variables.
252
# For CNS, it is simplest to formulate the viscous terms in primitive variables, so we transform the transformed
253
# variables into primitive variables.
254
@inline function convert_transformed_to_primitive(u_transformed,
255
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
256
return u_transformed
257
end
258
259
# TODO: parabolic. Make this more efficient!
260
@inline function convert_transformed_to_primitive(u_transformed,
261
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
262
# note: this uses CompressibleNavierStokesDiffusion3D versions of cons2prim and entropy2cons
263
return cons2prim(entropy2cons(u_transformed, equations), equations)
264
end
265
266
# Takes the solution values `u` and gradient of the entropy variables (w_2, w_3, w_4, w_5) and
267
# reverse engineers the gradients to be terms of the primitive variables (v1, v2, v3, T).
268
# Helpful because then the diffusive fluxes have the same form as on paper.
269
# Note, the first component of `gradient_entropy_vars` contains gradient(rho) which is unused.
270
# TODO: parabolic; entropy stable viscous terms
271
@inline function convert_derivative_to_primitive(u, gradient,
272
::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
273
return gradient
274
end
275
276
# the first argument is always the "transformed" variables.
277
@inline function convert_derivative_to_primitive(w, gradient_entropy_vars,
278
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
279
280
# TODO: parabolic. This is inefficient to pass in transformed variables but then transform them back.
281
# We can fix this if we directly compute v1, v2, v3, T from the entropy variables
282
u = entropy2cons(w, equations) # calls a "modified" entropy2cons defined for CompressibleNavierStokesDiffusion3D
283
rho, rho_v1, rho_v2, rho_v3, _ = u
284
285
v1 = rho_v1 / rho
286
v2 = rho_v2 / rho
287
v3 = rho_v3 / rho
288
T = temperature(u, equations)
289
290
return SVector(gradient_entropy_vars[1],
291
T * (gradient_entropy_vars[2] + v1 * gradient_entropy_vars[5]), # grad(u) = T*(grad(w_2)+v1*grad(w_5))
292
T * (gradient_entropy_vars[3] + v2 * gradient_entropy_vars[5]), # grad(v) = T*(grad(w_3)+v2*grad(w_5))
293
T * (gradient_entropy_vars[4] + v3 * gradient_entropy_vars[5]), # grad(v) = T*(grad(w_4)+v3*grad(w_5))
294
T * T * gradient_entropy_vars[5])
295
end
296
297
# This routine is required because `prim2cons` is called in `initial_condition`, which
298
# is called with `equations::CompressibleEulerEquations3D`. This means it is inconsistent
299
# with `cons2prim(..., ::CompressibleNavierStokesDiffusion3D)` as defined above.
300
# TODO: parabolic. Is there a way to clean this up?
301
@inline function prim2cons(u, equations::CompressibleNavierStokesDiffusion3D)
302
prim2cons(u, equations.equations_hyperbolic)
303
end
304
305
@inline function temperature(u, equations::CompressibleNavierStokesDiffusion3D)
306
rho, rho_v1, rho_v2, rho_v3, rho_e = u
307
308
p = (equations.gamma - 1) * (rho_e - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho)
309
T = p / rho
310
return T
311
end
312
313
@inline function velocity(u, equations::CompressibleNavierStokesDiffusion3D)
314
rho = u[1]
315
v1 = u[2] / rho
316
v2 = u[3] / rho
317
v3 = u[4] / rho
318
return SVector(v1, v2, v3)
319
end
320
321
@doc raw"""
322
enstrophy(u, gradients, equations::CompressibleNavierStokesDiffusion3D)
323
324
Computes the (node-wise) enstrophy, defined as
325
```math
326
\mathcal{E} = \frac{1}{2} \rho \boldsymbol{\omega} \cdot \boldsymbol{\omega}
327
```
328
where ``\boldsymbol{\omega} = \nabla \times \boldsymbol{v}`` is the [`vorticity`](@ref).
329
In 3D, ``\boldsymbol{\omega}`` is a full three-component vector.
330
"""
331
@inline function enstrophy(u, gradients, equations::CompressibleNavierStokesDiffusion3D)
332
# Enstrophy is 0.5 rho ω⋅ω where ω = ∇ × v
333
334
omega = vorticity(u, gradients, equations)
335
return 0.5f0 * u[1] * (omega[1]^2 + omega[2]^2 + omega[3]^2)
336
end
337
338
@doc raw"""
339
vorticity(u, gradients, equations::CompressibleNavierStokesDiffusion3D)
340
341
Computes the (node-wise) vorticity, defined in 3D as
342
```math
343
\boldsymbol{\omega} = \nabla \times \boldsymbol{v} =
344
\begin{pmatrix}
345
\frac{\partial v_3}{\partial y} - \frac{\partial v_2}{\partial z} \\
346
\frac{\partial v_1}{\partial z} - \frac{\partial v_3}{\partial x} \\
347
\frac{\partial v_2}{\partial x} - \frac{\partial v_1}{\partial y}
348
\end{pmatrix}
349
```
350
"""
351
@inline function vorticity(u, gradients, equations::CompressibleNavierStokesDiffusion3D)
352
# Ensure that we have velocity `gradients` by way of the `convert_gradient_variables` function.
353
_, _, dv2dx, dv3dx, _ = convert_derivative_to_primitive(u, gradients[1], equations)
354
_, dv1dy, _, dv3dy, _ = convert_derivative_to_primitive(u, gradients[2], equations)
355
_, dv1dz, dv2dz, _, _ = convert_derivative_to_primitive(u, gradients[3], equations)
356
357
return SVector(dv3dy - dv2dz, dv1dz - dv3dx, dv2dx - dv1dy)
358
end
359
360
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
361
<:Adiabatic})(flux_inner,
362
u_inner,
363
normal::AbstractVector,
364
x,
365
t,
366
operator_type::Gradient,
367
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
368
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
369
t,
370
equations)
371
return SVector(u_inner[1], v1, v2, v3, u_inner[5])
372
end
373
374
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
375
<:Adiabatic})(flux_inner,
376
u_inner,
377
normal::AbstractVector,
378
x,
379
t,
380
operator_type::Divergence,
381
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
382
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
383
t,
384
equations)
385
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
386
t,
387
equations)
388
_, tau_1n, tau_2n, tau_3n, _ = flux_inner # extract fluxes for 2nd, 3rd, and 4th equations
389
normal_energy_flux = v1 * tau_1n + v2 * tau_2n + v3 * tau_3n + normal_heat_flux
390
return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4],
391
normal_energy_flux)
392
end
393
394
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
395
<:Isothermal})(flux_inner,
396
u_inner,
397
normal::AbstractVector,
398
x,
399
t,
400
operator_type::Gradient,
401
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
402
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
403
t,
404
equations)
405
T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
406
equations)
407
return SVector(u_inner[1], v1, v2, v3, T)
408
end
409
410
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
411
<:Isothermal})(flux_inner,
412
u_inner,
413
normal::AbstractVector,
414
x,
415
t,
416
operator_type::Divergence,
417
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
418
return flux_inner
419
end
420
421
# specialized BC impositions for GradientVariablesEntropy.
422
423
# This should return a SVector containing the boundary values of entropy variables.
424
# Here, `w_inner` are the transformed variables (e.g., entropy variables).
425
#
426
# Taken from "Entropy stable modal discontinuous Galerkin schemes and wall boundary conditions
427
# for the compressible Navier-Stokes equations" by Chan, Lin, Warburton 2022.
428
# DOI: 10.1016/j.jcp.2021.110723
429
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
430
<:Adiabatic})(flux_inner,
431
w_inner,
432
normal::AbstractVector,
433
x,
434
t,
435
operator_type::Gradient,
436
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
437
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
438
t,
439
equations)
440
negative_rho_inv_p = w_inner[5] # w_5 = -rho / p
441
return SVector(w_inner[1], -v1 * negative_rho_inv_p, -v2 * negative_rho_inv_p,
442
-v3 * negative_rho_inv_p, negative_rho_inv_p)
443
end
444
445
# this is actually identical to the specialization for GradientVariablesPrimitive, but included for completeness.
446
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
447
<:Adiabatic})(flux_inner,
448
w_inner,
449
normal::AbstractVector,
450
x,
451
t,
452
operator_type::Divergence,
453
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
454
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
455
t,
456
equations)
457
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
458
t,
459
equations)
460
_, tau_1n, tau_2n, tau_3n, _ = flux_inner # extract fluxes for 2nd, 3rd, and 4th equations
461
normal_energy_flux = v1 * tau_1n + v2 * tau_2n + v3 * tau_3n + normal_heat_flux
462
return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4],
463
normal_energy_flux)
464
end
465
466
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
467
<:Isothermal})(flux_inner,
468
w_inner,
469
normal::AbstractVector,
470
x,
471
t,
472
operator_type::Gradient,
473
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
474
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
475
t,
476
equations)
477
T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
478
equations)
479
480
# the entropy variables w2 = rho * v1 / p = v1 / T = -v1 * w5. Similarly for w3 and w4
481
w5 = -1 / T
482
return SVector(w_inner[1], -v1 * w5, -v2 * w5, -v3 * w5, w5)
483
end
484
485
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
486
<:Isothermal})(flux_inner,
487
w_inner,
488
normal::AbstractVector,
489
x,
490
t,
491
operator_type::Divergence,
492
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
493
return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4],
494
flux_inner[5])
495
end
496
497
# Computes the mirror velocity across a symmetry plane which enforces
498
# a tangential velocity that is aligned with the symmetry plane, i.e.,
499
# which is normal to the `normal_direction`.
500
# See also `boundary_condition_slip_wall` and `rotate_to_x`.
501
@inline function velocity_symmetry_plane(normal_direction::AbstractVector, v1, v2, v3)
502
norm_ = norm(normal_direction)
503
normal = normal_direction / norm_
504
505
# Compute alignment of velocity with normal direction
506
v_normal = v1 * normal[1] + v2 * normal[2] + v3 * normal[3]
507
508
v1_outer = v1 - 2 * v_normal * normal[1]
509
v2_outer = v2 - 2 * v_normal * normal[2]
510
v3_outer = v3 - 2 * v_normal * normal[3]
511
512
return v1_outer, v2_outer, v3_outer
513
end
514
515
# Note: This should be used with `boundary_condition_slip_wall` for the hyperbolic (Euler) part.
516
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:Slip,
517
<:Adiabatic})(flux_inner,
518
u_inner,
519
normal::AbstractVector,
520
x,
521
t,
522
operator_type::Gradient,
523
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
524
v1_outer, v2_outer, v3_outer = velocity_symmetry_plane(normal,
525
u_inner[2],
526
u_inner[3],
527
u_inner[4])
528
529
return SVector(u_inner[1], v1_outer, v2_outer, v3_outer, u_inner[5])
530
end
531
532
# Note: This should be used with `boundary_condition_slip_wall` for the hyperbolic (Euler) part.
533
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:Slip,
534
<:Adiabatic})(flux_inner,
535
u_inner,
536
normal::AbstractVector,
537
x,
538
t,
539
operator_type::Divergence,
540
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
541
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
542
t,
543
equations)
544
# Normal stresses should be 0. This implies also that `normal_energy_flux = normal_heat_flux`.
545
# For details, see Section 4.2 of
546
# "Entropy stable modal discontinuous Galerkin schemes and wall boundary conditions
547
# for the compressible Navier-Stokes equations" by Chan, Lin, Warburton 2022.
548
# DOI: 10.1016/j.jcp.2021.110723
549
return SVector(flux_inner[1], 0, 0, 0, normal_heat_flux)
550
end
551
552
# Dirichlet Boundary Condition for e.g. P4est mesh
553
@inline function (boundary_condition::BoundaryConditionDirichlet)(flux_inner,
554
u_inner,
555
normal::AbstractVector,
556
x, t,
557
operator_type::Gradient,
558
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
559
# BCs are usually specified as conservative variables so we convert them to primitive variables
560
# because the gradients are assumed to be with respect to the primitive variables
561
u_boundary = boundary_condition.boundary_value_function(x, t, equations)
562
563
return cons2prim(u_boundary, equations)
564
end
565
566
@inline function (boundary_condition::BoundaryConditionDirichlet)(flux_inner,
567
u_inner,
568
normal::AbstractVector,
569
x, t,
570
operator_type::Divergence,
571
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
572
# for Dirichlet boundary conditions, we do not impose any conditions on the viscous fluxes
573
return flux_inner
574
end
575
end # @muladd
576
577