Path: blob/main/src/equations/linearized_euler_2d.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"""8LinearizedEulerEquations2D(v_mean_global, c_mean_global, rho_mean_global)910Linearized Euler equations in two space dimensions. The equations are given by11```math12\partial_t13\begin{pmatrix}14\rho' \\ v_1' \\ v_2' \\ p'15\end{pmatrix}16+17\partial_x18\begin{pmatrix}19\bar{\rho} v_1' + \bar{v_1} \rho ' \\ \bar{v_1} v_1' + \frac{p'}{\bar{\rho}} \\ \bar{v_1} v_2' \\ \bar{v_1} p' + c^2 \bar{\rho} v_1'20\end{pmatrix}21+22\partial_y23\begin{pmatrix}24\bar{\rho} v_2' + \bar{v_2} \rho ' \\ \bar{v_2} v_1' \\ \bar{v_2} v_2' + \frac{p'}{\bar{\rho}} \\ \bar{v_2} p' + c^2 \bar{\rho} v_2'25\end{pmatrix}26=27\begin{pmatrix}280 \\ 0 \\ 0 \\ 029\end{pmatrix}30```31The bar ``\bar{(\cdot)}`` indicates uniform mean flow variables and ``c`` is the speed of sound.32The unknowns are the acoustic velocities ``v' = (v_1', v_2')``, the pressure ``p'`` and the density ``\rho'``.33"""34struct LinearizedEulerEquations2D{RealT <: Real} <:35AbstractLinearizedEulerEquations{2, 4}36v_mean_global::SVector{2, RealT}37c_mean_global::RealT38rho_mean_global::RealT39end4041function LinearizedEulerEquations2D(v_mean_global::NTuple{2, <:Real},42c_mean_global::Real, rho_mean_global::Real)43if rho_mean_global < 044throw(ArgumentError("rho_mean_global must be non-negative"))45elseif c_mean_global < 046throw(ArgumentError("c_mean_global must be non-negative"))47end4849return LinearizedEulerEquations2D(SVector(v_mean_global), c_mean_global,50rho_mean_global)51end5253function LinearizedEulerEquations2D(; v_mean_global::NTuple{2, <:Real},54c_mean_global::Real, rho_mean_global::Real)55return LinearizedEulerEquations2D(v_mean_global, c_mean_global,56rho_mean_global)57end5859function varnames(::typeof(cons2cons), ::LinearizedEulerEquations2D)60("rho_prime", "v1_prime", "v2_prime", "p_prime")61end62function varnames(::typeof(cons2prim), ::LinearizedEulerEquations2D)63("rho_prime", "v1_prime", "v2_prime", "p_prime")64end6566"""67initial_condition_convergence_test(x, t, equations::LinearizedEulerEquations2D)6869A smooth initial condition used for convergence tests.70"""71function initial_condition_convergence_test(x, t, equations::LinearizedEulerEquations2D)72rho_prime = -cospi(2 * t) * (sinpi(2 * x[1]) + sinpi(2 * x[2]))73v1_prime = sinpi(2 * t) * cospi(2 * x[1])74v2_prime = sinpi(2 * t) * cospi(2 * x[2])75p_prime = rho_prime7677return SVector(rho_prime, v1_prime, v2_prime, p_prime)78end7980"""81boundary_condition_wall(u_inner, orientation, direction, x, t, surface_flux_function,82equations::LinearizedEulerEquations2D)8384Boundary conditions for a solid wall.85"""86function boundary_condition_wall(u_inner, orientation, direction, x, t,87surface_flux_function,88equations::LinearizedEulerEquations2D)89# Boundary state is equal to the inner state except for the velocity. For boundaries90# in the -x/+x direction, we multiply the velocity in the x direction by -1.91# Similarly, for boundaries in the -y/+y direction, we multiply the velocity in the92# y direction by -193if direction in (1, 2) # x direction94u_boundary = SVector(u_inner[1], -u_inner[2], u_inner[3], u_inner[4])95else # y direction96u_boundary = SVector(u_inner[1], u_inner[2], -u_inner[3], u_inner[4])97end9899# Calculate boundary flux100if iseven(direction) # u_inner is "left" of boundary, u_boundary is "right" of boundary101flux = surface_flux_function(u_inner, u_boundary, orientation, equations)102else # u_boundary is "left" of boundary, u_inner is "right" of boundary103flux = surface_flux_function(u_boundary, u_inner, orientation, equations)104end105106return flux107end108109# Calculate 1D flux for a single point110@inline function flux(u, orientation::Integer, equations::LinearizedEulerEquations2D)111@unpack v_mean_global, c_mean_global, rho_mean_global = equations112rho_prime, v1_prime, v2_prime, p_prime = u113if orientation == 1114f1 = v_mean_global[1] * rho_prime + rho_mean_global * v1_prime115f2 = v_mean_global[1] * v1_prime + p_prime / rho_mean_global116f3 = v_mean_global[1] * v2_prime117f4 = v_mean_global[1] * p_prime + c_mean_global^2 * rho_mean_global * v1_prime118else119f1 = v_mean_global[2] * rho_prime + rho_mean_global * v2_prime120f2 = v_mean_global[2] * v1_prime121f3 = v_mean_global[2] * v2_prime + p_prime / rho_mean_global122f4 = v_mean_global[2] * p_prime + c_mean_global^2 * rho_mean_global * v2_prime123end124125return SVector(f1, f2, f3, f4)126end127128# Calculate 1D flux for a single point129@inline function flux(u, normal_direction::AbstractVector,130equations::LinearizedEulerEquations2D)131@unpack v_mean_global, c_mean_global, rho_mean_global = equations132rho_prime, v1_prime, v2_prime, p_prime = u133134v_mean_normal = v_mean_global[1] * normal_direction[1] +135v_mean_global[2] * normal_direction[2]136v_prime_normal = v1_prime * normal_direction[1] + v2_prime * normal_direction[2]137138f1 = v_mean_normal * rho_prime + rho_mean_global * v_prime_normal139f2 = v_mean_normal * v1_prime + normal_direction[1] * p_prime / rho_mean_global140f3 = v_mean_normal * v2_prime + normal_direction[2] * p_prime / rho_mean_global141f4 = v_mean_normal * p_prime + c_mean_global^2 * rho_mean_global * v_prime_normal142143return SVector(f1, f2, f3, f4)144end145146@inline have_constant_speed(::LinearizedEulerEquations2D) = True()147148@inline function max_abs_speeds(equations::LinearizedEulerEquations2D)149@unpack v_mean_global, c_mean_global = equations150return abs(v_mean_global[1]) + c_mean_global, abs(v_mean_global[2]) + c_mean_global151end152153@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,154equations::LinearizedEulerEquations2D)155@unpack v_mean_global, c_mean_global = equations156if orientation == 1157return abs(v_mean_global[1]) + c_mean_global158else # orientation == 2159return abs(v_mean_global[2]) + c_mean_global160end161end162163@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,164equations::LinearizedEulerEquations2D)165@unpack v_mean_global, c_mean_global = equations166v_mean_normal = normal_direction[1] * v_mean_global[1] +167normal_direction[2] * v_mean_global[2]168return abs(v_mean_normal) + c_mean_global * norm(normal_direction)169end170171@doc raw"""172flux_godunov(u_ll, u_rr, orientation_or_normal_direction,173equations::LinearizedEulerEquations2D)174175An upwind flux for the linearized Euler equations based on diagonalization of the physical176flux matrix. Given the physical flux ``Au``, ``A=T \Lambda T^{-1}`` with177``\Lambda`` being a diagonal matrix that holds the eigenvalues of ``A``, decompose178``\Lambda = \Lambda^+ + \Lambda^-`` where ``\Lambda^+`` and ``\Lambda^-`` are diagonal179matrices holding the positive and negative eigenvalues of ``A``, respectively. Then for180left and right states ``u_L, u_R``, the numerical flux calculated by this function is given181by ``A^+ u_L + A^- u_R`` where ``A^{\pm} = T \Lambda^{\pm} T^{-1}``.182183The diagonalization of the flux matrix can be found in184- R. F. Warming, Richard M. Beam and B. J. Hyett (1975)185Diagonalization and simultaneous symmetrization of the gas-dynamic matrices186[DOI: 10.1090/S0025-5718-1975-0388967-5](https://doi.org/10.1090/S0025-5718-1975-0388967-5)187"""188@inline function flux_godunov(u_ll, u_rr, orientation::Integer,189equations::LinearizedEulerEquations2D)190@unpack v_mean_global, rho_mean_global, c_mean_global = equations191v1_mean = v_mean_global[1]192v2_mean = v_mean_global[2]193194rho_prime_ll, v1_prime_ll, v2_prime_ll, p_prime_ll = u_ll195rho_prime_rr, v1_prime_rr, v2_prime_rr, p_prime_rr = u_rr196197if orientation == 1198# Eigenvalues of the flux matrix199lambda1 = v1_mean200lambda2 = v1_mean - c_mean_global201lambda3 = v1_mean + c_mean_global202203lambda1_p = positive_part(lambda1)204lambda2_p = positive_part(lambda2)205lambda3_p = positive_part(lambda3)206lambda2p3_half_p = 0.5f0 * (lambda2_p + lambda3_p)207lambda3m2_half_p = 0.5f0 * (lambda3_p - lambda2_p)208209lambda1_m = negative_part(lambda1)210lambda2_m = negative_part(lambda2)211lambda3_m = negative_part(lambda3)212lambda2p3_half_m = 0.5f0 * (lambda2_m + lambda3_m)213lambda3m2_half_m = 0.5f0 * (lambda3_m - lambda2_m)214215f1p = (lambda1_p * rho_prime_ll +216lambda3m2_half_p / c_mean_global * rho_mean_global * v1_prime_ll +217(lambda2p3_half_p - lambda1_p) / c_mean_global^2 * p_prime_ll)218f2p = (lambda2p3_half_p * v1_prime_ll +219lambda3m2_half_p / c_mean_global * p_prime_ll / rho_mean_global)220f3p = lambda1_p * v2_prime_ll221f4p = (lambda3m2_half_p * c_mean_global * rho_mean_global * v1_prime_ll +222lambda2p3_half_p * p_prime_ll)223224f1m = (lambda1_m * rho_prime_rr +225lambda3m2_half_m / c_mean_global * rho_mean_global * v1_prime_rr +226(lambda2p3_half_m - lambda1_m) / c_mean_global^2 * p_prime_rr)227f2m = (lambda2p3_half_m * v1_prime_rr +228lambda3m2_half_m / c_mean_global * p_prime_rr / rho_mean_global)229f3m = lambda1_m * v2_prime_rr230f4m = (lambda3m2_half_m * c_mean_global * rho_mean_global * v1_prime_rr +231lambda2p3_half_m * p_prime_rr)232233f1 = f1p + f1m234f2 = f2p + f2m235f3 = f3p + f3m236f4 = f4p + f4m237else # orientation == 2238# Eigenvalues of the flux matrix239lambda1 = v2_mean240lambda2 = v2_mean - c_mean_global241lambda3 = v2_mean + c_mean_global242243lambda1_p = positive_part(lambda1)244lambda2_p = positive_part(lambda2)245lambda3_p = positive_part(lambda3)246lambda2p3_half_p = 0.5f0 * (lambda2_p + lambda3_p)247lambda3m2_half_p = 0.5f0 * (lambda3_p - lambda2_p)248249lambda1_m = negative_part(lambda1)250lambda2_m = negative_part(lambda2)251lambda3_m = negative_part(lambda3)252lambda2p3_half_m = 0.5f0 * (lambda2_m + lambda3_m)253lambda3m2_half_m = 0.5f0 * (lambda3_m - lambda2_m)254255f1p = (lambda1_p * rho_prime_ll +256lambda3m2_half_p / c_mean_global * rho_mean_global * v2_prime_ll +257(lambda2p3_half_p - lambda1_p) / c_mean_global^2 * p_prime_ll)258f2p = lambda1_p * v1_prime_ll259f3p = (lambda2p3_half_p * v2_prime_ll +260lambda3m2_half_p / c_mean_global * p_prime_ll / rho_mean_global)261f4p = (lambda3m2_half_p * c_mean_global * rho_mean_global * v2_prime_ll +262lambda2p3_half_p * p_prime_ll)263264f1m = (lambda1_m * rho_prime_rr +265lambda3m2_half_m / c_mean_global * rho_mean_global * v2_prime_rr +266(lambda2p3_half_m - lambda1_m) / c_mean_global^2 * p_prime_rr)267f2m = lambda1_m * v1_prime_rr268f3m = (lambda2p3_half_m * v2_prime_rr +269lambda3m2_half_m / c_mean_global * p_prime_rr / rho_mean_global)270f4m = (lambda3m2_half_m * c_mean_global * rho_mean_global * v2_prime_rr +271lambda2p3_half_m * p_prime_rr)272273f1 = f1p + f1m274f2 = f2p + f2m275f3 = f3p + f3m276f4 = f4p + f4m277end278279return SVector(f1, f2, f3, f4)280end281282@inline function flux_godunov(u_ll, u_rr, normal_direction::AbstractVector,283equations::LinearizedEulerEquations2D)284@unpack v_mean_global, rho_mean_global, c_mean_global = equations285rho_prime_ll, v1_prime_ll, v2_prime_ll, p_prime_ll = u_ll286rho_prime_rr, v1_prime_rr, v2_prime_rr, p_prime_rr = u_rr287288# Do not use `normalize` since we use `norm_` later to scale the eigenvalues289norm_ = norm(normal_direction)290normal_vector = normal_direction / norm_291292# Use normalized vector here, scaling is applied via eigenvalues of the flux matrix293v_mean_normal = v_mean_global[1] * normal_vector[1] +294v_mean_global[2] * normal_vector[2]295v_prime_normal_ll = v1_prime_ll * normal_vector[1] + v2_prime_ll * normal_vector[2]296v_prime_normal_rr = v1_prime_rr * normal_vector[1] + v2_prime_rr * normal_vector[2]297298# Eigenvalues of the flux matrix299lambda1 = v_mean_normal * norm_300lambda2 = (v_mean_normal - c_mean_global) * norm_301lambda3 = (v_mean_normal + c_mean_global) * norm_302303lambda1_p = positive_part(lambda1)304lambda2_p = positive_part(lambda2)305lambda3_p = positive_part(lambda3)306lambda2p3_half_p = 0.5f0 * (lambda2_p + lambda3_p)307lambda3m2_half_p = 0.5f0 * (lambda3_p - lambda2_p)308309lambda1_m = negative_part(lambda1)310lambda2_m = negative_part(lambda2)311lambda3_m = negative_part(lambda3)312lambda2p3_half_m = 0.5f0 * (lambda2_m + lambda3_m)313lambda3m2_half_m = 0.5f0 * (lambda3_m - lambda2_m)314315f1p = (lambda1_p * rho_prime_ll +316lambda3m2_half_p / c_mean_global * rho_mean_global * v_prime_normal_ll +317(lambda2p3_half_p - lambda1_p) / c_mean_global^2 * p_prime_ll)318f2p = (((lambda1_p * normal_vector[2]^2 +319lambda2p3_half_p * normal_vector[1]^2) * v1_prime_ll +320(lambda2p3_half_p - lambda1_p) * prod(normal_vector) * v2_prime_ll) +321lambda3m2_half_p / c_mean_global * normal_vector[1] * p_prime_ll /322rho_mean_global)323f3p = (((lambda1_p * normal_vector[1]^2 +324lambda2p3_half_p * normal_vector[2]^2) * v2_prime_ll +325(lambda2p3_half_p - lambda1_p) * prod(normal_vector) * v1_prime_ll) +326lambda3m2_half_p / c_mean_global * normal_vector[2] * p_prime_ll /327rho_mean_global)328f4p = (lambda3m2_half_p * c_mean_global * rho_mean_global * v_prime_normal_ll +329lambda2p3_half_p * p_prime_ll)330331f1m = (lambda1_m * rho_prime_rr +332lambda3m2_half_m / c_mean_global * rho_mean_global * v_prime_normal_rr +333(lambda2p3_half_m - lambda1_m) / c_mean_global^2 * p_prime_rr)334f2m = (((lambda1_m * normal_vector[2]^2 +335lambda2p3_half_m * normal_vector[1]^2) * v1_prime_rr +336(lambda2p3_half_m - lambda1_m) * prod(normal_vector) * v2_prime_rr) +337lambda3m2_half_m / c_mean_global * normal_vector[1] * p_prime_rr /338rho_mean_global)339f3m = (((lambda1_m * normal_vector[1]^2 +340lambda2p3_half_m * normal_vector[2]^2) * v2_prime_rr +341(lambda2p3_half_m - lambda1_m) * prod(normal_vector) * v1_prime_rr) +342lambda3m2_half_m / c_mean_global * normal_vector[2] * p_prime_rr /343rho_mean_global)344f4m = (lambda3m2_half_m * c_mean_global * rho_mean_global * v_prime_normal_rr +345lambda2p3_half_m * p_prime_rr)346347f1 = f1p + f1m348f2 = f2p + f2m349f3 = f3p + f3m350f4 = f4p + f4m351352return SVector(f1, f2, f3, f4)353end354355# Calculate estimate for minimum and maximum wave speeds for HLL-type fluxes356@inline function min_max_speed_naive(u_ll, u_rr, orientation::Integer,357equations::LinearizedEulerEquations2D)358min_max_speed_davis(u_ll, u_rr, orientation, equations)359end360361@inline function min_max_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,362equations::LinearizedEulerEquations2D)363min_max_speed_davis(u_ll, u_rr, normal_direction, equations)364end365366# More refined estimates for minimum and maximum wave speeds for HLL-type fluxes367@inline function min_max_speed_davis(u_ll, u_rr, orientation::Integer,368equations::LinearizedEulerEquations2D)369@unpack v_mean_global, c_mean_global = equations370371λ_min = v_mean_global[orientation] - c_mean_global372λ_max = v_mean_global[orientation] + c_mean_global373374return λ_min, λ_max375end376377@inline function min_max_speed_davis(u_ll, u_rr, normal_direction::AbstractVector,378equations::LinearizedEulerEquations2D)379@unpack v_mean_global, c_mean_global = equations380381norm_ = norm(normal_direction)382383v_normal = v_mean_global[1] * normal_direction[1] +384v_mean_global[2] * normal_direction[2]385386# The v_normals are already scaled by the norm387λ_min = v_normal - c_mean_global * norm_388λ_max = v_normal + c_mean_global * norm_389390return λ_min, λ_max391end392393# Convert conservative variables to primitive394@inline cons2prim(u, equations::LinearizedEulerEquations2D) = u395@inline cons2entropy(u, ::LinearizedEulerEquations2D) = u396end # muladd397398399