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
5586 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_{\text{total}}
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_{\text{total}} + 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_{\text{total}} - \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 [`CompressibleEulerEquations3D`](@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 Fourier'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 three spatial dimensions we require gradients for four 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
# Add NGRADS as a type parameter here and in AbstractEquationsParabolic, add `ngradients(...)` accessor function
92
93
mu::Mu # viscosity
94
Pr::RealT # Prandtl number
95
kappa::RealT # thermal diffusivity for Fourier's law
96
max_4over3_kappa::RealT # max(4/3, kappa) used for parabolic cfl => `max_diffusivity`
97
98
equations_hyperbolic::E # CompressibleEulerEquations3D
99
gradient_variables::GradientVariables # GradientVariablesPrimitive or GradientVariablesEntropy
100
end
101
102
# default to primitive gradient variables
103
function CompressibleNavierStokesDiffusion3D(equations::CompressibleEulerEquations3D;
104
mu, Prandtl,
105
gradient_variables = GradientVariablesPrimitive())
106
@unpack gamma, inv_gamma_minus_one = equations
107
108
Pr = promote_type(typeof(gamma), typeof(Prandtl))(Prandtl)
109
# Under the assumption of constant Prandtl number the thermal conductivity
110
# constant is kappa = gamma μ / ((gamma-1) Prandtl).
111
# Important note! Factor of μ is accounted for later in `flux`.
112
# This avoids recomputation of kappa for non-constant μ.
113
kappa = gamma * inv_gamma_minus_one / Pr
114
115
return CompressibleNavierStokesDiffusion3D{typeof(gradient_variables),
116
typeof(Pr), typeof(mu),
117
typeof(equations)}(mu, Pr, kappa,
118
max(4 / 3, kappa),
119
equations,
120
gradient_variables)
121
end
122
123
# TODO: parabolic
124
# This is the flexibility a user should have to select the different gradient variable types
125
# varnames(::typeof(cons2prim) , ::CompressibleNavierStokesDiffusion3D) = ("v1", "v2", "v3", "T")
126
# varnames(::typeof(cons2entropy), ::CompressibleNavierStokesDiffusion3D) = ("w2", "w3", "w4", "w5")
127
128
function varnames(variable_mapping,
129
equations_parabolic::CompressibleNavierStokesDiffusion3D)
130
return varnames(variable_mapping, equations_parabolic.equations_hyperbolic)
131
end
132
133
# we specialize this function to compute gradients of primitive variables instead of
134
# conservative variables.
135
function gradient_variable_transformation(::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
136
return cons2prim
137
end
138
function gradient_variable_transformation(::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
139
return cons2entropy
140
end
141
142
# Explicit formulas for the diffusive Navier-Stokes fluxes are available, e.g., in Section 2
143
# of the paper by Rueda-Ramírez, Hennemann, Hindenlang, Winters, and Gassner
144
# "An Entropy Stable Nodal Discontinuous Galerkin Method for the resistive
145
# MHD Equations. Part II: Subcell Finite Volume Shock Capturing"
146
# where one sets the magnetic field components equal to 0.
147
function flux(u, gradients, orientation::Integer,
148
equations::CompressibleNavierStokesDiffusion3D)
149
# Here, `u` is assumed to be the "transformed" variables specified by `gradient_variable_transformation`.
150
_, v1, v2, v3, _ = convert_transformed_to_primitive(u, equations)
151
# Here `gradients` is assumed to contain the gradients of the primitive variables (rho, v1, v2, v3, T)
152
# either computed directly or reverse engineered from the gradient of the entropy variables
153
# by way of the `convert_gradient_variables` function.
154
_, dv1dx, dv2dx, dv3dx, dTdx = convert_derivative_to_primitive(u, gradients[1],
155
equations)
156
_, dv1dy, dv2dy, dv3dy, dTdy = convert_derivative_to_primitive(u, gradients[2],
157
equations)
158
_, dv1dz, dv2dz, dv3dz, dTdz = convert_derivative_to_primitive(u, gradients[3],
159
equations)
160
161
# Components of viscous stress tensor
162
163
# Diagonal parts
164
# (4 * (v1)_x / 3 - 2 * ((v2)_y + (v3)_z)) / 3)
165
tau_11 = (4 * dv1dx - 2 * (dv2dy + dv3dz)) / 3
166
# (4 * (v2)_y / 3 - 2 * ((v1)_x + (v3)_z) / 3)
167
tau_22 = (4 * dv2dy - 2 * (dv1dx + dv3dz)) / 3
168
# (4 * (v3)_z / 3 - 2 * ((v1)_x + (v2)_y) / 3)
169
tau_33 = (4 * dv3dz - 2 * (dv1dx + dv2dy)) / 3
170
171
# Off diagonal parts, exploit that stress tensor is symmetric
172
# ((v1)_y + (v2)_x)
173
tau_12 = dv1dy + dv2dx # = tau_21
174
# ((v1)_z + (v3)_x)
175
tau_13 = dv1dz + dv3dx # = tau_31
176
# ((v2)_z + (v3)_y)
177
tau_23 = dv2dz + dv3dy # = tau_32
178
179
# Fourier's law q = -kappa * grad(T) = -kappa * grad(p / (R rho))
180
# with thermal diffusivity constant kappa = gamma μ R / ((gamma-1) Pr)
181
# Note, the gas constant cancels under this formulation, so it is not present
182
# in the implementation
183
q1 = equations.kappa * dTdx
184
q2 = equations.kappa * dTdy
185
q3 = equations.kappa * dTdz
186
187
# In the simplest cases, the user passed in `mu` or `mu()`
188
# (which returns just a constant) but
189
# more complex functions like Sutherland's law are possible.
190
# `dynamic_viscosity` is a helper function that handles both cases
191
# by dispatching on the type of `equations.mu`.
192
mu = dynamic_viscosity(u, equations)
193
194
if orientation == 1
195
# parabolic flux components in the x-direction
196
f1 = 0
197
f2 = tau_11 * mu
198
f3 = tau_12 * mu
199
f4 = tau_13 * mu
200
f5 = (v1 * tau_11 + v2 * tau_12 + v3 * tau_13 + q1) * mu
201
202
return SVector(f1, f2, f3, f4, f5)
203
elseif orientation == 2
204
# parabolic flux components in the y-direction
205
# Note, symmetry is exploited for tau_12 = tau_21
206
g1 = 0
207
g2 = tau_12 * mu # tau_21 * mu
208
g3 = tau_22 * mu
209
g4 = tau_23 * mu
210
g5 = (v1 * tau_12 + v2 * tau_22 + v3 * tau_23 + q2) * mu
211
212
return SVector(g1, g2, g3, g4, g5)
213
else # if orientation == 3
214
# parabolic flux components in the z-direction
215
# Note, symmetry is exploited for tau_13 = tau_31, tau_23 = tau_32
216
h1 = 0
217
h2 = tau_13 * mu # tau_31 * mu
218
h3 = tau_23 * mu # tau_32 * mu
219
h4 = tau_33 * mu
220
h5 = (v1 * tau_13 + v2 * tau_23 + v3 * tau_33 + q3) * mu
221
222
return SVector(h1, h2, h3, h4, h5)
223
end
224
end
225
226
@doc raw"""
227
max_diffusivity(u, equations_parabolic::CompressibleNavierStokesDiffusion3D)
228
229
# Returns
230
- `dynamic_viscosity(u, equations_parabolic) / u[1] * equations_parabolic.max_4over3_kappa`
231
where `max_4over3_kappa = max(4/3, kappa)` is computed in the constructor.
232
233
For the diffusive estimate we use the eigenvalues of the diffusivity matrix,
234
as suggested in Section 3.5 of
235
- Krais et. al (2021)
236
FLEXI: A high order discontinuous Galerkin framework for hyperbolic–parabolic conservation laws
237
[DOI: 10.1016/j.camwa.2020.05.004](https://doi.org/10.1016/j.camwa.2020.05.004)
238
239
For details on the derivation of eigenvalues of the diffusivity matrix
240
for the compressible Navier-Stokes equations see for instance
241
- Richard P. Dwight (2006)
242
Efficiency improvements of RANS-based analysis and optimization using implicit and adjoint methods on unstructured grids
243
PhD Thesis, University of Manchester
244
https://elib.dlr.de/50794/1/rdwight-PhDThesis-ImplicitAndAdjoint.pdf
245
See especially equations (2.79), (3.24), and (3.25) from Chapter 3.2.3
246
247
The eigenvalues of the diffusivity matrix in 3D are
248
``-\frac{\mu}{\rho} \{0, 4/3, 1, 1, \kappa\}``
249
and thus the largest absolute eigenvalue is
250
``\frac{\mu}{\rho} \max(4/3, \kappa)``.
251
"""
252
@inline function max_diffusivity(u,
253
equations_parabolic::CompressibleNavierStokesDiffusion3D)
254
# See for instance also the computation in FLUXO:
255
# https://github.com/project-fluxo/fluxo/blob/c7e0cc9b7fd4569dcab67bbb6e5a25c0a84859f1/src/equation/navierstokes/calctimestep.f90#L122-L128
256
#
257
# Accordingly, the spectral radius/largest absolute eigenvalue can be computed as:
258
return dynamic_viscosity(u, equations_parabolic) / u[1] *
259
equations_parabolic.max_4over3_kappa
260
end
261
262
# Convert conservative variables to primitive
263
@inline function cons2prim(u, equations::CompressibleNavierStokesDiffusion3D)
264
rho, rho_v1, rho_v2, rho_v3, _ = u
265
266
v1 = rho_v1 / rho
267
v2 = rho_v2 / rho
268
v3 = rho_v3 / rho
269
T = temperature(u, equations)
270
271
return SVector(rho, v1, v2, v3, T)
272
end
273
274
# Convert conservative variables to entropy
275
# TODO: parabolic. We can improve efficiency by not computing w_1, which involves logarithms
276
# This can be done by specializing `cons2entropy` and `entropy2cons` to `CompressibleNavierStokesDiffusion2D`,
277
# but this may be confusing to new users.
278
function cons2entropy(u, equations::CompressibleNavierStokesDiffusion3D)
279
return cons2entropy(u, equations.equations_hyperbolic)
280
end
281
function entropy2cons(w, equations::CompressibleNavierStokesDiffusion3D)
282
return entropy2cons(w, equations.equations_hyperbolic)
283
end
284
285
# the `flux` function takes in transformed variables `u` which depend on the type of the gradient variables.
286
# For CNS, it is simplest to formulate the parabolic terms in primitive variables, so we transform the transformed
287
# variables into primitive variables.
288
@inline function convert_transformed_to_primitive(u_transformed,
289
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
290
return u_transformed
291
end
292
293
# TODO: parabolic. Make this more efficient!
294
@inline function convert_transformed_to_primitive(u_transformed,
295
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
296
# note: this uses CompressibleNavierStokesDiffusion3D versions of cons2prim and entropy2cons
297
return cons2prim(entropy2cons(u_transformed, equations), equations)
298
end
299
300
# Takes the solution values `u` and gradient of the entropy variables (w_2, w_3, w_4, w_5) and
301
# reverse engineers the gradients to be terms of the primitive variables (v1, v2, v3, T).
302
# Helpful because then the diffusive fluxes have the same form as on paper.
303
# Note, the first component of `gradient_entropy_vars` contains gradient(rho) which is unused.
304
# TODO: parabolic; entropy stable parabolic terms
305
@inline function convert_derivative_to_primitive(u, gradient,
306
::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
307
return gradient
308
end
309
310
# the first argument is always the "transformed" variables.
311
@inline function convert_derivative_to_primitive(w, gradient_entropy_vars,
312
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
313
314
# TODO: parabolic. This is inefficient to pass in transformed variables but then transform them back.
315
# We can fix this if we directly compute v1, v2, v3, T from the entropy variables
316
u = entropy2cons(w, equations) # calls a "modified" entropy2cons defined for CompressibleNavierStokesDiffusion3D
317
rho, rho_v1, rho_v2, rho_v3, _ = u
318
319
v1 = rho_v1 / rho
320
v2 = rho_v2 / rho
321
v3 = rho_v3 / rho
322
T = temperature(u, equations)
323
324
return SVector(gradient_entropy_vars[1],
325
T * (gradient_entropy_vars[2] + v1 * gradient_entropy_vars[5]), # grad(u) = T*(grad(w_2)+v1*grad(w_5))
326
T * (gradient_entropy_vars[3] + v2 * gradient_entropy_vars[5]), # grad(v) = T*(grad(w_3)+v2*grad(w_5))
327
T * (gradient_entropy_vars[4] + v3 * gradient_entropy_vars[5]), # grad(v) = T*(grad(w_4)+v3*grad(w_5))
328
T * T * gradient_entropy_vars[5])
329
end
330
331
# This routine is required because `prim2cons` is called in `initial_condition`, which
332
# is called with `equations::CompressibleEulerEquations3D`. This means it is inconsistent
333
# with `cons2prim(..., ::CompressibleNavierStokesDiffusion3D)` as defined above.
334
# TODO: parabolic. Is there a way to clean this up?
335
@inline function prim2cons(u, equations::CompressibleNavierStokesDiffusion3D)
336
return prim2cons(u, equations.equations_hyperbolic)
337
end
338
339
"""
340
temperature(u, equations::CompressibleNavierStokesDiffusion3D)
341
342
Compute the temperature from the conservative variables `u`.
343
In particular, this assumes a specific gas constant ``R = 1``:
344
```math
345
T = \\frac{p}{\\rho}
346
```
347
"""
348
@inline function temperature(u, equations::CompressibleNavierStokesDiffusion3D)
349
rho, rho_v1, rho_v2, rho_v3, rho_e_total = u
350
@unpack gamma = equations
351
352
p = (gamma - 1) *
353
(rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho)
354
T = p / rho # Corresponds to a specific gas constant R = 1
355
return T
356
end
357
358
@inline function velocity(u, equations::CompressibleNavierStokesDiffusion3D)
359
rho = u[1]
360
v1 = u[2] / rho
361
v2 = u[3] / rho
362
v3 = u[4] / rho
363
return SVector(v1, v2, v3)
364
end
365
366
@doc raw"""
367
enstrophy(u, gradients, equations::CompressibleNavierStokesDiffusion3D)
368
369
Computes the (node-wise) enstrophy, defined as
370
```math
371
\mathcal{E} = \frac{1}{2} \rho \boldsymbol{\omega} \cdot \boldsymbol{\omega}
372
```
373
where ``\boldsymbol{\omega} = \nabla \times \boldsymbol{v}`` is the [`vorticity`](@ref).
374
In 3D, ``\boldsymbol{\omega}`` is a full three-component vector.
375
"""
376
@inline function enstrophy(u, gradients, equations::CompressibleNavierStokesDiffusion3D)
377
# Enstrophy is 0.5 rho ω⋅ω where ω = ∇ × v
378
379
omega = vorticity(u, gradients, equations)
380
return 0.5f0 * u[1] * (omega[1]^2 + omega[2]^2 + omega[3]^2)
381
end
382
383
@doc raw"""
384
vorticity(u, gradients, equations::CompressibleNavierStokesDiffusion3D)
385
386
Computes the (node-wise) vorticity, defined in 3D as
387
```math
388
\boldsymbol{\omega} = \nabla \times \boldsymbol{v} =
389
\begin{pmatrix}
390
\frac{\partial v_3}{\partial y} - \frac{\partial v_2}{\partial z} \\
391
\frac{\partial v_1}{\partial z} - \frac{\partial v_3}{\partial x} \\
392
\frac{\partial v_2}{\partial x} - \frac{\partial v_1}{\partial y}
393
\end{pmatrix}
394
```
395
"""
396
@inline function vorticity(u, gradients, equations::CompressibleNavierStokesDiffusion3D)
397
# Ensure that we have velocity `gradients` by way of the `convert_gradient_variables` function.
398
_, _, dv2dx, dv3dx, _ = convert_derivative_to_primitive(u, gradients[1], equations)
399
_, dv1dy, _, dv3dy, _ = convert_derivative_to_primitive(u, gradients[2], equations)
400
_, dv1dz, dv2dz, _, _ = convert_derivative_to_primitive(u, gradients[3], equations)
401
402
return SVector(dv3dy - dv2dz, dv1dz - dv3dx, dv2dx - dv1dy)
403
end
404
405
@inline function vorticity(u, gradients,
406
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
407
# Need to convert to entropy variables first for `convert_derivative_to_primitive` to work correctly.
408
w = cons2entropy(u, equations)
409
410
# Ensure that we have velocity `gradients` by way of the `convert_gradient_variables` function.
411
_, _, dv2dx, dv3dx, _ = convert_derivative_to_primitive(w, gradients[1], equations)
412
_, dv1dy, _, dv3dy, _ = convert_derivative_to_primitive(w, gradients[2], equations)
413
_, dv1dz, dv2dz, _, _ = convert_derivative_to_primitive(w, gradients[3], equations)
414
415
return SVector(dv3dy - dv2dz, dv1dz - dv3dx, dv2dx - dv1dy)
416
end
417
418
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
419
<:Adiabatic})(flux_inner,
420
u_inner,
421
normal::AbstractVector,
422
x,
423
t,
424
operator_type::Gradient,
425
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
426
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
427
t,
428
equations)
429
return SVector(u_inner[1], v1, v2, v3, u_inner[5])
430
end
431
432
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
433
<:Adiabatic})(flux_inner,
434
u_inner,
435
normal::AbstractVector,
436
x,
437
t,
438
operator_type::Divergence,
439
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
440
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
441
t,
442
equations)
443
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
444
t,
445
equations)
446
_, tau_1n, tau_2n, tau_3n, _ = flux_inner # extract fluxes for 2nd, 3rd, and 4th equations
447
normal_energy_flux = v1 * tau_1n + v2 * tau_2n + v3 * tau_3n + normal_heat_flux
448
return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4],
449
normal_energy_flux)
450
end
451
452
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
453
<:Isothermal})(flux_inner,
454
u_inner,
455
normal::AbstractVector,
456
x,
457
t,
458
operator_type::Gradient,
459
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
460
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
461
t,
462
equations)
463
T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
464
equations)
465
return SVector(u_inner[1], v1, v2, v3, T)
466
end
467
468
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
469
<:Isothermal})(flux_inner,
470
u_inner,
471
normal::AbstractVector,
472
x,
473
t,
474
operator_type::Divergence,
475
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
476
return flux_inner
477
end
478
479
# specialized BC impositions for GradientVariablesEntropy.
480
481
# This should return a SVector containing the boundary values of entropy variables.
482
# Here, `w_inner` are the transformed variables (e.g., entropy variables).
483
#
484
# Taken from "Entropy stable modal discontinuous Galerkin schemes and wall boundary conditions
485
# for the compressible Navier-Stokes equations" by Chan, Lin, Warburton 2022.
486
# DOI: 10.1016/j.jcp.2021.110723
487
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
488
<:Adiabatic})(flux_inner,
489
w_inner,
490
normal::AbstractVector,
491
x,
492
t,
493
operator_type::Gradient,
494
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
495
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
496
t,
497
equations)
498
negative_rho_inv_p = w_inner[5] # w_5 = -rho / p
499
return SVector(w_inner[1], -v1 * negative_rho_inv_p, -v2 * negative_rho_inv_p,
500
-v3 * negative_rho_inv_p, negative_rho_inv_p)
501
end
502
503
# this is actually identical to the specialization for GradientVariablesPrimitive, but included for completeness.
504
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
505
<:Adiabatic})(flux_inner,
506
w_inner,
507
normal::AbstractVector,
508
x,
509
t,
510
operator_type::Divergence,
511
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
512
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
513
t,
514
equations)
515
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
516
t,
517
equations)
518
_, tau_1n, tau_2n, tau_3n, _ = flux_inner # extract fluxes for 2nd, 3rd, and 4th equations
519
normal_energy_flux = v1 * tau_1n + v2 * tau_2n + v3 * tau_3n + normal_heat_flux
520
return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4],
521
normal_energy_flux)
522
end
523
524
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
525
<:Isothermal})(flux_inner,
526
w_inner,
527
normal::AbstractVector,
528
x,
529
t,
530
operator_type::Gradient,
531
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
532
v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,
533
t,
534
equations)
535
T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
536
equations)
537
538
# the entropy variables w2 = rho * v1 / p = v1 / T = -v1 * w5. Similarly for w3 and w4
539
w5 = -1 / T
540
return SVector(w_inner[1], -v1 * w5, -v2 * w5, -v3 * w5, w5)
541
end
542
543
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
544
<:Isothermal})(flux_inner,
545
w_inner,
546
normal::AbstractVector,
547
x,
548
t,
549
operator_type::Divergence,
550
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})
551
return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4],
552
flux_inner[5])
553
end
554
555
# Computes the mirror velocity across a symmetry plane which enforces
556
# a tangential velocity that is aligned with the symmetry plane, i.e.,
557
# which is normal to the `normal_direction`.
558
# See also `boundary_condition_slip_wall` and `rotate_to_x`.
559
@inline function velocity_symmetry_plane(normal_direction::AbstractVector, v1, v2, v3)
560
norm_ = norm(normal_direction)
561
normal = normal_direction / norm_
562
563
# Compute alignment of velocity with normal direction
564
v_normal = v1 * normal[1] + v2 * normal[2] + v3 * normal[3]
565
566
v1_outer = v1 - 2 * v_normal * normal[1]
567
v2_outer = v2 - 2 * v_normal * normal[2]
568
v3_outer = v3 - 2 * v_normal * normal[3]
569
570
return v1_outer, v2_outer, v3_outer
571
end
572
573
# Note: This should be used with `boundary_condition_slip_wall` for the hyperbolic (Euler) part.
574
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:Slip,
575
<:Adiabatic})(flux_inner,
576
u_inner,
577
normal::AbstractVector,
578
x,
579
t,
580
operator_type::Gradient,
581
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
582
v1_outer, v2_outer, v3_outer = velocity_symmetry_plane(normal,
583
u_inner[2],
584
u_inner[3],
585
u_inner[4])
586
587
return SVector(u_inner[1], v1_outer, v2_outer, v3_outer, u_inner[5])
588
end
589
590
# Note: This should be used with `boundary_condition_slip_wall` for the hyperbolic (Euler) part.
591
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:Slip,
592
<:Adiabatic})(flux_inner,
593
u_inner,
594
normal::AbstractVector,
595
x,
596
t,
597
operator_type::Divergence,
598
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
599
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
600
t,
601
equations)
602
# Normal stresses should be 0. This implies also that `normal_energy_flux = normal_heat_flux`.
603
# For details, see Section 4.2 of
604
# "Entropy stable modal discontinuous Galerkin schemes and wall boundary conditions
605
# for the compressible Navier-Stokes equations" by Chan, Lin, Warburton 2022.
606
# DOI: 10.1016/j.jcp.2021.110723
607
return SVector(flux_inner[1], 0, 0, 0, normal_heat_flux)
608
end
609
610
# Dirichlet Boundary Condition for e.g. P4est mesh
611
@inline function (boundary_condition::BoundaryConditionDirichlet)(flux_inner,
612
u_inner,
613
normal::AbstractVector,
614
x, t,
615
operator_type::Gradient,
616
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
617
# BCs are usually specified as conservative variables so we convert them to primitive variables
618
# because the gradients are assumed to be with respect to the primitive variables
619
u_boundary = boundary_condition.boundary_value_function(x, t, equations)
620
621
return cons2prim(u_boundary, equations)
622
end
623
624
@inline function (boundary_condition::BoundaryConditionDirichlet)(flux_inner,
625
u_inner,
626
normal::AbstractVector,
627
x, t,
628
operator_type::Divergence,
629
equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})
630
# for Dirichlet boundary conditions, we do not impose any conditions on the parabolic fluxes
631
return flux_inner
632
end
633
end # @muladd
634
635