Physics_NSSA.f90 Source File

This routine returns the maximum eigenvalues for the Euler equations for the given solution value in each spatial direction. These are to be used to compute the local time step.



Source Code

!    
!@mark -
!
#include "Includes.h"
!  **************
   module Physics_NSSA
!  **************
!
      use SMConstants
      use PhysicsStorage_NSSA
      use VariableConversion_NSSA
      use FluidData_NSSA
      use Utilities, only: outer_product
      use SpallartAlmarasTurbulence
      implicit none

      private
      public  EulerFlux 
      public  ViscousFlux_STATE, ViscousFlux_ENTROPY, ViscousFlux_ENERGY
      public  GuermondPopovFlux_ENTROPY
      public  InviscidJacobian
      public  getStressTensor, getFrictionVelocity
!
!     ========
      CONTAINS 
!     ========
!
!     
!
!//////////////////////////////////////////////////////////////////////////////
!
!           INVISCID FLUXES
!           ---------------   
!
!//////////////////////////////////////////////////////////////////////////////
!
      pure subroutine EulerFlux(Q, F, rho_)
         implicit none
         real(kind=RP), intent(in)   :: Q(1:NCONS)
         real(kind=RP), intent(out)  :: F(1:NCONS , 1:NDIM)
         real(kind=RP), intent(in), optional :: rho_
!
!        ---------------
!        Local variables
!        ---------------
!
         real(kind=RP)           :: u , v , w , p

         associate ( gammaMinus1 => thermodynamics % gammaMinus1 ) 

         u = Q(IRHOU) / Q(IRHO)
         v = Q(IRHOV) / Q(IRHO)
         w = Q(IRHOW) / Q(IRHO)
         p = gammaMinus1 * (Q(IRHOE) - 0.5_RP * ( Q(IRHOU) * u + Q(IRHOV) * v + Q(IRHOW) * w ) )
!
!        X-Flux
!        ------         
         F(IRHO , IX ) = Q(IRHOU)
         F(IRHOU, IX ) = Q(IRHOU) * u + p
         F(IRHOV, IX ) = Q(IRHOU) * v
         F(IRHOW, IX ) = Q(IRHOU) * w
         F(IRHOE, IX ) = ( Q(IRHOE) + p ) * u
!
!        Y-Flux
!        ------
         F(IRHO , IY ) = Q(IRHOV)
         F(IRHOU ,IY ) = F(IRHOV,IX)
         F(IRHOV ,IY ) = Q(IRHOV) * v + p
         F(IRHOW ,IY ) = Q(IRHOV) * w
         F(IRHOE ,IY ) = ( Q(IRHOE) + p ) * v
!
!        Z-Flux
!        ------
         F(IRHO ,IZ) = Q(IRHOW)
         F(IRHOU,IZ) = F(IRHOW,IX)
         F(IRHOV,IZ) = F(IRHOW,IY)
         F(IRHOW,IZ) = Q(IRHOW) * w + P
         F(IRHOE,IZ) = ( Q(IRHOE) + p ) * w

         F(IRHOTHETA, IX ) =  Q(IRHOTHETA) * u
         F(IRHOTHETA, IY ) =  Q(IRHOTHETA) * v
         F(IRHOTHETA, IZ ) =  Q(IRHOTHETA) * w
      
         end associate

      end subroutine EulerFlux
!     -------------------------------------------------------------------------------
!     Subroutine for computing the Jacobian of the inviscid flux when it has the form 
!
!        F = f*iHat + g*jHat + h*kHat
!
!     First index indicates the flux term and second index indicates the conserved 
!     variable term. For example:
!           dfdq     := df/dq
!                       d f(2) |
!           dfdq(2,4) = ------ |
!                       d q(4) |q
!     ***** This routine is necessary for computing the analytical Jacobian. *****
!     -------------------------------------------------------------------------------
      pure subroutine InviscidJacobian(q,dfdq,dgdq,dhdq)
         implicit none
         !-------------------------------------------------
         real(kind=RP), intent (in)  :: q(NCONS)
         real(kind=RP), intent (out) :: dfdq(NCONS,NCONS)
         real(kind=RP), intent (out) :: dgdq(NCONS,NCONS)
         real(kind=RP), intent (out) :: dhdq(NCONS,NCONS)
         !-------------------------------------------------
         real(kind=RP)  :: u,v,w ! Velocity components
         real(kind=RP)  :: V2    ! Total velocity squared
         real(kind=RP)  :: p     ! Pressure
         real(kind=RP)  :: H     ! Total enthalpy
         !-------------------------------------------------
         
         associate( gammaMinus1 => thermodynamics % gammaMinus1, & 
                    gamma => thermodynamics % gamma )
         
         u  = q(IRHOU) / q(IRHO)
         v  = q(IRHOV) / q(IRHO)
         w  = q(IRHOW) / q(IRHO)
         V2 = u*u + v*v + w*w
         p  = Pressure(q)
         H  = (q(IRHOE) + p) / q(IRHO)
