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
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
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
34
\end{pmatrix}
35
+
36
\frac{\partial}{\partial x}
37
\begin{pmatrix}
38
\rho v_1 \\ \rho v_1^2 + p \\ (\rho e + 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 - \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
100
equations_hyperbolic::E # CompressibleEulerEquations1D
101
gradient_variables::GradientVariables # GradientVariablesPrimitive or GradientVariablesEntropy
102
end
103
104
# default to primitive gradient variables
105
function CompressibleNavierStokesDiffusion1D(equations::CompressibleEulerEquations1D;
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
CompressibleNavierStokesDiffusion1D{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) , ::CompressibleNavierStokesDiffusion1D) = ("v1", "T")
128
# varnames(::typeof(cons2entropy), ::CompressibleNavierStokesDiffusion1D) = ("w2", "w3")
129
130
function varnames(variable_mapping,
131
equations_parabolic::CompressibleNavierStokesDiffusion1D)
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(::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
138
cons2prim
139
end
140
function gradient_variable_transformation(::CompressibleNavierStokesDiffusion1D{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::CompressibleNavierStokesDiffusion1D)
151
# Here, `u` is assumed to be the "transformed" variables specified by `gradient_variable_transformation`.
152
_, v1, _ = convert_transformed_to_primitive(u, equations)
153
# Here `gradients` is assumed to contain the gradients of the primitive variables (rho, v1, 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, dTdx = convert_derivative_to_primitive(u, gradients, equations)
157
158
# Viscous stress (tensor)
159
tau_11 = dv1dx
160
161
# Fick's law q = -kappa * grad(T) = -kappa * grad(p / (R rho))
162
# with thermal diffusivity constant kappa = gamma μ R / ((gamma-1) Pr)
163
# Note, the gas constant cancels under this formulation, so it is not present
164
# in the implementation
165
q1 = equations.kappa * dTdx
166
167
# In the simplest cases, the user passed in `mu` or `mu()`
168
# (which returns just a constant) but
169
# more complex functions like Sutherland's law are possible.
170
# `dynamic_viscosity` is a helper function that handles both cases
171
# by dispatching on the type of `equations.mu`.
172
mu = dynamic_viscosity(u, equations)
173
174
# viscous flux components in the x-direction
175
f1 = 0
176
f2 = tau_11 * mu
177
f3 = (v1 * tau_11 + q1) * mu
178
179
return SVector(f1, f2, f3)
180
end
181
182
# Convert conservative variables to primitive
183
@inline function cons2prim(u, equations::CompressibleNavierStokesDiffusion1D)
184
rho, rho_v1, _ = u
185
186
v1 = rho_v1 / rho
187
T = temperature(u, equations)
188
189
return SVector(rho, v1, T)
190
end
191
192
# Convert conservative variables to entropy
193
# TODO: parabolic. We can improve efficiency by not computing w_1, which involves logarithms
194
# This can be done by specializing `cons2entropy` and `entropy2cons` to `CompressibleNavierStokesDiffusion1D`,
195
# but this may be confusing to new users.
196
function cons2entropy(u, equations::CompressibleNavierStokesDiffusion1D)
197
cons2entropy(u, equations.equations_hyperbolic)
198
end
199
function entropy2cons(w, equations::CompressibleNavierStokesDiffusion1D)
200
entropy2cons(w, equations.equations_hyperbolic)
201
end
202
203
# the `flux` function takes in transformed variables `u` which depend on the type of the gradient variables.
204
# For CNS, it is simplest to formulate the viscous terms in primitive variables, so we transform the transformed
205
# variables into primitive variables.
206
@inline function convert_transformed_to_primitive(u_transformed,
207
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
208
return u_transformed
209
end
210
211
# TODO: parabolic. Make this more efficient!
212
@inline function convert_transformed_to_primitive(u_transformed,
213
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
214
# note: this uses CompressibleNavierStokesDiffusion1D versions of cons2prim and entropy2cons
215
return cons2prim(entropy2cons(u_transformed, equations), equations)
216
end
217
218
# Takes the solution values `u` and gradient of the entropy variables (w_2, w_3) and
219
# reverse engineers the gradients to be terms of the primitive variables (v1, T).
220
# Helpful because then the diffusive fluxes have the same form as on paper.
221
# Note, the first component of `gradient_entropy_vars` contains gradient(rho) which is unused.
222
# TODO: parabolic; entropy stable viscous terms
223
@inline function convert_derivative_to_primitive(u, gradient,
224
::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
225
return gradient
226
end
227
228
# the first argument is always the "transformed" variables.
229
@inline function convert_derivative_to_primitive(w, gradient_entropy_vars,
230
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
231
232
# TODO: parabolic. This is inefficient to pass in transformed variables but then transform them back.
233
# We can fix this if we directly compute v1, T from the entropy variables
234
u = entropy2cons(w, equations) # calls a "modified" entropy2cons defined for CompressibleNavierStokesDiffusion1D
235
rho, rho_v1, _ = u
236
237
v1 = rho_v1 / rho
238
T = temperature(u, equations)
239
240
return SVector(gradient_entropy_vars[1],
241
T * (gradient_entropy_vars[2] + v1 * gradient_entropy_vars[3]), # grad(u) = T*(grad(w_2)+v1*grad(w_3))
242
T * T * gradient_entropy_vars[3])
243
end
244
245
# This routine is required because `prim2cons` is called in `initial_condition`, which
246
# is called with `equations::CompressibleEulerEquations1D`. This means it is inconsistent
247
# with `cons2prim(..., ::CompressibleNavierStokesDiffusion1D)` as defined above.
248
# TODO: parabolic. Is there a way to clean this up?
249
@inline function prim2cons(u, equations::CompressibleNavierStokesDiffusion1D)
250
prim2cons(u, equations.equations_hyperbolic)
251
end
252
253
@inline function temperature(u, equations::CompressibleNavierStokesDiffusion1D)
254
rho, rho_v1, rho_e = u
255
256
p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho_v1^2 / rho)
257
T = p / rho
258
return T
259
end
260
261
@inline function velocity(u, equations::CompressibleNavierStokesDiffusion1D)
262
rho = u[1]
263
v1 = u[2] / rho
264
return v1
265
end
266
267
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
268
<:Adiabatic})(flux_inner,
269
u_inner,
270
orientation::Integer,
271
direction,
272
x,
273
t,
274
operator_type::Gradient,
275
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
276
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
277
equations)
278
return SVector(u_inner[1], v1, u_inner[3])
279
end
280
281
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
282
<:Adiabatic})(flux_inner,
283
u_inner,
284
orientation::Integer,
285
direction,
286
x,
287
t,
288
operator_type::Divergence,
289
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
290
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
291
t,
292
equations)
293
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
294
equations)
295
_, tau_1n, _ = flux_inner # extract fluxes for 2nd equation
296
normal_energy_flux = v1 * tau_1n + normal_heat_flux
297
return SVector(flux_inner[1], flux_inner[2], normal_energy_flux)
298
end
299
300
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
301
<:Isothermal})(flux_inner,
302
u_inner,
303
orientation::Integer,
304
direction,
305
x,
306
t,
307
operator_type::Gradient,
308
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
309
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
310
equations)
311
T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
312
equations)
313
return SVector(u_inner[1], v1, T)
314
end
315
316
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
317
<:Isothermal})(flux_inner,
318
u_inner,
319
orientation::Integer,
320
direction,
321
x,
322
t,
323
operator_type::Divergence,
324
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})
325
return flux_inner
326
end
327
328
# specialized BC impositions for GradientVariablesEntropy.
329
330
# This should return a SVector containing the boundary values of entropy variables.
331
# Here, `w_inner` are the transformed variables (e.g., entropy variables).
332
#
333
# Taken from "Entropy stable modal discontinuous Galerkin schemes and wall boundary conditions
334
# for the compressible Navier-Stokes equations" by Chan, Lin, Warburton 2022.
335
# DOI: 10.1016/j.jcp.2021.110723
336
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
337
<:Adiabatic})(flux_inner,
338
w_inner,
339
orientation::Integer,
340
direction,
341
x,
342
t,
343
operator_type::Gradient,
344
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
345
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
346
equations)
347
negative_rho_inv_p = w_inner[3] # w_3 = -rho / p
348
return SVector(w_inner[1], -v1 * negative_rho_inv_p, negative_rho_inv_p)
349
end
350
351
# this is actually identical to the specialization for GradientVariablesPrimitive, but included for completeness.
352
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
353
<:Adiabatic})(flux_inner,
354
w_inner,
355
orientation::Integer,
356
direction,
357
x,
358
t,
359
operator_type::Divergence,
360
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
361
normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,
362
t,
363
equations)
364
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
365
equations)
366
_, tau_1n, _ = flux_inner # extract fluxes for 2nd equation
367
normal_energy_flux = v1 * tau_1n + normal_heat_flux
368
return SVector(flux_inner[1], flux_inner[2], normal_energy_flux)
369
end
370
371
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
372
<:Isothermal})(flux_inner,
373
w_inner,
374
orientation::Integer,
375
direction,
376
x,
377
t,
378
operator_type::Gradient,
379
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
380
v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,
381
equations)
382
T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,
383
equations)
384
385
# the entropy variables w2 = rho * v1 / p = v1 / T = -v1 * w3.
386
w3 = -1 / T
387
return SVector(w_inner[1], -v1 * w3, w3)
388
end
389
390
@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,
391
<:Isothermal})(flux_inner,
392
w_inner,
393
orientation::Integer,
394
direction,
395
x,
396
t,
397
operator_type::Divergence,
398
equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})
399
return SVector(flux_inner[1], flux_inner[2], flux_inner[3])
400
end
401
end # @muladd
402
403