Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
trixi-framework
GitHub Repository: trixi-framework/Trixi.jl
Path: blob/main/src/equations/linear_scalar_advection_1d.jl
5586 views
1
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).
2
# Since these FMAs can increase the performance of many numerical algorithms,
3
# we need to opt-in explicitly.
4
# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.
5
@muladd begin
6
#! format: noindent
7
8
@doc raw"""
9
LinearScalarAdvectionEquation1D
10
11
The linear scalar advection equation
12
```math
13
\partial_t u + a \partial_1 u = 0
14
```
15
in one space dimension with constant velocity `a`.
16
"""
17
struct LinearScalarAdvectionEquation1D{RealT <: Real} <:
18
AbstractLinearScalarAdvectionEquation{1}
19
advection_velocity::SVector{1, RealT}
20
end
21
22
function LinearScalarAdvectionEquation1D(a::Real)
23
return LinearScalarAdvectionEquation1D(SVector(a))
24
end
25
26
varnames(::typeof(cons2cons), ::LinearScalarAdvectionEquation1D) = ("scalar",)
27
varnames(::typeof(cons2prim), ::LinearScalarAdvectionEquation1D) = ("scalar",)
28
29
# Set initial conditions at physical location `x` for time `t`
30
"""
31
initial_condition_constant(x, t, equations::LinearScalarAdvectionEquation1D)
32
33
A constant initial condition to test free-stream preservation.
34
"""
35
function initial_condition_constant(x, t, equation::LinearScalarAdvectionEquation1D)
36
RealT = eltype(x)
37
return SVector(RealT(2))
38
end
39
40
"""
41
initial_condition_convergence_test(x, t, equations::LinearScalarAdvectionEquation1D)
42
43
A smooth initial condition used for convergence tests
44
(in combination with [`BoundaryConditionDirichlet(initial_condition_convergence_test)`](@ref)
45
in non-periodic domains).
46
"""
47
function initial_condition_convergence_test(x, t,
48
equation::LinearScalarAdvectionEquation1D)
49
# Store translated coordinate for easy use of exact solution
50
RealT = eltype(x)
51
x_trans = x - equation.advection_velocity * t
52
53
c = 1
54
A = 0.5f0
55
L = 2
56
f = 1.0f0 / L
57
omega = 2 * convert(RealT, pi) * f
58
scalar = c + A * sin(omega * sum(x_trans))
59
return SVector(scalar)
60
end
61
62
"""
63
initial_condition_gauss(x, t, equations::LinearScalarAdvectionEquation1D)
64
65
A Gaussian pulse used together with
66
[`BoundaryConditionDirichlet(initial_condition_gauss)`](@ref).
67
"""
68
function initial_condition_gauss(x, t, equation::LinearScalarAdvectionEquation1D)
69
# Store translated coordinate for easy use of exact solution
70
x_trans = x - equation.advection_velocity * t
71
72
scalar = exp(-(x_trans[1]^2))
73
return SVector(scalar)
74
end
75
76
"""
77
initial_condition_sin(x, t, equations::LinearScalarAdvectionEquation1D)
78
79
A sine wave in the conserved variable.
80
"""
81
function initial_condition_sin(x, t, equation::LinearScalarAdvectionEquation1D)
82
# Store translated coordinate for easy use of exact solution
83
x_trans = x - equation.advection_velocity * t
84
85
scalar = sinpi(2 * x_trans[1])
86
return SVector(scalar)
87
end
88
89
"""
90
initial_condition_linear_x(x, t, equations::LinearScalarAdvectionEquation1D)
91
92
A linear function of `x[1]` used together with
93
[`boundary_condition_linear_x`](@ref).
94
"""
95
function initial_condition_linear_x(x, t, equation::LinearScalarAdvectionEquation1D)
96
# Store translated coordinate for easy use of exact solution
97
x_trans = x - equation.advection_velocity * t
98
99
return SVector(x_trans[1])
100
end
101
102
"""
103
boundary_condition_linear_x(u_inner, orientation, direction, x, t,
104
surface_flux_function,
105
equation::LinearScalarAdvectionEquation1D)
106
107
Boundary conditions for
108
[`initial_condition_linear_x`](@ref).
109
"""
110
function boundary_condition_linear_x(u_inner, orientation, direction, x, t,
111
surface_flux_function,
112
equation::LinearScalarAdvectionEquation1D)
113
u_boundary = initial_condition_linear_x(x, t, equation)
114
115
# Calculate boundary flux
116
if direction == 2 # u_inner is "left" of boundary, u_boundary is "right" of boundary
117
flux = surface_flux_function(u_inner, u_boundary, orientation, equation)
118
else # u_boundary is "left" of boundary, u_inner is "right" of boundary
119
flux = surface_flux_function(u_boundary, u_inner, orientation, equation)
120
end
121
122
return flux
123
end
124
125
# Pre-defined source terms should be implemented as
126
# function source_terms_WHATEVER(u, x, t, equations::LinearScalarAdvectionEquation1D)
127
128
# Calculate 1D flux for a single point
129
@inline function flux(u, orientation::Integer,
130
equation::LinearScalarAdvectionEquation1D)
131
a = equation.advection_velocity[orientation]
132
return a * u
133
end
134
135
# Calculate maximum wave speed for local Lax-Friedrichs-type dissipation
136
@inline function max_abs_speed_naive(u_ll, u_rr, orientation::Int,
137
equation::LinearScalarAdvectionEquation1D)
138
return abs(equation.advection_velocity[orientation])
139
end
140
141
"""
142
flux_godunov(u_ll, u_rr, orientation,
143
equations::LinearScalarAdvectionEquation1D)
144
145
Godunov (upwind) flux for the 1D linear scalar advection equation.
146
Essentially first order upwind, see e.g. https://math.stackexchange.com/a/4355076/805029 .
147
"""
148
function flux_godunov(u_ll, u_rr, orientation::Int,
149
equation::LinearScalarAdvectionEquation1D)
150
u_L = u_ll[1]
151
u_R = u_rr[1]
152
153
v_normal = equation.advection_velocity[orientation]
154
if v_normal >= 0
155
return SVector(v_normal * u_L)
156
else
157
return SVector(v_normal * u_R)
158
end
159
end
160
161
# See https://metaphor.ethz.ch/x/2019/hs/401-4671-00L/literature/mishra_hyperbolic_pdes.pdf ,
162
# section 4.2.5 and especially equation (4.33).
163
function flux_engquist_osher(u_ll, u_rr, orientation::Int,
164
equation::LinearScalarAdvectionEquation1D)
165
u_L = u_ll[1]
166
u_R = u_rr[1]
167
168
return SVector(0.5f0 * (flux(u_L, orientation, equation) +
169
flux(u_R, orientation, equation) -
170
abs(equation.advection_velocity[orientation]) * (u_R - u_L)))
171
end
172
173
"""
174
have_constant_speed(::LinearScalarAdvectionEquation1D)
175
176
Indicates whether the characteristic speeds are constant, i.e., independent of the solution.
177
Queried in the timestep computation [`StepsizeCallback`](@ref) and [`linear_structure`](@ref).
178
179
# Returns
180
- `True()`
181
"""
182
@inline have_constant_speed(::LinearScalarAdvectionEquation1D) = True()
183
184
@inline function max_abs_speeds(equation::LinearScalarAdvectionEquation1D)
185
return abs.(equation.advection_velocity)
186
end
187
188
"""
189
splitting_lax_friedrichs(u, orientation::Integer,
190
equations::LinearScalarAdvectionEquation1D)
191
splitting_lax_friedrichs(u, which::Union{Val{:minus}, Val{:plus}}
192
orientation::Integer,
193
equations::LinearScalarAdvectionEquation1D)
194
195
Naive local Lax-Friedrichs style flux splitting of the form `f⁺ = 0.5 (f + λ u)`
196
and `f⁻ = 0.5 (f - λ u)` where ` is the absolute value of the advection
197
velocity.
198
199
Returns a tuple of the fluxes "minus" (associated with waves going into the
200
negative axis direction) and "plus" (associated with waves going into the
201
positive axis direction). If only one of the fluxes is required, use the
202
function signature with argument `which` set to `Val{:minus}()` or `Val{:plus}()`.
203
204
!!! warning "Experimental implementation (upwind SBP)"
205
This is an experimental feature and may change in future releases.
206
"""
207
@inline function splitting_lax_friedrichs(u, orientation::Integer,
208
equations::LinearScalarAdvectionEquation1D)
209
fm = splitting_lax_friedrichs(u, Val{:minus}(), orientation, equations)
210
fp = splitting_lax_friedrichs(u, Val{:plus}(), orientation, equations)
211
return fm, fp
212
end
213
214
@inline function splitting_lax_friedrichs(u, ::Val{:plus}, orientation::Integer,
215
equations::LinearScalarAdvectionEquation1D)
216
RealT = eltype(u)
217
a = equations.advection_velocity[1]
218
return a > 0 ? flux(u, orientation, equations) : SVector(zero(RealT))
219
end
220
221
@inline function splitting_lax_friedrichs(u, ::Val{:minus}, orientation::Integer,
222
equations::LinearScalarAdvectionEquation1D)
223
RealT = eltype(u)
224
a = equations.advection_velocity[1]
225
return a < 0 ? flux(u, orientation, equations) : SVector(zero(RealT))
226
end
227
228
# Convert conservative variables to primitive and vice versa
229
@inline cons2prim(u, equation::LinearScalarAdvectionEquation1D) = u
230
@inline prim2cons(u, equation::LinearScalarAdvectionEquation1D) = u
231
232
# Convert conservative variables to entropy variables
233
@inline cons2entropy(u, equation::LinearScalarAdvectionEquation1D) = u
234
235
@doc raw"""
236
entropy(u, equations::AbstractLinearScalarAdvectionEquation)
237
238
Calculate entropy for a conservative state `u` as
239
```math
240
S(u) = \frac{1}{2} u^2
241
```
242
"""
243
@inline entropy(u::Real, ::LinearScalarAdvectionEquation1D) = 0.5f0 * u^2
244
@inline entropy(u, equation::LinearScalarAdvectionEquation1D) = entropy(u[1], equation)
245
246
# Calculate total energy for a conservative state `u`
247
@inline energy_total(u::Real, ::LinearScalarAdvectionEquation1D) = 0.5f0 * u^2
248
@inline function energy_total(u, equation::LinearScalarAdvectionEquation1D)
249
return energy_total(u[1], equation)
250
end
251
end # @muladd
252
253