!
!        Flux in the x direction (f)
!        ---------------------------

         dfdq(1,1) = 0._RP
         dfdq(1,2) = 1._RP
         dfdq(1,3) = 0._RP
         dfdq(1,4) = 0._RP
         dfdq(1,5) = 0._RP
         
         dfdq(2,1) = -u*u + 0.5_RP*gammaMinus1*V2
         dfdq(2,2) = (3._RP - gamma) * u
         dfdq(2,3) = -gammaMinus1 * v
         dfdq(2,4) = -gammaMinus1 * w
         dfdq(2,5) = gammaMinus1
         
         dfdq(3,1) = -u*v
         dfdq(3,2) = v
         dfdq(3,3) = u
         dfdq(3,4) = 0._RP
         dfdq(3,5) = 0._RP
         
         dfdq(4,1) = -u*w
         dfdq(4,2) = w
         dfdq(4,3) = 0._RP
         dfdq(4,4) = u
         dfdq(4,5) = 0._RP
         
         dfdq(5,1) = u * (0.5_RP*gammaMinus1*V2 - H)
         dfdq(5,2) = H - gammaMinus1 * u*u
         dfdq(5,3) = -gammaMinus1 * u*v
         dfdq(5,4) = -gammaMinus1 * u*w
         dfdq(5,5) = gamma * u
         
!
!        Flux in the y direction (g)
!        ---------------------------
         
         dgdq(1,1) = 0._RP
         dgdq(1,2) = 0._RP
         dgdq(1,3) = 1._RP
         dgdq(1,4) = 0._RP
         dgdq(1,5) = 0._RP
         
         dgdq(2,1) = -u*v
         dgdq(2,2) = v
         dgdq(2,3) = u
         dgdq(2,4) = 0._RP
         dgdq(2,5) = 0._RP
         
         dgdq(3,1) = -v*v + 0.5_RP*gammaMinus1*V2
         dgdq(3,2) = -gammaMinus1 * u
         dgdq(3,3) = (3._RP - gamma) * v
         dgdq(3,4) = -gammaMinus1 * w
         dgdq(3,5) = gammaMinus1
         
         dgdq(4,1) = -v*w
         dgdq(4,2) = 0._RP
         dgdq(4,3) = w
         dgdq(4,4) = v
         dgdq(4,5) = 0._RP
         
         dgdq(5,1) = v * (0.5_RP*gammaMinus1*V2 - H)
         dgdq(5,2) = -gammaMinus1 * u*v
         dgdq(5,3) = H - gammaMinus1 * v*v
         dgdq(5,4) = -gammaMinus1 * v*w
         dgdq(5,5) = gamma * v
!
!        Flux in the z direction (h)
!        ---------------------------
         
         dhdq(1,1) = 0._RP
         dhdq(1,2) = 0._RP
         dhdq(1,3) = 0._RP
         dhdq(1,4) = 1._RP
         dhdq(1,5) = 0._RP
         
         dhdq(2,1) = -u*w
         dhdq(2,2) = w
         dhdq(2,3) = 0._RP
         dhdq(2,4) = u
         dhdq(2,5) = 0._RP
         
         dhdq(3,1) = -v*w
         dhdq(3,2) = 0._RP
         dhdq(3,3) = w
         dhdq(3,4) = v
         dhdq(3,5) = 0._RP
         
         dhdq(4,1) = -w*w + 0.5_RP*gammaMinus1*V2
         dhdq(4,2) = -gammaMinus1 * u
         dhdq(4,3) = -gammaMinus1 * v
         dhdq(4,4) = (3._RP - gamma) * w
         dhdq(4,5) = gammaMinus1
         
         dhdq(5,1) = w * (0.5_RP*gammaMinus1*V2 - H)
         dhdq(5,2) = -gammaMinus1 * u*w
         dhdq(5,3) = -gammaMinus1 * v*w
         dhdq(5,4) = H - gammaMinus1 * w*w
         dhdq(5,5) = gamma * w
         
         end associate
         
      end subroutine InviscidJacobian
!
!//////////////////////////////////////////////////////////////////////////////////////////
!
!>        VISCOUS FLUXES
!         --------------
!
!//////////////////////////////////////////////////////////////////////////////////////////
!
      pure subroutine ViscousFlux_STATE(nEqn, nGradEqn, Q, Q_x, Q_y, Q_z, mu, beta, kappa, F)
         implicit none
         integer,       intent(in)  :: nEqn
         integer,       intent(in)  :: nGradEqn
         real(kind=RP), intent(in)  :: Q   (1:nEqn     )
         real(kind=RP), intent(in)  :: Q_x (1:nGradEqn)
         real(kind=RP), intent(in)  :: Q_y (1:nGradEqn)
         real(kind=RP), intent(in)  :: Q_z (1:nGradEqn)
         real(kind=RP), intent(in)  :: mu
         real(kind=RP), intent(in)  :: beta
         real(kind=RP), intent(in)  :: kappa
         real(kind=RP), intent(out) :: F(1:nEqn, 1:NDIM)
