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_1d.jl
2831 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
CompressibleNavierStokesDiffusion1D(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 [`CompressibleEulerEquations1D`](@ref).
15
16
- `equations`: instance of the [`CompressibleEulerEquations1D`](@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 v_1 \\ \rho e_{\text{total}}
34
\end{pmatrix}
35
+
36
\frac{\partial}{\partial x}
37
\begin{pmatrix}
38
\rho v_1 \\ \rho v_1^2 + p \\ (\rho e_{\text{total}} + p) v_1
39
\end{pmatrix}
40
=
41
\frac{\partial}{\partial x}
42
\begin{pmatrix}
43
0 \\ \tau \\ \tau v_1 - 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 \right)
49
```
50
as the pressure. The value of the adiabatic constant `gamma` is taken from the [`CompressibleEulerEquations1D`](@ref).
51
The terms on the right hand side of the system above
52
are built from the viscous stress
53
```math
54
\tau = \mu \frac{\partial}{\partial x} v_1
55
```
56
where the heat flux is
57
```math
58
q = -\kappa \frac{\partial}{\partial x} \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
q = -\kappa \frac{\partial}{\partial x} \left(T\right) = -\frac{\gamma \mu}{(\gamma - 1)\textrm{Pr}} \frac{\partial}{\partial x} \left(\frac{p}{\rho}\right)
70
```
71
which is the form implemented below in the [`flux`](@ref) function.
72
73
In one spatial dimensions we require gradients for two quantities, e.g.,
74
primitive quantities
75
```math
76
\frac{\partial}{\partial x} v_1,\, \frac{\partial}{\partial x} T
77
```
78
or the entropy variables
79
```math
80
\frac{\partial}{\partial x} w_2,\, \frac{\partial}{\partial x} w_3
81
```
82
where
83
```math
84
w_2 = \frac{\rho v_1}{p},\, w_3 = -\frac{\rho}{p}
85
```
86
"""
87
struct CompressibleNavierStokesDiffusion1D{GradientVariables, RealT <: Real, Mu,
88
E <: AbstractCompressibleEulerEquations{1}} <:
89
AbstractCompressibleNavierStokesDiffusion{1, 3, 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
max_1_kappa::RealT # max(1, kappa) used for diffusive CFL => `max_diffusivity`
100
101
equations_hyperbolic::E # CompressibleEulerEquations1D
102
gradient_variables::GradientVariables # GradientVariablesPrimitive or GradientVariablesEntropy
103
end
104
105
# default to primitive gradient variables
106
function CompressibleNavierStokesDiffusion1D(equations::CompressibleEulerEquations1D;
107
mu, Prandtl,
108
gradient_variables = GradientVariablesPrimitive())
109
gamma = equations.gamma
110
inv_gamma_minus_one = equations.inv_gamma_minus_one
111
112
# Under the assumption of constant Prandtl number the thermal conductivity
113
# constant is kappa = gamma μ / ((gamma-1) Prandtl).
114
# Important note! Factor of μ is accounted for later in `flux`.
115
# This avoids recomputation of kappa for non-constant μ.
116
kappa = gamma * inv_gamma_minus_one / Prandtl
117
118
return CompressibleNavierStokesDiffusion1D{typeof(gradient_variables),
119
typeof(gamma),
120
typeof(mu),
121
typeof(equations)}(gamma,
122
inv_gamma_minus_one,
123
mu, Prandtl, kappa,
124
max(one(kappa),
125
kappa),
126
equations,
127
gradient_variables)
128
end
129
130
# TODO: parabolic
131
# This is the flexibility a user should have to select the different gradient variable types
132
# varnames(::typeof(cons2prim) , ::CompressibleNavierStokesDiffusion1D) = ("v1", "T")
133
# varnames(::typeof(cons2entropy), ::CompressibleNavierStokesDiffusion1D) = ("w2", "w3")
134
135
function varnames(variable_mapping,
136
equations_parabolic::CompressibleNavierStokesDiffusion1D)
137
return varnames(variable_mapping, equations_parabolic.equations_hyperbolic)
138
end
139
140
# we specialize this function to compute gradients of primitive variables instead of
141
# conservative variables.
142
function gradient_variable_transformation(::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
143
return cons2prim
144
end
145
function gradient_variable_transformation(::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
146
return cons2entropy
147
end
148
149
# Explicit formulas for the diffusive Navier-Stokes fluxes are available, e.g., in Section 2
150
# of the paper by Rueda-Ramírez, Hennemann, Hindenlang, Winters, and Gassner
151
# "An Entropy Stable Nodal Discontinuous Galerkin Method for the resistive
152
# MHD Equations. Part II: Subcell Finite Volume Shock Capturing"
153
# where one sets the magnetic field components equal to 0.
154
function flux(u, gradients, orientation::Integer,
155
equations::CompressibleNavierStokesDiffusion1D)
156
# Here, `u` is assumed to be the "transformed" variables specified by `gradient_variable_transformation`.
157
_, v1, _ = convert_transformed_to_primitive(u, equations)
158
# Here `gradients` is assumed to contain the gradients of the primitive variables (rho, v1, T)
159
# either computed directly or reverse engineered from the gradient of the entropy variables
160
# by way of the `convert_gradient_variables` function.
161
_, dv1dx, dTdx = convert_derivative_to_primitive(u, gradients[1], equations)
162
163
# Viscous stress (tensor)
164
tau_11 = dv1dx
165
166
# Fick's law q = -kappa * grad(T) = -kappa * grad(p / (R rho))
167
# with thermal diffusivity constant kappa = gamma μ R / ((gamma-1) Pr)
168
# Note, the gas constant cancels under this formulation, so it is not present
169
# in the implementation
170
q1 = equations.kappa * dTdx
171
172
# In the simplest cases, the user passed in `mu` or `mu()`
173
# (which returns just a constant) but
174
# more complex functions like Sutherland's law are possible.
175
# `dynamic_viscosity` is a helper function that handles both cases
176
# by dispatching on the type of `equations.mu`.
177
mu = dynamic_viscosity(u, equations)
178
179
# viscous flux components in the x-direction
180
f1 = 0
181
f2 = tau_11 * mu
182
f3 = (v1 * tau_11 + q1) * mu
183
184
return SVector(f1, f2, f3)
185
end
186
187
@doc raw"""
188
max_diffusivity(u, equations_parabolic::CompressibleNavierStokesDiffusion1D)
189
190
# Returns
191
- `dynamic_viscosity(u, equations_parabolic) / u[1] * equations_parabolic.max_1_kappa`
192
where `max_1_kappa = max(one(kappa), kappa)` is computed in the constructor.
193
194
For the diffusive estimate we use the eigenvalues of the diffusivity matrix,
195
as suggested in Section 3.5 of
196
- Krais et. al (2021)
197
FLEXI: A high order discontinuous Galerkin framework for hyperbolic–parabolic conservation laws
198
[DOI: 10.1016/j.camwa.2020.05.004](https://doi.org/10.1016/j.camwa.2020.05.004)
199
200
For details on the derivation of eigenvalues of the diffusivity matrix
201
for the compressible Navier-Stokes equations see for instance
202
- Richard P. Dwight (2006)
203
Efficiency improvements of RANS-based analysis and optimization using implicit and adjoint methods on unstructured grids
204
PhD Thesis, University of Manchester
205
https://elib.dlr.de/50794/1/rdwight-PhDThesis-ImplicitAndAdjoint.pdf
206
See especially equations (2.79), (3.24), and (3.25) from Chapter 3.2.3
207
208
The eigenvalues of the diffusivity matrix in 1D are
209
``-\frac{\mu}{\rho} \{0, 1, \kappa\}``
210
and thus the largest absolute eigenvalue is
211
``\frac{\mu}{\rho} \max(1, \kappa)``.
212
"""
213
@inline function max_diffusivity(u,
214
equations_parabolic::CompressibleNavierStokesDiffusion1D)
215
# See for instance also the computation in FLUXO:
216
# https://github.com/project-fluxo/fluxo/blob/c7e0cc9b7fd4569dcab67bbb6e5a25c0a84859f1/src/equation/navierstokes/calctimestep.f90#L122-L128
217
#
218
# Accordingly, the spectral radius/largest absolute eigenvalue can be computed as:
219
return dynamic_viscosity(u, equations_parabolic) / u[1] *
220
equations_parabolic.max_1_kappa
221
end
222
223
# Convert conservative variables to primitive
224
@inline function cons2prim(u, equations::CompressibleNavierStokesDiffusion1D)
225
rho, rho_v1, _ = u
226
227
v1 = rho_v1 / rho
228
T = temperature(u, equations)
229
230
return SVector(rho, v1, T)
231
end
232
233
# Convert conservative variables to entropy
234
# TODO: parabolic. We can improve efficiency by not computing w_1, which involves logarithms
235
# This can be done by specializing `cons2entropy` and `entropy2cons` to `CompressibleNavierStokesDiffusion1D`,
236
# but this may be confusing to new users.
237
function cons2entropy(u, equations::CompressibleNavierStokesDiffusion1D)
238
return cons2entropy(u, equations.equations_hyperbolic)
239
end
240
function entropy2cons(w, equations::CompressibleNavierStokesDiffusion1D)
241
return entropy2cons(w, equations.equations_hyperbolic)
242
end
243
244
# the `flux` function takes in transformed variables `u` which depend on the type of the gradient variables.
245
# For CNS, it is simplest to formulate the viscous terms in primitive variables, so we transform the transformed
246
# variables into primitive variables.
247
@inline function convert_transformed_to_primitive(u_transformed,
248
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
249
return u_transformed
250
end
251
252
# TODO: parabolic. Make this more efficient!
253
@inline function convert_transformed_to_primitive(u_transformed,
254
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
255
# note: this uses CompressibleNavierStokesDiffusion1D versions of cons2prim and entropy2cons
256
return cons2prim(entropy2cons(u_transformed, equations), equations)
257
end
258
259
# Takes the solution values `u` and gradient of the entropy variables (w_2, w_3) and
260
# reverse engineers the gradients to be terms of the primitive variables (v1, T).
261
# Helpful because then the diffusive fluxes have the same form as on paper.
262
# Note, the first component of `gradient_entropy_vars` contains gradient(rho) which is unused.
263
# TODO: parabolic; entropy stable viscous terms
264
@inline function convert_derivative_to_primitive(u, gradient,
265
::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
266
return gradient
267
end
268
269
# the first argument is always the "transformed" variables.
270
@inline function convert_derivative_to_primitive(w, gradient_entropy_vars,
271
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
272
273
# TODO: parabolic. This is inefficient to pass in transformed variables but then transform them back.
274
# We can fix this if we directly compute v1, T from the entropy variables
275
u = entropy2cons(w, equations) # calls a "modified" entropy2cons defined for CompressibleNavierStokesDiffusion1D
276
rho, rho_v1, _ = u
277
278
v1 = rho_v1 / rho
279
T = temperature(u, equations)
280
281
return SVector(gradient_entropy_vars[1],
282
T * (gradient_entropy_vars[2] + v1 * gradient_entropy_vars[3]), # grad(u) = T*(grad(w_2)+v1*grad(w_3))
283
T * T * gradient_entropy_vars[3])
284
end
285
286
# This routine is required because `prim2cons` is called in `initial_condition`, which
287
# is called with `equations::CompressibleEulerEquations1D`. This means it is inconsistent
288
# with `cons2prim(..., ::CompressibleNavierStokesDiffusion1D)` as defined above.
289
# TODO: parabolic. Is there a way to clean this up?
290
@inline function prim2cons(u, equations::CompressibleNavierStokesDiffusion1D)
291
return prim2cons(u, equations.equations_hyperbolic)
292
end
293
294
"""
295
temperature(u, equations::CompressibleNavierStokesDiffusion1D)
296
297
Compute the temperature from the conservative variables `u`.
298
In particular, this assumes a specific gas constant ``R = 1``:
299
```math
300
T = \\frac{p}{\\rho}
301
```
302
"""
303
@inline function temperature(u, equations::CompressibleNavierStokesDiffusion1D)
304
rho, rho_v1, rho_e_total = u
305
306
p = (equations.gamma - 1) * (rho_e_total - 0.5f0 * rho_v1^2 / rho)
307
T = p / rho # Corresponds to a specific gas constant R = 1
308
return T
309
end
310
311
@inline function velocity(u, equations::CompressibleNavierStokesDiffusion1D)
312
rho = u[1]
313
v1 = u[2] / rho
314
return v1
315
end
316
317
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
318
<:Adiabatic})(flux_inner,
319
u_inner,
320
orientation::Integer,
321
direction,
322
x,
323
t,
324
operator_type::Gradient,
325
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
326
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
327
equations)
328
return SVector(u_inner[1], v1, u_inner[3])
329
end
330
331
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
332
<:Adiabatic})(flux_inner,
333
u_inner,
334
orientation::Integer,
335
direction,
336
x,
337
t,
338
operator_type::Divergence,
339
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
340
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
341
t,
342
equations)
343
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
344
equations)
345
_, tau_1n, _ = flux_inner # extract fluxes for 2nd equation
346
normal_energy_flux = v1 * tau_1n + normal_heat_flux
347
return SVector(flux_inner[1], flux_inner[2], normal_energy_flux)
348
end
349
350
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
351
<:Isothermal})(flux_inner,
352
u_inner,
353
orientation::Integer,
354
direction,
355
x,
356
t,
357
operator_type::Gradient,
358
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
359
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
360
equations)
361
T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
362
equations)
363
return SVector(u_inner[1], v1, T)
364
end
365
366
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
367
<:Isothermal})(flux_inner,
368
u_inner,
369
orientation::Integer,
370
direction,
371
x,
372
t,
373
operator_type::Divergence,
374
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
375
return flux_inner
376
end
377
378
# specialized BC impositions for GradientVariablesEntropy.
379
380
# This should return a SVector containing the boundary values of entropy variables.
381
# Here, `w_inner` are the transformed variables (e.g., entropy variables).
382
#
383
# Taken from "Entropy stable modal discontinuous Galerkin schemes and wall boundary conditions
384
# for the compressible Navier-Stokes equations" by Chan, Lin, Warburton 2022.
385
# DOI: 10.1016/j.jcp.2021.110723
386
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
387
<:Adiabatic})(flux_inner,
388
w_inner,
389
orientation::Integer,
390
direction,
391
x,
392
t,
393
operator_type::Gradient,
394
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
395
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
396
equations)
397
negative_rho_inv_p = w_inner[3] # w_3 = -rho / p
398
return SVector(w_inner[1], -v1 * negative_rho_inv_p, negative_rho_inv_p)
399
end
400
401
# this is actually identical to the specialization for GradientVariablesPrimitive, but included for completeness.
402
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
403
<:Adiabatic})(flux_inner,
404
w_inner,
405
orientation::Integer,
406
direction,
407
x,
408
t,
409
operator_type::Divergence,
410
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
411
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
412
t,
413
equations)
414
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
415
equations)
416
_, tau_1n, _ = flux_inner # extract fluxes for 2nd equation
417
normal_energy_flux = v1 * tau_1n + normal_heat_flux
418
return SVector(flux_inner[1], flux_inner[2], normal_energy_flux)
419
end
420
421
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
422
<:Isothermal})(flux_inner,
423
w_inner,
424
orientation::Integer,
425
direction,
426
x,
427
t,
428
operator_type::Gradient,
429
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
430
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
431
equations)
432
T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
433
equations)
434
435
# the entropy variables w2 = rho * v1 / p = v1 / T = -v1 * w3.
436
w3 = -1 / T
437
return SVector(w_inner[1], -v1 * w3, w3)
438
end
439
440
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
441
<:Isothermal})(flux_inner,
442
w_inner,
443
orientation::Integer,
444
direction,
445
x,
446
t,
447
operator_type::Divergence,
448
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
449
return SVector(flux_inner[1], flux_inner[2], flux_inner[3])
450
end
451
end # @muladd
452
453