Path: blob/main/src/equations/linear_scalar_advection_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"""8LinearScalarAdvectionEquation2D910The linear scalar advection equation11```math12\partial_t u + a_1 \partial_1 u + a_2 \partial_2 u = 013```14in two space dimensions with constant velocity `a`.15"""16struct LinearScalarAdvectionEquation2D{RealT <: Real} <:17AbstractLinearScalarAdvectionEquation{2}18advection_velocity::SVector{2, RealT}19end2021function LinearScalarAdvectionEquation2D(a::NTuple{2, <:Real})22LinearScalarAdvectionEquation2D(SVector(a))23end2425function LinearScalarAdvectionEquation2D(a1::Real, a2::Real)26LinearScalarAdvectionEquation2D(SVector(a1, a2))27end2829varnames(::typeof(cons2cons), ::LinearScalarAdvectionEquation2D) = ("scalar",)30varnames(::typeof(cons2prim), ::LinearScalarAdvectionEquation2D) = ("scalar",)3132# Calculates translated coordinates `x` for a periodic domain33function x_trans_periodic_2d(x, domain_length = SVector(10, 10), center = SVector(0, 0))34x_normalized = x .- center35x_shifted = x_normalized .% domain_length36x_offset = ((x_shifted .< -0.5f0 * domain_length) -37(x_shifted .> 0.5f0 * domain_length)) .* domain_length38return center + x_shifted + x_offset39end4041# Set initial conditions at physical location `x` for time `t`42"""43initial_condition_constant(x, t, equations::LinearScalarAdvectionEquation2D)4445A constant initial condition to test free-stream preservation.46"""47function initial_condition_constant(x, t, equation::LinearScalarAdvectionEquation2D)48RealT = eltype(x)49return SVector(RealT(2))50end5152"""53initial_condition_convergence_test(x, t, equations::LinearScalarAdvectionEquation2D)5455A smooth initial condition used for convergence tests.56"""57function initial_condition_convergence_test(x, t,58equation::LinearScalarAdvectionEquation2D)59# Store translated coordinate for easy use of exact solution60RealT = eltype(x)61x_trans = x - equation.advection_velocity * t6263c = 164A = 0.5f065L = 266f = 1.0f0 / L67omega = 2 * convert(RealT, pi) * f68scalar = c + A * sin(omega * sum(x_trans))69return SVector(scalar)70end7172"""73initial_condition_gauss(x, t, equation::LinearScalarAdvectionEquation2D)7475A Gaussian pulse used together with76[`BoundaryConditionDirichlet(initial_condition_gauss)`](@ref).77"""78function initial_condition_gauss(x, t, equation::LinearScalarAdvectionEquation2D)79# Store translated coordinate for easy use of exact solution80x_trans = x_trans_periodic_2d(x - equation.advection_velocity * t)8182scalar = exp(-(x_trans[1]^2 + x_trans[2]^2))83return SVector(scalar)84end8586"""87initial_condition_sin_sin(x, t, equations::LinearScalarAdvectionEquation2D)8889A sine wave in the conserved variable.90"""91function initial_condition_sin_sin(x, t, equation::LinearScalarAdvectionEquation2D)92# Store translated coordinate for easy use of exact solution93x_trans = x - equation.advection_velocity * t9495scalar = sinpi(2 * x_trans[1]) * sinpi(2 * x_trans[2])96return SVector(scalar)97end9899"""100initial_condition_linear_x_y(x, t, equations::LinearScalarAdvectionEquation2D)101102A linear function of `x[1] + x[2]` used together with103[`boundary_condition_linear_x_y`](@ref).104"""105function initial_condition_linear_x_y(x, t, equation::LinearScalarAdvectionEquation2D)106# Store translated coordinate for easy use of exact solution107x_trans = x - equation.advection_velocity * t108109return SVector(sum(x_trans))110end111112"""113boundary_condition_linear_x_y(u_inner, orientation, direction, x, t,114surface_flux_function,115equation::LinearScalarAdvectionEquation2D)116117Boundary conditions for118[`initial_condition_linear_x_y`](@ref).119"""120function boundary_condition_linear_x_y(u_inner, orientation, direction, x, t,121surface_flux_function,122equation::LinearScalarAdvectionEquation2D)123u_boundary = initial_condition_linear_x_y(x, t, equation)124125# Calculate boundary flux126if direction in (2, 4) # u_inner is "left" of boundary, u_boundary is "right" of boundary127flux = surface_flux_function(u_inner, u_boundary, orientation, equation)128else # u_boundary is "left" of boundary, u_inner is "right" of boundary129flux = surface_flux_function(u_boundary, u_inner, orientation, equation)130end131132return flux133end134135"""136initial_condition_linear_x(x, t, equations::LinearScalarAdvectionEquation2D)137138A linear function of `x[1]` used together with139[`boundary_condition_linear_x`](@ref).140"""141function initial_condition_linear_x(x, t, equation::LinearScalarAdvectionEquation2D)142# Store translated coordinate for easy use of exact solution143x_trans = x - equation.advection_velocity * t144145return SVector(x_trans[1])146end147148"""149boundary_condition_linear_x(u_inner, orientation, direction, x, t,150surface_flux_function,151equation::LinearScalarAdvectionEquation2D)152153Boundary conditions for154[`initial_condition_linear_x`](@ref).155"""156function boundary_condition_linear_x(u_inner, orientation, direction, x, t,157surface_flux_function,158equation::LinearScalarAdvectionEquation2D)159u_boundary = initial_condition_linear_x(x, t, equation)160161# Calculate boundary flux162if direction in (2, 4) # u_inner is "left" of boundary, u_boundary is "right" of boundary163flux = surface_flux_function(u_inner, u_boundary, orientation, equation)164else # u_boundary is "left" of boundary, u_inner is "right" of boundary165flux = surface_flux_function(u_boundary, u_inner, orientation, equation)166end167168return flux169end170171"""172initial_condition_linear_y(x, t, equations::LinearScalarAdvectionEquation2D)173174A linear function of `x[1]` used together with175[`boundary_condition_linear_y`](@ref).176"""177function initial_condition_linear_y(x, t, equation::LinearScalarAdvectionEquation2D)178# Store translated coordinate for easy use of exact solution179x_trans = x - equation.advection_velocity * t180181return SVector(x_trans[2])182end183184"""185boundary_condition_linear_y(u_inner, orientation, direction, x, t,186surface_flux_function,187equation::LinearScalarAdvectionEquation2D)188189Boundary conditions for190[`initial_condition_linear_y`](@ref).191"""192function boundary_condition_linear_y(u_inner, orientation, direction, x, t,193surface_flux_function,194equation::LinearScalarAdvectionEquation2D)195u_boundary = initial_condition_linear_y(x, t, equation)196197# Calculate boundary flux198if direction in (2, 4) # u_inner is "left" of boundary, u_boundary is "right" of boundary199flux = surface_flux_function(u_inner, u_boundary, orientation, equation)200else # u_boundary is "left" of boundary, u_inner is "right" of boundary201flux = surface_flux_function(u_boundary, u_inner, orientation, equation)202end203204return flux205end206207# Pre-defined source terms should be implemented as208# function source_terms_WHATEVER(u, x, t, equations::LinearScalarAdvectionEquation2D)209210# Calculate 1D flux for a single point211@inline function flux(u, orientation::Integer,212equation::LinearScalarAdvectionEquation2D)213a = equation.advection_velocity[orientation]214return a * u215end216217# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation218@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Integer,219equation::LinearScalarAdvectionEquation2D)220return abs(equation.advection_velocity[orientation])221end222223# Calculate 1D flux for a single point in the normal direction224# Note, this directional vector is not normalized225@inline function flux(u, normal_direction::AbstractVector,226equation::LinearScalarAdvectionEquation2D)227a = dot(equation.advection_velocity, normal_direction) # velocity in normal direction228return a * u229end230231# Calculate maximum wave speed in the normal direction for local Lax-Friedrichs-type dissipation232@inline function max_abs_speed_naive(u_ll, u_rr, normal_direction::AbstractVector,233equation::LinearScalarAdvectionEquation2D)234a = dot(equation.advection_velocity, normal_direction) # velocity in normal direction235return abs(a)236end237238"""239flux_godunov(u_ll, u_rr, orientation_or_normal_direction,240equations::LinearScalarAdvectionEquation2D)241242Godunov (upwind) flux for the 2D linear scalar advection equation.243Essentially first order upwind, see e.g. https://math.stackexchange.com/a/4355076/805029 .244"""245function flux_godunov(u_ll, u_rr, orientation::Integer,246equation::LinearScalarAdvectionEquation2D)247u_L = u_ll[1]248u_R = u_rr[1]249250v_normal = equation.advection_velocity[orientation]251if v_normal >= 0252return SVector(v_normal * u_L)253else254return SVector(v_normal * u_R)255end256end257258function flux_godunov(u_ll, u_rr, normal_direction::AbstractVector,259equation::LinearScalarAdvectionEquation2D)260u_L = u_ll[1]261u_R = u_rr[1]262263a_normal = dot(equation.advection_velocity, normal_direction)264if a_normal >= 0265return SVector(a_normal * u_L)266else267return SVector(a_normal * u_R)268end269end270271@inline have_constant_speed(::LinearScalarAdvectionEquation2D) = True()272273@inline function max_abs_speeds(equation::LinearScalarAdvectionEquation2D)274return abs.(equation.advection_velocity)275end276277# Convert conservative variables to primitive278@inline cons2prim(u, equation::LinearScalarAdvectionEquation2D) = u279280# Convert conservative variables to entropy variables281@inline cons2entropy(u, equation::LinearScalarAdvectionEquation2D) = u282283# Calculate entropy for a conservative state `cons`284@inline entropy(u::Real, ::LinearScalarAdvectionEquation2D) = 0.5f0 * u^2285@inline entropy(u, equation::LinearScalarAdvectionEquation2D) = entropy(u[1], equation)286287# Calculate total energy for a conservative state `cons`288@inline energy_total(u::Real, ::LinearScalarAdvectionEquation2D) = 0.5f0 * u^2289@inline function energy_total(u, equation::LinearScalarAdvectionEquation2D)290energy_total(u[1], equation)291end292end # @muladd293294295