!
!        ---------------
!        Local variables
!        ---------------
!
         real(kind=RP)                    :: divV
         real(kind=RP)                    :: u , v , w
         real(kind=RP)                    :: invRho, uDivRho(NDIM), u_x(NDIM), u_y(NDIM), u_z(NDIM), nablaT(NDIM)
         real(kind=RP)                    :: theta, thetaDivRho, theta_x, theta_y,theta_z
         
         invRho  = 1.0_RP / Q(IRHO)

         u = Q(IRHOU) * invRho
         v = Q(IRHOV) * invRho
         w = Q(IRHOW) * invRho
         
         uDivRho = [u * invRho, v * invRho, w * invRho]
         
         u_x = invRho * Q_x(IRHOU:IRHOW) - uDivRho * Q_x(IRHO)
         u_y = invRho * Q_y(IRHOU:IRHOW) - uDivRho * Q_y(IRHO)
         u_z = invRho * Q_z(IRHOU:IRHOW) - uDivRho * Q_z(IRHO)
                  
         nablaT(IX) = thermodynamics % gammaMinus1*dimensionless % gammaM2*(invRho*Q_x(IRHOE) - Q(IRHOE)*invRho*invRho*Q_x(IRHO) - u*u_x(IX)-v*u_x(IY)-w*u_x(IZ))
         nablaT(IY) = thermodynamics % gammaMinus1*dimensionless % gammaM2*(invRho*Q_y(IRHOE) - Q(IRHOE)*invRho*invRho*Q_y(IRHO) - u*u_y(IX)-v*u_y(IY)-w*u_y(IZ))
         nablaT(IZ) = thermodynamics % gammaMinus1*dimensionless % gammaM2*(invRho*Q_z(IRHOE) - Q(IRHOE)*invRho*invRho*Q_z(IRHO) - u*u_z(IX)-v*u_z(IY)-w*u_z(IZ))
         
         divV = U_x(IX) + U_y(IY) + U_z(IZ)

         F(IRHO,IX)  = 0.0_RP
         F(IRHOU,IX) = mu  * (2.0_RP * U_x(IX) - 2.0_RP/3.0_RP * divV ) !+ beta * divV
         F(IRHOV,IX) = mu  * ( U_x(IY) + U_y(IX) ) 
         F(IRHOW,IX) = mu  * ( U_x(IZ) + U_z(IX) ) 
         F(IRHOE,IX) = F(IRHOU,IX) * u + F(IRHOV,IX) * v + F(IRHOW,IX) * w + kappa  * nablaT(IX) 

         F(IRHO,IY) = 0.0_RP
         F(IRHOU,IY) = F(IRHOV,IX) 
         F(IRHOV,IY) = mu  * (2.0_RP * U_y(IY) - 2.0_RP / 3.0_RP * divV ) !+ beta * divV
         F(IRHOW,IY) = mu  * ( U_y(IZ) + U_z(IY) ) 
         F(IRHOE,IY) = F(IRHOU,IY) * u + F(IRHOV,IY) * v + F(IRHOW,IY) * w + kappa  * nablaT(IY)

         F(IRHO,IZ) = 0.0_RP
         F(IRHOU,IZ) = F(IRHOW,IX) 
         F(IRHOV,IZ) = F(IRHOW,IY) 
         F(IRHOW,IZ) = mu  * ( 2.0_RP * U_z(IZ) - 2.0_RP / 3.0_RP * divV ) !+ beta * divV
         F(IRHOE,IZ) = F(IRHOU,IZ) * u + F(IRHOV,IZ) * v + F(IRHOW,IZ) * w + kappa  * nablaT(IZ)

         theta = Q(IRHOTHETA) * invRho
         thetaDivRho = theta * invRho
         theta_x = invRho * Q_x(IRHOTHETA) - thetaDivRho * Q_x(IRHO)
         theta_y = invRho * Q_y(IRHOTHETA) - thetaDivRho * Q_y(IRHO)
         theta_z = invRho * Q_z(IRHOTHETA) - thetaDivRho * Q_z(IRHO)
         F(IRHOTHETA,IX) = theta_x * beta
         F(IRHOTHETA,IY) = theta_y * beta
         F(IRHOTHETA,IZ) = theta_z * beta

         ! with Pr = constant, dmudx = dkappadx
      end subroutine ViscousFlux_STATE
      
      pure subroutine ViscousFlux_ENTROPY(nEqn, nGradEqn, Q, Q_x, Q_y, Q_z, mu, beta, kappa, F)
         implicit none
         integer,       intent(in)  :: nEqn
         integer,       intent(in)  :: nGradEqn
         real(kind=RP), intent(in)  :: Q   (1:nEqn     )
         real(kind=RP), intent(in)  :: Q_x (1:nGradEqn)
         real(kind=RP), intent(in)  :: Q_y (1:nGradEqn)
         real(kind=RP), intent(in)  :: Q_z (1:nGradEqn)
         real(kind=RP), intent(in)  :: mu
         real(kind=RP), intent(in)  :: beta
         real(kind=RP), intent(in)  :: kappa
         real(kind=RP), intent(out) :: F(1:nEqn, 1:NDIM)
