Path: blob/main/src/equations/compressible_navier_stokes_1d.jl
2055 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"""8CompressibleNavierStokesDiffusion1D(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 [`CompressibleEulerEquations1D`](@ref).1415- `equations`: instance of the [`CompressibleEulerEquations1D`](@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 v_1 \\ \rho e33\end{pmatrix}34+35\frac{\partial}{\partial x}36\begin{pmatrix}37\rho v_1 \\ \rho v_1^2 + p \\ (\rho e + p) v_138\end{pmatrix}39=40\frac{\partial}{\partial x}41\begin{pmatrix}420 \\ \tau \\ \tau v_1 - q43\end{pmatrix}44```45where the system is closed with the ideal gas assumption giving46```math47p = (\gamma - 1) \left( \rho e - \frac{1}{2} \rho v_1^2 \right)48```49as the pressure. The value of the adiabatic constant `gamma` is taken from the [`CompressibleEulerEquations1D`](@ref).50The terms on the right hand side of the system above51are built from the viscous stress52```math53\tau = \mu \frac{\partial}{\partial x} v_154```55where the heat flux is56```math57q = -\kappa \frac{\partial}{\partial x} \left(T\right),\quad T = \frac{p}{R\rho}58```59where ``T`` is the temperature and ``\kappa`` is the thermal conductivity for Fick'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```math68q = -\kappa \frac{\partial}{\partial x} \left(T\right) = -\frac{\gamma \mu}{(\gamma - 1)\textrm{Pr}} \frac{\partial}{\partial x} \left(\frac{p}{\rho}\right)69```70which is the form implemented below in the [`flux`](@ref) function.7172In one spatial dimensions we require gradients for two quantities, e.g.,73primitive quantities74```math75\frac{\partial}{\partial x} v_1,\, \frac{\partial}{\partial x} T76```77or the entropy variables78```math79\frac{\partial}{\partial x} w_2,\, \frac{\partial}{\partial x} w_380```81where82```math83w_2 = \frac{\rho v_1}{p},\, w_3 = -\frac{\rho}{p}84```85"""86struct CompressibleNavierStokesDiffusion1D{GradientVariables, RealT <: Real, Mu,87E <: AbstractCompressibleEulerEquations{1}} <:88AbstractCompressibleNavierStokesDiffusion{1, 3, GradientVariables}89# TODO: parabolic90# 1) For now save gamma and inv(gamma-1) again, but could potentially reuse them from the Euler equations91# 2) Add NGRADS as a type parameter here and in AbstractEquationsParabolic, add `ngradients(...)` accessor function92gamma::RealT # ratio of specific heats93inv_gamma_minus_one::RealT # = inv(gamma - 1); can be used to write slow divisions as fast multiplications9495mu::Mu # viscosity96Pr::RealT # Prandtl number97kappa::RealT # thermal diffusivity for Fick's law9899equations_hyperbolic::E # CompressibleEulerEquations1D100gradient_variables::GradientVariables # GradientVariablesPrimitive or GradientVariablesEntropy101end102103# default to primitive gradient variables104function CompressibleNavierStokesDiffusion1D(equations::CompressibleEulerEquations1D;105mu, Prandtl,106gradient_variables = GradientVariablesPrimitive())107gamma = equations.gamma108inv_gamma_minus_one = equations.inv_gamma_minus_one109110# Under the assumption of constant Prandtl number the thermal conductivity111# constant is kappa = gamma μ / ((gamma-1) Prandtl).112# Important note! Factor of μ is accounted for later in `flux`.113# This avoids recomputation of kappa for non-constant μ.114kappa = gamma * inv_gamma_minus_one / Prandtl115116CompressibleNavierStokesDiffusion1D{typeof(gradient_variables), typeof(gamma),117typeof(mu),118typeof(equations)}(gamma, inv_gamma_minus_one,119mu, Prandtl, kappa,120equations,121gradient_variables)122end123124# TODO: parabolic125# This is the flexibility a user should have to select the different gradient variable types126# varnames(::typeof(cons2prim) , ::CompressibleNavierStokesDiffusion1D) = ("v1", "T")127# varnames(::typeof(cons2entropy), ::CompressibleNavierStokesDiffusion1D) = ("w2", "w3")128129function varnames(variable_mapping,130equations_parabolic::CompressibleNavierStokesDiffusion1D)131varnames(variable_mapping, equations_parabolic.equations_hyperbolic)132end133134# we specialize this function to compute gradients of primitive variables instead of135# conservative variables.136function gradient_variable_transformation(::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})137cons2prim138end139function gradient_variable_transformation(::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})140cons2entropy141end142143# Explicit formulas for the diffusive Navier-Stokes fluxes are available, e.g., in Section 2144# of the paper by Rueda-Ramírez, Hennemann, Hindenlang, Winters, and Gassner145# "An Entropy Stable Nodal Discontinuous Galerkin Method for the resistive146# MHD Equations. Part II: Subcell Finite Volume Shock Capturing"147# where one sets the magnetic field components equal to 0.148function flux(u, gradients, orientation::Integer,149equations::CompressibleNavierStokesDiffusion1D)150# Here, `u` is assumed to be the "transformed" variables specified by `gradient_variable_transformation`.151_, v1, _ = convert_transformed_to_primitive(u, equations)152# Here `gradients` is assumed to contain the gradients of the primitive variables (rho, v1, T)153# either computed directly or reverse engineered from the gradient of the entropy variables154# by way of the `convert_gradient_variables` function.155_, dv1dx, dTdx = convert_derivative_to_primitive(u, gradients, equations)156157# Viscous stress (tensor)158tau_11 = dv1dx159160# Fick's law q = -kappa * grad(T) = -kappa * grad(p / (R rho))161# with thermal diffusivity constant kappa = gamma μ R / ((gamma-1) Pr)162# Note, the gas constant cancels under this formulation, so it is not present163# in the implementation164q1 = equations.kappa * dTdx165166# In the simplest cases, the user passed in `mu` or `mu()`167# (which returns just a constant) but168# more complex functions like Sutherland's law are possible.169# `dynamic_viscosity` is a helper function that handles both cases170# by dispatching on the type of `equations.mu`.171mu = dynamic_viscosity(u, equations)172173# viscous flux components in the x-direction174f1 = 0175f2 = tau_11 * mu176f3 = (v1 * tau_11 + q1) * mu177178return SVector(f1, f2, f3)179end180181# Convert conservative variables to primitive182@inline function cons2prim(u, equations::CompressibleNavierStokesDiffusion1D)183rho, rho_v1, _ = u184185v1 = rho_v1 / rho186T = temperature(u, equations)187188return SVector(rho, v1, T)189end190191# Convert conservative variables to entropy192# TODO: parabolic. We can improve efficiency by not computing w_1, which involves logarithms193# This can be done by specializing `cons2entropy` and `entropy2cons` to `CompressibleNavierStokesDiffusion1D`,194# but this may be confusing to new users.195function cons2entropy(u, equations::CompressibleNavierStokesDiffusion1D)196cons2entropy(u, equations.equations_hyperbolic)197end198function entropy2cons(w, equations::CompressibleNavierStokesDiffusion1D)199entropy2cons(w, equations.equations_hyperbolic)200end201202# the `flux` function takes in transformed variables `u` which depend on the type of the gradient variables.203# For CNS, it is simplest to formulate the viscous terms in primitive variables, so we transform the transformed204# variables into primitive variables.205@inline function convert_transformed_to_primitive(u_transformed,206equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})207return u_transformed208end209210# TODO: parabolic. Make this more efficient!211@inline function convert_transformed_to_primitive(u_transformed,212equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})213# note: this uses CompressibleNavierStokesDiffusion1D versions of cons2prim and entropy2cons214return cons2prim(entropy2cons(u_transformed, equations), equations)215end216217# Takes the solution values `u` and gradient of the entropy variables (w_2, w_3) and218# reverse engineers the gradients to be terms of the primitive variables (v1, T).219# Helpful because then the diffusive fluxes have the same form as on paper.220# Note, the first component of `gradient_entropy_vars` contains gradient(rho) which is unused.221# TODO: parabolic; entropy stable viscous terms222@inline function convert_derivative_to_primitive(u, gradient,223::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})224return gradient225end226227# the first argument is always the "transformed" variables.228@inline function convert_derivative_to_primitive(w, gradient_entropy_vars,229equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})230231# TODO: parabolic. This is inefficient to pass in transformed variables but then transform them back.232# We can fix this if we directly compute v1, T from the entropy variables233u = entropy2cons(w, equations) # calls a "modified" entropy2cons defined for CompressibleNavierStokesDiffusion1D234rho, rho_v1, _ = u235236v1 = rho_v1 / rho237T = temperature(u, equations)238239return SVector(gradient_entropy_vars[1],240T * (gradient_entropy_vars[2] + v1 * gradient_entropy_vars[3]), # grad(u) = T*(grad(w_2)+v1*grad(w_3))241T * T * gradient_entropy_vars[3])242end243244# This routine is required because `prim2cons` is called in `initial_condition`, which245# is called with `equations::CompressibleEulerEquations1D`. This means it is inconsistent246# with `cons2prim(..., ::CompressibleNavierStokesDiffusion1D)` as defined above.247# TODO: parabolic. Is there a way to clean this up?248@inline function prim2cons(u, equations::CompressibleNavierStokesDiffusion1D)249prim2cons(u, equations.equations_hyperbolic)250end251252@inline function temperature(u, equations::CompressibleNavierStokesDiffusion1D)253rho, rho_v1, rho_e = u254255p = (equations.gamma - 1) * (rho_e - 0.5f0 * rho_v1^2 / rho)256T = p / rho257return T258end259260@inline function velocity(u, equations::CompressibleNavierStokesDiffusion1D)261rho = u[1]262v1 = u[2] / rho263return v1264end265266@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,267<:Adiabatic})(flux_inner,268u_inner,269orientation::Integer,270direction,271x,272t,273operator_type::Gradient,274equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})275v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,276equations)277return SVector(u_inner[1], v1, u_inner[3])278end279280@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,281<:Adiabatic})(flux_inner,282u_inner,283orientation::Integer,284direction,285x,286t,287operator_type::Divergence,288equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})289normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,290t,291equations)292v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,293equations)294_, tau_1n, _ = flux_inner # extract fluxes for 2nd equation295normal_energy_flux = v1 * tau_1n + normal_heat_flux296return SVector(flux_inner[1], flux_inner[2], normal_energy_flux)297end298299@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,300<:Isothermal})(flux_inner,301u_inner,302orientation::Integer,303direction,304x,305t,306operator_type::Gradient,307equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})308v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,309equations)310T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,311equations)312return SVector(u_inner[1], v1, T)313end314315@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,316<:Isothermal})(flux_inner,317u_inner,318orientation::Integer,319direction,320x,321t,322operator_type::Divergence,323equations::CompressibleNavierStokesDiffusion1D{GradientVariablesPrimitive})324return flux_inner325end326327# specialized BC impositions for GradientVariablesEntropy.328329# This should return a SVector containing the boundary values of entropy variables.330# Here, `w_inner` are the transformed variables (e.g., entropy variables).331#332# Taken from "Entropy stable modal discontinuous Galerkin schemes and wall boundary conditions333# for the compressible Navier-Stokes equations" by Chan, Lin, Warburton 2022.334# DOI: 10.1016/j.jcp.2021.110723335@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,336<:Adiabatic})(flux_inner,337w_inner,338orientation::Integer,339direction,340x,341t,342operator_type::Gradient,343equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})344v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,345equations)346negative_rho_inv_p = w_inner[3] # w_3 = -rho / p347return SVector(w_inner[1], -v1 * negative_rho_inv_p, negative_rho_inv_p)348end349350# this is actually identical to the specialization for GradientVariablesPrimitive, but included for completeness.351@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,352<:Adiabatic})(flux_inner,353w_inner,354orientation::Integer,355direction,356x,357t,358operator_type::Divergence,359equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})360normal_heat_flux = boundary_condition.boundary_condition_heat_flux.boundary_value_normal_flux_function(x,361t,362equations)363v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,364equations)365_, tau_1n, _ = flux_inner # extract fluxes for 2nd equation366normal_energy_flux = v1 * tau_1n + normal_heat_flux367return SVector(flux_inner[1], flux_inner[2], normal_energy_flux)368end369370@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,371<:Isothermal})(flux_inner,372w_inner,373orientation::Integer,374direction,375x,376t,377operator_type::Gradient,378equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})379v1 = boundary_condition.boundary_condition_velocity.boundary_value_function(x, t,380equations)381T = boundary_condition.boundary_condition_heat_flux.boundary_value_function(x, t,382equations)383384# the entropy variables w2 = rho * v1 / p = v1 / T = -v1 * w3.385w3 = -1 / T386return SVector(w_inner[1], -v1 * w3, w3)387end388389@inline function (boundary_condition::BoundaryConditionNavierStokesWall{<:NoSlip,390<:Isothermal})(flux_inner,391w_inner,392orientation::Integer,393direction,394x,395t,396operator_type::Divergence,397equations::CompressibleNavierStokesDiffusion1D{GradientVariablesEntropy})398return SVector(flux_inner[1], flux_inner[2], flux_inner[3])399end400end # @muladd401402403