Linear advection diffusion equation with periodic boundary conditions

Let's consider the linear advection diffusion equation

\[\begin{aligned} \partial_t u(t,x) + a \partial_x u(t,x) &= \varepsilon \partial_x^2 u(t,x), && t \in (0,T), x \in (x_{min}, x_{max}), \\ u(0,x) &= u_0(x), && x \in (x_{min}, x_{max}), \\ \end{aligned}\]

with periodic boundary conditions. Here, a is the constant advection velocity and ε > 0 is the constant diffusion coefficient.

Basic example using finite difference SBP operators

Let's create an appropriate discretization of this equation step by step. At first, we load packages that we will use in this example.

using SummationByPartsOperators, OrdinaryDiffEq
using LaTeXStrings; using Plots: Plots, plot, plot!, savefig

Next, we specify the initial data as Julia function as well as the spatial domain and the time span.

xmin, xmax = -1.0, 1.0
u0_func(x) = sinpi(x)
tspan = (0., 10.0)
(0.0, 10.0)

Next, we implement the semidiscretization using the interface of OrdinaryDiffEq.jl which is part of DifferentialEquations.jl.

function advection_diffusion!(du, u, params, t)
    # In-place version of du = -a * D1 * u
    mul!(du, params.D1, u, -params.a)
    # In-place version of du = du + ε * D2 * u
    mul!(du, params.D2, u, params.ε, true)
end
advection_diffusion! (generic function with 1 method)

Next, we choose first- and second-derivative SBP operators D1, D2, evaluate the initial data on the grid, and set up the semidiscretization as an ODE problem.

N = 100 # number of grid points
D1 = periodic_derivative_operator(derivative_order=1, accuracy_order=4,
                                  xmin=xmin, xmax=xmax, N=N)
D2 = periodic_derivative_operator(derivative_order=2, accuracy_order=4,
                                  xmin=xmin, xmax=xmax, N=N)
u0 = u0_func.(grid(D1))
params = (D1=D1, D2=D2, a=1.0, ε=0.03)
ode = ODEProblem(advection_diffusion!, u0, tspan, params);
ODEProblem with uType Vector{Float64} and tType Float64. In-place: true
timespan: (0.0, 10.0)
u0: 100-element Vector{Float64}:
 -0.0
 -0.06279051952931343
 -0.12533323356430437
 -0.1873813145857248
 -0.24868988716485466
 -0.30901699437494734
 -0.3681245526846779
 -0.42577929156507266
 -0.4817536741017154
 -0.5358267949789968
  ⋮
  0.5358267949789968
  0.4817536741017154
  0.42577929156507266
  0.3681245526846779
  0.30901699437494734
  0.24868988716485466
  0.1873813145857248
  0.12533323356430437
  0.06279051952931343

Finally, we can solve the ODE using an explicit Runge-Kutta method with adaptive time stepping.

sol = solve(ode, Tsit5(), saveat=range(first(tspan), stop=last(tspan), length=200));

plot(xguide=L"x", yguide=L"u")
plot!(evaluate_coefficients(sol[1], D1), label=L"u_0")
plot!(evaluate_coefficients(sol[end], D1), label=L"u_\mathrm{numerical}")
savefig("example_advection_diffusion.png");
"/home/runner/work/SummationByPartsOperators.jl/SummationByPartsOperators.jl/docs/build/tutorials/example_advection_diffusion.png"

Advanced visualization

Let's create an animation of the numerical solution.

using Printf; using Plots: Animation, frame, gif

let anim = Animation()
    idx = 1
    x, u = evaluate_coefficients(sol[idx], D1)
    fig = plot(x, u, xguide=L"x", yguide=L"u", xlim=extrema(x), ylim=(-1.05, 1.05),
              label="", title=@sprintf("\$t = %6.2f \$", sol.t[idx]))
    for idx in 1:length(sol.t)
        fig[1] = x, sol.u[idx]
        plot!(title=@sprintf("\$t = %6.2f \$", sol.t[idx]))
        frame(anim)
    end
    gif(anim, "example_advection_diffusion.gif")
end

example_advection_diffusion_animation

Package versions

These results were obtained using the following versions.

using InteractiveUtils
versioninfo()

using Pkg
Pkg.status(["SummationByPartsOperators", "OrdinaryDiffEq"],
           mode=PKGMODE_MANIFEST)
Julia Version 1.6.7
Commit 3b76b25b64 (2022-07-19 15:11 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: AMD EPYC 7763 64-Core Processor
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-11.0.1 (ORCJIT, generic)
Environment:
  JULIA_PKG_SERVER_REGISTRY_PREFERENCE = eager
      Status `~/work/SummationByPartsOperators.jl/SummationByPartsOperators.jl/docs/Manifest.toml`
  [1dea7af3] OrdinaryDiffEq v6.51.2
  [9f78cca6] SummationByPartsOperators v0.5.60 `~/work/SummationByPartsOperators.jl/SummationByPartsOperators.jl`