!
!        ---------------
!        Local variables
!        ---------------
!
         real(kind=RP)                    :: divV, p_div_rho
         real(kind=RP)                    :: u(NDIM)
         real(kind=RP)                    :: invRho, uDivRho(NDIM), u_x(NDIM), u_y(NDIM), u_z(NDIM), nablaT(NDIM)

         invRho  = 1.0_RP / Q(IRHO)
         p_div_rho = thermodynamics % gammaMinus1*invRho*(Q(IRHOE)-0.5_RP*(POW2(Q(IRHOU))+POW2(Q(IRHOV))+POW2(Q(IRHOW)))*invRho)

         u = Q(IRHOU:IRHOW)*invRho
         u_x = p_div_rho * (Q_x(IRHOU:IRHOW) + u*Q_x(IRHOE))
         u_y = p_div_rho * (Q_y(IRHOU:IRHOW) + u*Q_y(IRHOE))
         u_z = p_div_rho * (Q_z(IRHOU:IRHOW) + u*Q_z(IRHOE))
         
         nablaT(IX) = dimensionless % gammaM2*POW2(p_div_rho)*Q_x(IRHOE)
         nablaT(IY) = dimensionless % gammaM2*POW2(p_div_rho)*Q_y(IRHOE)
         nablaT(IZ) = dimensionless % gammaM2*POW2(p_div_rho)*Q_z(IRHOE)
         
         divV = U_x(IX) + U_y(IY) + U_z(IZ)

         F(IRHO,IX)  = 0.0_RP
         F(IRHOU,IX) = mu  * (2.0_RP * U_x(IX) - 2.0_RP/3.0_RP * divV ) + beta * divV
         F(IRHOV,IX) = mu  * ( U_x(IY) + U_y(IX) ) 
         F(IRHOW,IX) = mu  * ( U_x(IZ) + U_z(IX) ) 
         F(IRHOE,IX) = F(IRHOU,IX) * u(IX) + F(IRHOV,IX) * u(IY) + F(IRHOW,IX) * u(IZ) + kappa  * nablaT(IX) 

         F(IRHO,IY) = 0.0_RP
         F(IRHOU,IY) = F(IRHOV,IX) 
         F(IRHOV,IY) = mu  * (2.0_RP * U_y(IY) - 2.0_RP / 3.0_RP * divV ) + beta * divV
         F(IRHOW,IY) = mu  * ( U_y(IZ) + U_z(IY) ) 
         F(IRHOE,IY) = F(IRHOU,IY) * u(IX) + F(IRHOV,IY) * u(IY) + F(IRHOW,IY) * u(IZ) + kappa  * nablaT(IY)

         F(IRHO,IZ) = 0.0_RP
         F(IRHOU,IZ) = F(IRHOW,IX) 
         F(IRHOV,IZ) = F(IRHOW,IY) 
         F(IRHOW,IZ) = mu  * ( 2.0_RP * U_z(IZ) - 2.0_RP / 3.0_RP * divV ) + beta * divV
         F(IRHOE,IZ) = F(IRHOU,IZ) * u(IX) + F(IRHOV,IZ) * u(IY) + F(IRHOW,IZ) * u(IZ) + kappa  * nablaT(IZ)

      end subroutine ViscousFlux_ENTROPY

      pure subroutine ViscousFlux_ENERGY(nEqn, nGradEqn, Q, Q_x, Q_y, Q_z, mu, beta, kappa, F)
         implicit none
         integer,       intent(in)  :: nEqn
         integer,       intent(in)  :: nGradEqn
         real(kind=RP), intent(in)  :: Q   (1:nEqn     )
         real(kind=RP), intent(in)  :: Q_x (1:nGradEqn)
         real(kind=RP), intent(in)  :: Q_y (1:nGradEqn)
         real(kind=RP), intent(in)  :: Q_z (1:nGradEqn)
         real(kind=RP), intent(in)  :: mu
         real(kind=RP), intent(in)  :: beta
         real(kind=RP), intent(in)  :: kappa
         real(kind=RP), intent(out) :: F(1:nEqn, 1:NDIM)
