Path: blob/main/src/equations/compressible_navier_stokes_3d.jl
5586 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67@doc raw"""8CompressibleNavierStokesDiffusion3D(equations; mu, Pr,9gradient_variables=GradientVariablesPrimitive())1011Contains the diffusion (i.e. parabolic) terms applied12to mass, momenta, and total energy together with the advective terms from13the [`CompressibleEulerEquations3D`](@ref).1415- `equations`: instance of the [`CompressibleEulerEquations3D`](@ref)16- `mu`: dynamic viscosity,17- `Pr`: Prandtl number,18- `gradient_variables`: which variables the gradients are taken with respect to.19Defaults to [`GradientVariablesPrimitive()`](@ref).20For an entropy stable formulation, use [`GradientVariablesEntropy()`](@ref).2122Fluid properties such as the dynamic viscosity ``\mu`` can be provided in any consistent unit system, e.g.,23[``\mu``] = kg m⁻¹ s⁻¹.24The viscosity ``\mu`` may be a constant or a function of the current state, e.g.,25depending on temperature (Sutherland's law): ``\mu = \mu(T)``.26In the latter case, the function `mu` needs to have the signature `mu(u, equations)`.2728The particular form of the compressible Navier-Stokes implemented is29```math30\frac{\partial}{\partial t}31\begin{pmatrix}32\rho \\ \rho \mathbf{v} \\ \rho e_{\text{total}}33\end{pmatrix}34+35\nabla \cdot36\begin{pmatrix}37\rho \mathbf{v} \\ \rho \mathbf{v}\mathbf{v}^T + p \underline{I} \\ (\rho e_{\text{total}} + p) \mathbf{v}38\end{pmatrix}39=40\nabla \cdot41\begin{pmatrix}420 \\ \underline{\tau} \\ \underline{\tau}\mathbf{v} - \mathbf{q}43\end{pmatrix}44```45where the system is closed with the ideal gas assumption giving46```math47p = (\gamma - 1) \left( \rho e_{\text{total}} - \frac{1}{2} \rho (v_1^2+v_2^2+v_3^2) \right)48```49as the pressure. The value of the adiabatic constant `gamma` is taken from the [`CompressibleEulerEquations3D`](@ref).50The terms on the right hand side of the system above51are built from the viscous stress tensor52```math53\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}54```55where ``\underline{I}`` is the ``3\times 3`` identity matrix and the heat flux is56```math57\mathbf{q} = -\kappa\nabla\left(T\right),\quad T = \frac{p}{R\rho}58```59where ``T`` is the temperature and ``\kappa`` is the thermal conductivity for Fourier's law.60Under the assumption that the gas has a constant Prandtl number,61the thermal conductivity is62```math63\kappa = \frac{\gamma \mu R}{(\gamma - 1)\textrm{Pr}}.64```65From this combination of temperature ``T`` and thermal conductivity ``\kappa`` we see66that the gas constant `R` cancels and the heat flux becomes67```math68\mathbf{q} = -\kappa\nabla\left(T\right) = -\frac{\gamma \mu}{(\gamma - 1)\textrm{Pr}}\nabla\left(\frac{p}{\rho}\right)69```70which is the form implemented below in the [`flux`](@ref) function.7172In three spatial dimensions we require gradients for four quantities, e.g.,73primitive quantities74```math75\nabla v_1,\, \nabla v_2,\, \nabla v_3,\, \nabla T76```77or the entropy variables78```math79\nabla w_2,\, \nabla w_3,\, \nabla w_4\, \nabla w_580```81where82```math83w_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}84```85"""86struct CompressibleNavierStokesDiffusion3D{GradientVariables, RealT <: Real, Mu,87E <: AbstractCompressibleEulerEquations{3}} <:88AbstractCompressibleNavierStokesDiffusion{3, 5, GradientVariables}89# TODO: parabolic90# Add NGRADS as a type parameter here and in AbstractEquationsParabolic, add `ngradients(...)` accessor function9192mu::Mu # viscosity93Pr::RealT # Prandtl number94kappa::RealT # thermal diffusivity for Fourier's law95max_4over3_kappa::RealT # max(4/3, kappa) used for parabolic cfl => `max_diffusivity`9697equations_hyperbolic::E # CompressibleEulerEquations3D98gradient_variables::GradientVariables # GradientVariablesPrimitive or GradientVariablesEntropy99end100101# default to primitive gradient variables102function CompressibleNavierStokesDiffusion3D(equations::CompressibleEulerEquations3D;103mu, Prandtl,104gradient_variables = GradientVariablesPrimitive())105@unpack gamma, inv_gamma_minus_one = equations106107Pr = promote_type(typeof(gamma), typeof(Prandtl))(Prandtl)108# Under the assumption of constant Prandtl number the thermal conductivity109# constant is kappa = gamma μ / ((gamma-1) Prandtl).110# Important note! Factor of μ is accounted for later in `flux`.111# This avoids recomputation of kappa for non-constant μ.112kappa = gamma * inv_gamma_minus_one / Pr113114return CompressibleNavierStokesDiffusion3D{typeof(gradient_variables),115typeof(Pr), typeof(mu),116typeof(equations)}(mu, Pr, kappa,117max(4 / 3, kappa),118equations,119gradient_variables)120end121122# TODO: parabolic123# This is the flexibility a user should have to select the different gradient variable types124# varnames(::typeof(cons2prim) , ::CompressibleNavierStokesDiffusion3D) = ("v1", "v2", "v3", "T")125# varnames(::typeof(cons2entropy), ::CompressibleNavierStokesDiffusion3D) = ("w2", "w3", "w4", "w5")126127function varnames(variable_mapping,128equations_parabolic::CompressibleNavierStokesDiffusion3D)129return varnames(variable_mapping, equations_parabolic.equations_hyperbolic)130end131132# we specialize this function to compute gradients of primitive variables instead of133# conservative variables.134function gradient_variable_transformation(::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})135return cons2prim136end137function gradient_variable_transformation(::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})138return cons2entropy139end140141# Explicit formulas for the diffusive Navier-Stokes fluxes are available, e.g., in Section 2142# of the paper by Rueda-Ramírez, Hennemann, Hindenlang, Winters, and Gassner143# "An Entropy Stable Nodal Discontinuous Galerkin Method for the resistive144# MHD Equations. Part II: Subcell Finite Volume Shock Capturing"145# where one sets the magnetic field components equal to 0.146function flux(u, gradients, orientation::Integer,147equations::CompressibleNavierStokesDiffusion3D)148# Here, `u` is assumed to be the "transformed" variables specified by `gradient_variable_transformation`.149_, v1, v2, v3, _ = convert_transformed_to_primitive(u, equations)150# Here `gradients` is assumed to contain the gradients of the primitive variables (rho, v1, v2, v3, T)151# either computed directly or reverse engineered from the gradient of the entropy variables152# by way of the `convert_gradient_variables` function.153_, dv1dx, dv2dx, dv3dx, dTdx = convert_derivative_to_primitive(u, gradients[1],154equations)155_, dv1dy, dv2dy, dv3dy, dTdy = convert_derivative_to_primitive(u, gradients[2],156equations)157_, dv1dz, dv2dz, dv3dz, dTdz = convert_derivative_to_primitive(u, gradients[3],158equations)159160# Components of viscous stress tensor161162# Diagonal parts163# (4 * (v1)_x / 3 - 2 * ((v2)_y + (v3)_z)) / 3)164tau_11 = (4 * dv1dx - 2 * (dv2dy + dv3dz)) / 3165# (4 * (v2)_y / 3 - 2 * ((v1)_x + (v3)_z) / 3)166tau_22 = (4 * dv2dy - 2 * (dv1dx + dv3dz)) / 3167# (4 * (v3)_z / 3 - 2 * ((v1)_x + (v2)_y) / 3)168tau_33 = (4 * dv3dz - 2 * (dv1dx + dv2dy)) / 3169170# Off diagonal parts, exploit that stress tensor is symmetric171# ((v1)_y + (v2)_x)172tau_12 = dv1dy + dv2dx # = tau_21173# ((v1)_z + (v3)_x)174tau_13 = dv1dz + dv3dx # = tau_31175# ((v2)_z + (v3)_y)176tau_23 = dv2dz + dv3dy # = tau_32177178# Fourier's law q = -kappa * grad(T) = -kappa * grad(p / (R rho))179# with thermal diffusivity constant kappa = gamma μ R / ((gamma-1) Pr)180# Note, the gas constant cancels under this formulation, so it is not present181# in the implementation182q1 = equations.kappa * dTdx183q2 = equations.kappa * dTdy184q3 = equations.kappa * dTdz185186# In the simplest cases, the user passed in `mu` or `mu()`187# (which returns just a constant) but188# more complex functions like Sutherland's law are possible.189# `dynamic_viscosity` is a helper function that handles both cases190# by dispatching on the type of `equations.mu`.191mu = dynamic_viscosity(u, equations)192193if orientation == 1194# parabolic flux components in the x-direction195f1 = 0196f2 = tau_11 * mu197f3 = tau_12 * mu198f4 = tau_13 * mu199f5 = (v1 * tau_11 + v2 * tau_12 + v3 * tau_13 + q1) * mu200201return SVector(f1, f2, f3, f4, f5)202elseif orientation == 2203# parabolic flux components in the y-direction204# Note, symmetry is exploited for tau_12 = tau_21205g1 = 0206g2 = tau_12 * mu # tau_21 * mu207g3 = tau_22 * mu208g4 = tau_23 * mu209g5 = (v1 * tau_12 + v2 * tau_22 + v3 * tau_23 + q2) * mu210211return SVector(g1, g2, g3, g4, g5)212else # if orientation == 3213# parabolic flux components in the z-direction214# Note, symmetry is exploited for tau_13 = tau_31, tau_23 = tau_32215h1 = 0216h2 = tau_13 * mu # tau_31 * mu217h3 = tau_23 * mu # tau_32 * mu218h4 = tau_33 * mu219h5 = (v1 * tau_13 + v2 * tau_23 + v3 * tau_33 + q3) * mu220221return SVector(h1, h2, h3, h4, h5)222end223end224225@doc raw"""226max_diffusivity(u, equations_parabolic::CompressibleNavierStokesDiffusion3D)227228# Returns229- `dynamic_viscosity(u, equations_parabolic) / u[1] * equations_parabolic.max_4over3_kappa`230where `max_4over3_kappa = max(4/3, kappa)` is computed in the constructor.231232For the diffusive estimate we use the eigenvalues of the diffusivity matrix,233as suggested in Section 3.5 of234- Krais et. al (2021)235FLEXI: A high order discontinuous Galerkin framework for hyperbolic–parabolic conservation laws236[DOI: 10.1016/j.camwa.2020.05.004](https://doi.org/10.1016/j.camwa.2020.05.004)237238For details on the derivation of eigenvalues of the diffusivity matrix239for the compressible Navier-Stokes equations see for instance240- Richard P. Dwight (2006)241Efficiency improvements of RANS-based analysis and optimization using implicit and adjoint methods on unstructured grids242PhD Thesis, University of Manchester243https://elib.dlr.de/50794/1/rdwight-PhDThesis-ImplicitAndAdjoint.pdf244See especially equations (2.79), (3.24), and (3.25) from Chapter 3.2.3245246The eigenvalues of the diffusivity matrix in 3D are247``-\frac{\mu}{\rho} \{0, 4/3, 1, 1, \kappa\}``248and thus the largest absolute eigenvalue is249``\frac{\mu}{\rho} \max(4/3, \kappa)``.250"""251@inline function max_diffusivity(u,252equations_parabolic::CompressibleNavierStokesDiffusion3D)253# See for instance also the computation in FLUXO:254# https://github.com/project-fluxo/fluxo/blob/c7e0cc9b7fd4569dcab67bbb6e5a25c0a84859f1/src/equation/navierstokes/calctimestep.f90#L122-L128255#256# Accordingly, the spectral radius/largest absolute eigenvalue can be computed as:257return dynamic_viscosity(u, equations_parabolic) / u[1] *258equations_parabolic.max_4over3_kappa259end260261# Convert conservative variables to primitive262@inline function cons2prim(u, equations::CompressibleNavierStokesDiffusion3D)263rho, rho_v1, rho_v2, rho_v3, _ = u264265v1 = rho_v1 / rho266v2 = rho_v2 / rho267v3 = rho_v3 / rho268T = temperature(u, equations)269270return SVector(rho, v1, v2, v3, T)271end272273# Convert conservative variables to entropy274# TODO: parabolic. We can improve efficiency by not computing w_1, which involves logarithms275# This can be done by specializing `cons2entropy` and `entropy2cons` to `CompressibleNavierStokesDiffusion2D`,276# but this may be confusing to new users.277function cons2entropy(u, equations::CompressibleNavierStokesDiffusion3D)278return cons2entropy(u, equations.equations_hyperbolic)279end280function entropy2cons(w, equations::CompressibleNavierStokesDiffusion3D)281return entropy2cons(w, equations.equations_hyperbolic)282end283284# the `flux` function takes in transformed variables `u` which depend on the type of the gradient variables.285# For CNS, it is simplest to formulate the parabolic terms in primitive variables, so we transform the transformed286# variables into primitive variables.287@inline function convert_transformed_to_primitive(u_transformed,288equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})289return u_transformed290end291292# TODO: parabolic. Make this more efficient!293@inline function convert_transformed_to_primitive(u_transformed,294equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})295# note: this uses CompressibleNavierStokesDiffusion3D versions of cons2prim and entropy2cons296return cons2prim(entropy2cons(u_transformed, equations), equations)297end298299# Takes the solution values `u` and gradient of the entropy variables (w_2, w_3, w_4, w_5) and300# reverse engineers the gradients to be terms of the primitive variables (v1, v2, v3, T).301# Helpful because then the diffusive fluxes have the same form as on paper.302# Note, the first component of `gradient_entropy_vars` contains gradient(rho) which is unused.303# TODO: parabolic; entropy stable parabolic terms304@inline function convert_derivative_to_primitive(u, gradient,305::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})306return gradient307end308309# the first argument is always the "transformed" variables.310@inline function convert_derivative_to_primitive(w, gradient_entropy_vars,311equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})312313# TODO: parabolic. This is inefficient to pass in transformed variables but then transform them back.314# We can fix this if we directly compute v1, v2, v3, T from the entropy variables315u = entropy2cons(w, equations) # calls a "modified" entropy2cons defined for CompressibleNavierStokesDiffusion3D316rho, rho_v1, rho_v2, rho_v3, _ = u317318v1 = rho_v1 / rho319v2 = rho_v2 / rho320v3 = rho_v3 / rho321T = temperature(u, equations)322323return SVector(gradient_entropy_vars[1],324T * (gradient_entropy_vars[2] + v1 * gradient_entropy_vars[5]), # grad(u) = T*(grad(w_2)+v1*grad(w_5))325T * (gradient_entropy_vars[3] + v2 * gradient_entropy_vars[5]), # grad(v) = T*(grad(w_3)+v2*grad(w_5))326T * (gradient_entropy_vars[4] + v3 * gradient_entropy_vars[5]), # grad(v) = T*(grad(w_4)+v3*grad(w_5))327T * T * gradient_entropy_vars[5])328end329330# This routine is required because `prim2cons` is called in `initial_condition`, which331# is called with `equations::CompressibleEulerEquations3D`. This means it is inconsistent332# with `cons2prim(..., ::CompressibleNavierStokesDiffusion3D)` as defined above.333# TODO: parabolic. Is there a way to clean this up?334@inline function prim2cons(u, equations::CompressibleNavierStokesDiffusion3D)335return prim2cons(u, equations.equations_hyperbolic)336end337338"""339temperature(u, equations::CompressibleNavierStokesDiffusion3D)340341Compute the temperature from the conservative variables `u`.342In particular, this assumes a specific gas constant ``R = 1``:343```math344T = \\frac{p}{\\rho}345```346"""347@inline function temperature(u, equations::CompressibleNavierStokesDiffusion3D)348rho, rho_v1, rho_v2, rho_v3, rho_e_total = u349@unpack gamma = equations350351p = (gamma - 1) *352(rho_e_total - 0.5f0 * (rho_v1^2 + rho_v2^2 + rho_v3^2) / rho)353T = p / rho # Corresponds to a specific gas constant R = 1354return T355end356357@inline function velocity(u, equations::CompressibleNavierStokesDiffusion3D)358rho = u[1]359v1 = u[2] / rho360v2 = u[3] / rho361v3 = u[4] / rho362return SVector(v1, v2, v3)363end364365@doc raw"""366enstrophy(u, gradients, equations::CompressibleNavierStokesDiffusion3D)367368Computes the (node-wise) enstrophy, defined as369```math370\mathcal{E} = \frac{1}{2} \rho \boldsymbol{\omega} \cdot \boldsymbol{\omega}371```372where ``\boldsymbol{\omega} = \nabla \times \boldsymbol{v}`` is the [`vorticity`](@ref).373In 3D, ``\boldsymbol{\omega}`` is a full three-component vector.374"""375@inline function enstrophy(u, gradients, equations::CompressibleNavierStokesDiffusion3D)376# Enstrophy is 0.5 rho ω⋅ω where ω = ∇ × v377378omega = vorticity(u, gradients, equations)379return 0.5f0 * u[1] * (omega[1]^2 + omega[2]^2 + omega[3]^2)380end381382@doc raw"""383vorticity(u, gradients, equations::CompressibleNavierStokesDiffusion3D)384385Computes the (node-wise) vorticity, defined in 3D as386```math387\boldsymbol{\omega} = \nabla \times \boldsymbol{v} =388\begin{pmatrix}389\frac{\partial v_3}{\partial y} - \frac{\partial v_2}{\partial z} \\390\frac{\partial v_1}{\partial z} - \frac{\partial v_3}{\partial x} \\391\frac{\partial v_2}{\partial x} - \frac{\partial v_1}{\partial y}392\end{pmatrix}393```394"""395@inline function vorticity(u, gradients, equations::CompressibleNavierStokesDiffusion3D)396# Ensure that we have velocity `gradients` by way of the `convert_gradient_variables` function.397_, _, dv2dx, dv3dx, _ = convert_derivative_to_primitive(u, gradients[1], equations)398_, dv1dy, _, dv3dy, _ = convert_derivative_to_primitive(u, gradients[2], equations)399_, dv1dz, dv2dz, _, _ = convert_derivative_to_primitive(u, gradients[3], equations)400401return SVector(dv3dy - dv2dz, dv1dz - dv3dx, dv2dx - dv1dy)402end403404@inline function vorticity(u, gradients,405equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})406# Need to convert to entropy variables first for `convert_derivative_to_primitive` to work correctly.407w = cons2entropy(u, equations)408409# Ensure that we have velocity `gradients` by way of the `convert_gradient_variables` function.410_, _, dv2dx, dv3dx, _ = convert_derivative_to_primitive(w, gradients[1], equations)411_, dv1dy, _, dv3dy, _ = convert_derivative_to_primitive(w, gradients[2], equations)412_, dv1dz, dv2dz, _, _ = convert_derivative_to_primitive(w, gradients[3], equations)413414return SVector(dv3dy - dv2dz, dv1dz - dv3dx, dv2dx - dv1dy)415end416417@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,418<:Adiabatic})(flux_inner,419u_inner,420normal::AbstractVector,421x,422t,423operator_type::Gradient,424equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})425v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,426t,427equations)428return SVector(u_inner[1], v1, v2, v3, u_inner[5])429end430431@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,432<:Adiabatic})(flux_inner,433u_inner,434normal::AbstractVector,435x,436t,437operator_type::Divergence,438equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})439normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,440t,441equations)442v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,443t,444equations)445_, tau_1n, tau_2n, tau_3n, _ = flux_inner # extract fluxes for 2nd, 3rd, and 4th equations446normal_energy_flux = v1 * tau_1n + v2 * tau_2n + v3 * tau_3n + normal_heat_flux447return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4],448normal_energy_flux)449end450451@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,452<:Isothermal})(flux_inner,453u_inner,454normal::AbstractVector,455x,456t,457operator_type::Gradient,458equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})459v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,460t,461equations)462T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,463equations)464return SVector(u_inner[1], v1, v2, v3, T)465end466467@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,468<:Isothermal})(flux_inner,469u_inner,470normal::AbstractVector,471x,472t,473operator_type::Divergence,474equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})475return flux_inner476end477478# specialized BC impositions for GradientVariablesEntropy.479480# This should return a SVector containing the boundary values of entropy variables.481# Here, `w_inner` are the transformed variables (e.g., entropy variables).482#483# Taken from "Entropy stable modal discontinuous Galerkin schemes and wall boundary conditions484# for the compressible Navier-Stokes equations" by Chan, Lin, Warburton 2022.485# DOI: 10.1016/j.jcp.2021.110723486@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,487<:Adiabatic})(flux_inner,488w_inner,489normal::AbstractVector,490x,491t,492operator_type::Gradient,493equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})494v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,495t,496equations)497negative_rho_inv_p = w_inner[5] # w_5 = -rho / p498return SVector(w_inner[1], -v1 * negative_rho_inv_p, -v2 * negative_rho_inv_p,499-v3 * negative_rho_inv_p, negative_rho_inv_p)500end501502# this is actually identical to the specialization for GradientVariablesPrimitive, but included for completeness.503@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,504<:Adiabatic})(flux_inner,505w_inner,506normal::AbstractVector,507x,508t,509operator_type::Divergence,510equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})511normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,512t,513equations)514v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,515t,516equations)517_, tau_1n, tau_2n, tau_3n, _ = flux_inner # extract fluxes for 2nd, 3rd, and 4th equations518normal_energy_flux = v1 * tau_1n + v2 * tau_2n + v3 * tau_3n + normal_heat_flux519return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4],520normal_energy_flux)521end522523@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,524<:Isothermal})(flux_inner,525w_inner,526normal::AbstractVector,527x,528t,529operator_type::Gradient,530equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})531v1, v2, v3 = boundary_condition.boundary_condition_velocity.boundary_value_function(x,532t,533equations)534T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,535equations)536537# the entropy variables w2 = rho * v1 / p = v1 / T = -v1 * w5. Similarly for w3 and w4538w5 = -1 / T539return SVector(w_inner[1], -v1 * w5, -v2 * w5, -v3 * w5, w5)540end541542@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,543<:Isothermal})(flux_inner,544w_inner,545normal::AbstractVector,546x,547t,548operator_type::Divergence,549equations::CompressibleNavierStokesDiffusion3D{GradientVariablesEntropy})550return SVector(flux_inner[1], flux_inner[2], flux_inner[3], flux_inner[4],551flux_inner[5])552end553554# Computes the mirror velocity across a symmetry plane which enforces555# a tangential velocity that is aligned with the symmetry plane, i.e.,556# which is normal to the `normal_direction`.557# See also `boundary_condition_slip_wall` and `rotate_to_x`.558@inline function velocity_symmetry_plane(normal_direction::AbstractVector, v1, v2, v3)559norm_ = norm(normal_direction)560normal = normal_direction / norm_561562# Compute alignment of velocity with normal direction563v_normal = v1 * normal[1] + v2 * normal[2] + v3 * normal[3]564565v1_outer = v1 - 2 * v_normal * normal[1]566v2_outer = v2 - 2 * v_normal * normal[2]567v3_outer = v3 - 2 * v_normal * normal[3]568569return v1_outer, v2_outer, v3_outer570end571572# Note: This should be used with `boundary_condition_slip_wall` for the hyperbolic (Euler) part.573@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:Slip,574<:Adiabatic})(flux_inner,575u_inner,576normal::AbstractVector,577x,578t,579operator_type::Gradient,580equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})581v1_outer, v2_outer, v3_outer = velocity_symmetry_plane(normal,582u_inner[2],583u_inner[3],584u_inner[4])585586return SVector(u_inner[1], v1_outer, v2_outer, v3_outer, u_inner[5])587end588589# Note: This should be used with `boundary_condition_slip_wall` for the hyperbolic (Euler) part.590@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:Slip,591<:Adiabatic})(flux_inner,592u_inner,593normal::AbstractVector,594x,595t,596operator_type::Divergence,597equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})598normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,599t,600equations)601# Normal stresses should be 0. This implies also that `normal_energy_flux = normal_heat_flux`.602# For details, see Section 4.2 of603# "Entropy stable modal discontinuous Galerkin schemes and wall boundary conditions604# for the compressible Navier-Stokes equations" by Chan, Lin, Warburton 2022.605# DOI: 10.1016/j.jcp.2021.110723606return SVector(flux_inner[1], 0, 0, 0, normal_heat_flux)607end608609# Dirichlet Boundary Condition for e.g. P4est mesh610@inline function (boundary_condition::BoundaryConditionDirichlet)(flux_inner,611u_inner,612normal::AbstractVector,613x, t,614operator_type::Gradient,615equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})616# BCs are usually specified as conservative variables so we convert them to primitive variables617# because the gradients are assumed to be with respect to the primitive variables618u_boundary = boundary_condition.boundary_value_function(x, t, equations)619620return cons2prim(u_boundary, equations)621end622623@inline function (boundary_condition::BoundaryConditionDirichlet)(flux_inner,624u_inner,625normal::AbstractVector,626x, t,627operator_type::Divergence,628equations::CompressibleNavierStokesDiffusion3D{GradientVariablesPrimitive})629# for Dirichlet boundary conditions, we do not impose any conditions on the parabolic fluxes630return flux_inner631end632end # @muladd633634635