!
!        ---------------
!        Local variables
!        ---------------
!
         real(kind=RP)                    :: divV, p_div_rho
         real(kind=RP)                    :: u(NDIM)
         real(kind=RP)                    :: invRho, uDivRho(NDIM), u_x(NDIM), u_y(NDIM), u_z(NDIM), nablaT(NDIM)

         invRho  = 1.0_RP / Q(IRHO)
         p_div_rho = thermodynamics % gammaMinus1*invRho*(Q(IRHOE)-0.5_RP*(POW2(Q(IRHOU))+POW2(Q(IRHOV))+POW2(Q(IRHOW)))*invRho)
         u = Q(IRHOU:IRHOW)*invRho

         u_x = Q_x(IRHOU:IRHOW)
         u_y = Q_y(IRHOU:IRHOW) 
         u_z = Q_z(IRHOU:IRHOW)
         
         nablaT(IX) = Q_x(IRHOE)
         nablaT(IY) = Q_y(IRHOE)
         nablaT(IZ) = Q_z(IRHOE)
         
         divV = U_x(IX) + U_y(IY) + U_z(IZ)

         F(IRHO,IX)  = 0.0_RP
         F(IRHOU,IX) = mu  * (2.0_RP * U_x(IX) - 2.0_RP/3.0_RP * divV ) + beta * divV
         F(IRHOV,IX) = mu  * ( U_x(IY) + U_y(IX) ) 
         F(IRHOW,IX) = mu  * ( U_x(IZ) + U_z(IX) ) 
         F(IRHOE,IX) = F(IRHOU,IX) * u(IX) + F(IRHOV,IX) * u(IY) + F(IRHOW,IX) * u(IZ) + kappa  * nablaT(IX) 

         F(IRHO,IY) = 0.0_RP
         F(IRHOU,IY) = F(IRHOV,IX) 
         F(IRHOV,IY) = mu  * (2.0_RP * U_y(IY) - 2.0_RP / 3.0_RP * divV ) + beta * divV
         F(IRHOW,IY) = mu  * ( U_y(IZ) + U_z(IY) ) 
         F(IRHOE,IY) = F(IRHOU,IY) * u(IX) + F(IRHOV,IY) * u(IY) + F(IRHOW,IY) * u(IZ) + kappa  * nablaT(IY)

         F(IRHO,IZ) = 0.0_RP
         F(IRHOU,IZ) = F(IRHOW,IX) 
         F(IRHOV,IZ) = F(IRHOW,IY) 
         F(IRHOW,IZ) = mu  * ( 2.0_RP * U_z(IZ) - 2.0_RP / 3.0_RP * divV ) + beta * divV
         F(IRHOE,IZ) = F(IRHOU,IZ) * u(IX) + F(IRHOV,IZ) * u(IY) + F(IRHOW,IZ) * u(IZ) + kappa  * nablaT(IZ)

      end subroutine ViscousFlux_ENERGY

      pure subroutine GuermondPopovFlux_ENTROPY(nEqn, nGradEqn, Q, Q_x, Q_y, Q_z, mu, beta, kappa, F)
!
!        //////////////////////////////////////////////////////////////////////////////
!
!           Implementation of the Guermond-Popov fluxes (2014): "Viscous 
!           Regularization of the Euler Equations and Entropy Principles"
!
!           The fluxes are:
!
!              FGP = κ[ ∇ρ, u∇ρ, v∇ρ, w∇ρ,∇(ρe_i) + 0.5|v|²∇ρ] + μρ[0, ∇ˢv,v·∇ˢv]
!
!           where ρe_i=p/(γ-1) is the internal energy.
!
!           Because there is no reason to do othersise, we use κ=μ.
!
!           In the gradient of the entropy variables, the fluxes are: FGP = (κρB_κ+μpB_μ/2)G
!     
!            |---------------------|-----------------------|-----------------------|
!            | 1  u   v   w   e    |  0   0   0   0   0    |  0   0   0   0   0    |
!            | u  uu  uv  uw  eu   |  0   0   0   0   0    |  0   0   0   0   0    |
!            | v  uv  vv  vw  ev   |  0   0   0   0   0    |  0   0   0   0   0    |   
!            | w  uw  vw  ww  ew   |  0   0   0   0   0    |  0   0   0   0   0    |   
!            | e  eu  ev  ew ee+ΛΛ |  0   0   0   0   0    |  0   0   0   0   0    |   
!            |---------------------|-----------------------|-----------------------|
!            | 0   0   0   0   0   |  1   u   v   w   e    |  0   0   0   0   0    |
!            | 0   0   0   0   0   |  u   uu  uv  uw  eu   |  0   0   0   0   0    |
!       B_κ= | 0   0   0   0   0   |  v   uv  vv  vw  ev   |  0   0   0   0   0    |   
!            | 0   0   0   0   0   |  w   uw  vw  ww  ew   |  0   0   0   0   0    |   
!            | 0   0   0   0   0   |  e   eu  ev  ew ee+ΛΛ |  0   0   0   0   0    |   
!            |---------------------|-----------------------|-----------------------|
!            | 0   0   0   0   0   |  0   0   0   0   0    |  1   u   v   w   e    |
!            | 0   0   0   0   0   |  0   0   0   0   0    |  u   uu  uv  uw  eu   |
!            | 0   0   0   0   0   |  0   0   0   0   0    |  v   uv  vv  vw  ev   |   
!            | 0   0   0   0   0   |  0   0   0   0   0    |  w   uw  vw  ww  ew   |   
!            | 0   0   0   0   0   |  0   0   0   0   0    |  e   eu  ev  ew ee+ΛΛ |   
!            |---------------------|-----------------------|-----------------------|
!
!        Λ=(p/ρ)/√(γ-1)
!
!        and
!     
!            |---------------------|-----------------------|-----------------------|
!            | 0  0   0   0   0    |  0   0   0   0   0    |  0   0   0   0   0    |
!            | 0  2   0   0   2u   |  0   0   0   0   0    |  0   0   0   0   0    |
!            | 0  0   1   0   v    |  0   1   0   0   u    |  0   0   0   0   0    |   
!            | 0  0   0   1   w    |  0   0   0   0   0    |  0   1   0   0   u    |   
!            | 0  2u  v   w uu+vt² |  0   v   0   0   uv   |  0   w   0   0   uw   |   
!            |---------------------|-----------------------|-----------------------|
!            | 0   0   0   0   0   |  0   0   0   0   0    |   0   0   0   0   0   |
!            | 0   0   1   0   v   |  0   1   0   0   u    |   0   0   0   0   0   |
!       B_μ= | 0   0   0   0   0   |  v   0   2   0   2v   |   0   0   0   0   0   |   
!            | 0   0   0   0   0   |  w   0   0   1   w    |   0   0   1   0   v   |   
!            | 0   0   u   0   uv  |  e   u   2v  w vv+vt² |   0   0   w   0   vw  |   
!            |---------------------|-----------------------|-----------------------|
!            | 0   0   0   0   0   |  0   0   0   0   0    |  0   0   0   0   0    |
!            | 0   0   0   1   w   |  0   0   0   0   0    |  0   1   0   0   u    |
!            | 0   0   0   0   0   |  0   0   1   0   w    |  v   0   1   0   v    |   
!            | 0   0   0   0   0   |  0   0   0   0   0    |  w   0   0   2   2w   |   
!            | 0   0   0   u   uw  |  0   0   v   0   vw   |  e   u   v  2w ww+vt² |   
!            |---------------------|-----------------------|-----------------------|
!
!        where vt²=uu+vv+ww.
!
         implicit none
         integer,       intent(in)  :: nEqn
         integer,       intent(in)  :: nGradEqn
         real(kind=RP), intent(in)  :: Q   (1:nEqn     )
         real(kind=RP), intent(in)  :: Q_x (1:nGradEqn)
         real(kind=RP), intent(in)  :: Q_y (1:nGradEqn)
         real(kind=RP), intent(in)  :: Q_z (1:nGradEqn)
         real(kind=RP), intent(in)  :: mu
         real(kind=RP), intent(in)  :: beta
         real(kind=RP), intent(in)  :: kappa
         real(kind=RP), intent(out) :: F(1:nEqn, 1:NDIM)
!
!        ---------------
!        Local variables
!        ---------------
!
         real(kind=RP) :: invRho, p_div_rho, u(NDIM), u_x(NDIM), u_y(NDIM), u_z(NDIM), e
         real(kind=RP) :: grad_rho(NDIM)

         invRho  = 1.0_RP / Q(IRHO)
         p_div_rho = thermodynamics % gammaMinus1*invRho*(Q(IRHOE)-0.5_RP*(POW2(Q(IRHOU))+POW2(Q(IRHOV))+POW2(Q(IRHOW)))*invRho)

         u = Q(IRHOU:IRHOW)*invRho
         u_x = p_div_rho * (Q_x(IRHOU:IRHOW) + u*Q_x(IRHOE))
         u_y = p_div_rho * (Q_y(IRHOU:IRHOW) + u*Q_y(IRHOE))
         u_z = p_div_rho * (Q_z(IRHOU:IRHOW) + u*Q_z(IRHOE))

         e = Q(IRHOE)*invRho

         grad_rho(IX) = sum(Q*Q_x)
         grad_rho(IY) = sum(Q*Q_y)
         grad_rho(IZ) = sum(Q*Q_z)
!
!        Add the part related to ∇ˢv
         F(IRHO, IX) = 0.0_RP
         F(IRHOU,IX) = Q(IRHO)*u_x(IX)
         F(IRHOV,IX) = Q(IRHO)*0.5_RP*(u_x(IY)+u_y(IX))
         F(IRHOW,IX) = Q(IRHO)*0.5_RP*(u_x(IZ)+u_z(IX))
         F(IRHOE,IX) = F(IRHOU,IX)*u(IX) + F(IRHOV,IX)*u(IY) + F(IRHOW,IX)*u(IZ)

         F(IRHO, IY) = 0.0_RP
         F(IRHOU,IY) = F(IRHOV,IX)
         F(IRHOV,IY) = Q(IRHO)*u_y(IY)
         F(IRHOW,IY) = Q(IRHO)*0.5_RP*(u_y(IZ)+u_z(IY))
         F(IRHOE,IY) = F(IRHOU,IY)*u(IX) + F(IRHOV,IY)*u(IY) + F(IRHOW,IY)*u(IZ)

         F(IRHO, IZ) = 0.0_RP
         F(IRHOU,IZ) = F(IRHOW,IX)
         F(IRHOV,IZ) = F(IRHOW,IY)
         F(IRHOW,IZ) = Q(IRHO)*u_z(IZ)
         F(IRHOE,IZ) = F(IRHOU,IZ)*u(IX) + F(IRHOV,IZ)*u(IY) + F(IRHOW,IZ)*u(IZ)

!
!        Add the part related to ∇ρ
         F(:,IX)     = F(:,IX) + grad_rho(IX)*[1.0_RP,u(IX),u(IY),u(IZ),e]
         F(IRHOE,IX) = F(IRHOE,IX) + Q(IRHO)*p_div_rho*p_div_rho*dimensionless % cv*Q_x(IRHOE)

         F(:,IY)     = F(:,IY) + grad_rho(IY)*[1.0_RP,u(IX),u(IY),u(IZ),e]
         F(IRHOE,IY) = F(IRHOE,IY) + Q(IRHO)*p_div_rho*p_div_rho*dimensionless % cv*Q_y(IRHOE)

         F(:,IZ)     = F(:,IZ) + grad_rho(IZ)*[1.0_RP,u(IX),u(IY),u(IZ),e]
         F(IRHOE,IZ) = F(IRHOE,IZ) + Q(IRHO)*p_div_rho*p_div_rho*dimensionless % cv*Q_z(IRHOE)
!
!        Multiply by μ 
         F = mu*F

      end subroutine GuermondPopovFlux_ENTROPY
!
!///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
!
!
!     ------------------------------------------------------------------------------------------
!     Subroutine for computing the Jacobians of the viscous fluxes 
!
!     1. Jacobian with respect to the gradients of the conserved variables: df/d(∇q)
!        Every direction of the viscous flux can be expressed as
!
!        f_i = \sum_j df_dgradq(:,:,j,i) dq/dx_j
!
!        In Navier-Stokes, this dependence is linear. Therefore:
!
!              df_dgradq         := df/d(∇q)
!                                   d f_i(2)   |
!              df_dgradq(2,4,j,i) = ---------- |
!                                   d(∇q)_j(4) |q=cons,
!        where (∇q)_j = dq/dx_j
!        
!        Following Hartmann's notation, G_{ij} = df_dgradq(:,:,j,i). --> R. Hartmann. "Discontinuous Galerkin methods for compressible flows: higher order accuracy, error estimation and adaptivity". 2005.
!
!     2. Jacobian with respect to the conserved variables: df/dq
!
!              df_dq       := df/d(∇q)
!                             d f_i(2) |
!              df_dq(2,4,i) = -------- |
!                             dq(4)    |∇q=cons
!
!     NOTE 1: Here the thermal conductivity and the viscosity are computed using Sutherland's law!     
!     NOTE 2: The dependence of the temperature on q is not considered in Sutherland's law
!
!     ***** This routine is necessary for computing the analytical Jacobian. *****
!     ------------------------------------------------------------------------------------------

!///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
!
      subroutine getStressTensor(Q,Q_x,Q_y,Q_z,tau)
         implicit none
         real(kind=RP), intent(in)      :: Q   (1:NCONS         )
         real(kind=RP), intent(in)      :: Q_x (1:NGRAD    )
         real(kind=RP), intent(in)      :: Q_y (1:NGRAD    )
         real(kind=RP), intent(in)      :: Q_z (1:NGRAD    )
         real(kind=RP), intent(out)     :: tau (1:NDIM, 1:NDIM   )
!
!        ---------------
!        Local variables
!        ---------------
!
         real(kind=RP) :: T , muOfT
         real(kind=RP) :: divV, p_div_rho
         real(kind=RP) :: U_x(NDIM), U_y(NDIM), U_z(NDIM), invRho, invRho2, uDivRho(NDIM), u(NDIM)
         real(kind=RP) :: kinematicviscocity, musa, etasa
         type(Spalart_Almaras_t)       :: SAModel 

         associate ( mu0 => dimensionless % mu )

         select case(grad_vars)
         case(GRADVARS_STATE)

            invRho  = 1._RP / Q(IRHO)
            invRho2 = invRho * invRho
            
            uDivRho = [Q(IRHOU) , Q(IRHOV) , Q(IRHOW) ] * invRho2

            u_x = invRho * Q_x(IRHOU:IRHOW) - uDivRho * Q_x(IRHO)
            u_y = invRho * Q_y(IRHOU:IRHOW) - uDivRho * Q_y(IRHO)
            u_z = invRho * Q_z(IRHOU:IRHOW) - uDivRho * Q_z(IRHO)
            
         case(GRADVARS_ENERGY)
            u_x = Q_x(IRHOU:IRHOW)
            u_y = Q_y(IRHOU:IRHOW)
            u_z = Q_z(IRHOU:IRHOW)
   

         case(GRADVARS_ENTROPY)
            invRho  = 1.0_RP / Q(IRHO)
            p_div_rho = thermodynamics % gammaMinus1*invRho*(Q(IRHOE)-0.5_RP*(POW2(Q(IRHOU))+POW2(Q(IRHOV))+POW2(Q(IRHOW)))*invRho)
   
            u = Q(IRHOU:IRHOW)*invRho
            u_x = p_div_rho * (Q_x(IRHOU:IRHOW) + u*Q_x(IRHOE))
            u_y = p_div_rho * (Q_y(IRHOU:IRHOW) + u*Q_y(IRHOE))
            u_z = p_div_rho * (Q_z(IRHOU:IRHOW) + u*Q_z(IRHOE))

         end select

         T     = Temperature(Q)
         muOfT = SutherlandsLaw(T)
         call GetNSKinematicViscosity(muOfT, Q(IRHO), kinematicviscocity )
         call SAModel % ComputeViscosity( Q(IRHOTHETA), kinematicviscocity, Q(IRHO), muOfT, musa, etasa)
         divV = U_x(IX) + U_y(IY) + U_z(IZ)

         tau(IX,IX) = mu0 * (muOfT+musa) * (2.0_RP * U_x(IX) - 2.0_RP/3.0_RP * divV )
         tau(IY,IX) = mu0 * (muOfT+musa) * ( U_x(IY) + U_y(IX) ) 
         tau(IZ,IX) = mu0 * (muOfT+musa) * ( U_x(IZ) + U_z(IX) ) 
         tau(IX,IY) = tau(IY,IX)
         tau(IY,IY) = mu0 * (muOfT+musa) * (2.0_RP * U_y(IY) - 2.0_RP/3.0_RP * divV )
         tau(IZ,IY) = mu0 * (muOfT+musa) * ( U_y(IZ) + U_z(IY) ) 
         tau(IX,IZ) = tau(IZ,IX)
         tau(IY,IZ) = tau(IZ,IY)
         tau(IZ,IZ) = mu0 * (muOfT+musa) * (2.0_RP * U_z(IZ) - 2.0_RP/3.0_RP * divV )

         end associate

      end subroutine getStressTensor

      Subroutine getFrictionVelocity(Q,Q_x,Q_y,Q_z,normal,tangent,u_tau)
         implicit none
         real(kind=RP), intent(in)      :: Q   (1:NCONS   )
         real(kind=RP), intent(in)      :: Q_x (1:NGRAD   )
         real(kind=RP), intent(in)      :: Q_y (1:NGRAD   )
         real(kind=RP), intent(in)      :: Q_z (1:NGRAD   )
         real(kind=RP), intent(in)      :: normal (1:NDIM )
         real(kind=RP), intent(in)      :: tangent (1:NDIM )
         real(kind=RP), intent(out)     :: u_tau

!
!        ---------------
!        Local variables
!        ---------------
!
         real(kind=RP)                  :: tau (1:NDIM, 1:NDIM   )
         real(kind=RP)                  :: tau_w_vec(1:NDIM)
         real(kind=RP)                  :: tau_w

         call getStressTensor(Q, Q_x, Q_y, Q_z, tau)
         tau_w_vec = matmul(tau, normal)
         tau_w = dot_product(tau_w_vec, tangent)

         ! get the value of the friction velocity with the sign of the wall shear stress, so that the shear stress can be fully
         ! recovered if needed
         u_tau = sqrt(abs(tau_w) / Q(IRHO)) * sign(1.0_RP, tau_w)

      End Subroutine getFrictionVelocity
   END Module Physics_NSSA
!@mark -
!
!----------------------------------------------------------------------
!! This routine returns the maximum eigenvalues for the Euler equations 
!! for the given solution value in each spatial direction. 
!! These are to be used to compute the local time step.
!----------------------------------------------------------------------
!
      SUBROUTINE ComputeEigenvaluesForStateSA( Q, eigen )
      
      USE SMConstants
      USE PhysicsStorage_NSSA
      USE VariableConversion_NSSA, ONLY:Pressure
      use FluidData_NSSA,          only: Thermodynamics
      IMPLICIT NONE
!
!     ---------
!     Arguments
!     ---------
!
      REAL(KIND=Rp), DIMENSION(NCONS) :: Q
      REAL(KIND=Rp), DIMENSION(3)     :: eigen
!
!     ---------------
!     Local Variables
!     ---------------
!
      REAL(KIND=Rp) :: u, v, w, p, a
!      
      associate ( gamma => thermodynamics % gamma ) 

      u = ABS( Q(2)/Q(1) )
      v = ABS( Q(3)/Q(1) )
      w = ABS( Q(4)/Q(1) )
      p = Pressure(Q)
      a = SQRT(gamma*p/Q(1))
      

      eigen(1) = u + a
      eigen(2) = v + a
      eigen(3) = w + a

      end associate
      
      END SUBROUTINE ComputeEigenvaluesForStateSA
! /////////////////////////////////////////////////////////////////////