Implicit time-integration

Necessary packages

using Ariadne
using CairoMakie
using OffsetArrays

include(joinpath(dirname(pathof(Ariadne)), "..", "examples", "implicit.jl"))
include(joinpath(dirname(pathof(Ariadne)), "..", "examples", "halovector.jl"))

Diffusion 2D

Boundary

function bc_periodic!(u)
    N, M = size(u)
    N = N - 2
    M = M - 2

    # (wrap around)
    u[0, :] .= u[N, :]
    u[N + 1, :] .= u[1, :]
    u[:, 0] .= u[:, N]
    u[:, N + 1] .= u[:, 1]
    return nothing
end

function bc_zero!(u)
    N, M = size(u)
    N = N - 2
    M = M - 2

    u[0, :] .= 0
    u[N + 1, :] .= 0
    u[:, 0] .= 0
    u[:, N + 1] .= 0
    return nothing
end


function diffusion!(du::HaloVector, u::HaloVector, p, t)
    return diffusion!(du.data, u.data, p, t)
end

function diffusion!(du, u, (a, Δx, Δy, bc!), _)
    N, M = size(u)
    N = N - 2
    M = M - 2

    # Enforce boundary conditions
    bc!(u)

    for i in 1:N
        for j in 1:M
            du[i, j] = a * (
                (u[i + 1, j] - 2 * u[i, j] + u[i - 1, j]) / Δx^2 +
                    (u[i, j + 1] - 2 * u[i, j] + u[i, j - 1]) / Δy^2
            )
        end
    end
    return
end

a = 0.01

N = M = 40

Δx = 1 / (N + 1)  # x-grid spacing
Δy = 1 / (M + 1)  # y-grid spacing

# TODO: This is a time-step from explicit
Δt = Δx^2 * Δy^2 / (2.0 * a * (Δx^2 + Δy^2)) # Largest stable time step

let
    N = M = 12
    u₀ = HaloVector(OffsetArray(zeros(N + 2, M + 2), 0:(N + 1), 0:(M + 1)))
    jacobian(G_Euler!, diffusion!, u₀, (a, Δx, Δy, bc_zero!), Δt, 0.0)
end

xs = 0.0:Δx:1.0
ys = 0.0:Δy:1.0

function f(x, y)
    return sin(π * x) .* sin(π * y)
end

u₀ = let
    _u₀ = zeros(N + 2, M + 2)
    _u₀ .= f.(xs, ys')
    HaloVector(OffsetArray(_u₀, 0:(N + 1), 0:(M + 1)))
end

function plot_state(t, u, xs, ys)
    data = @lift $u.data.parent

    xs_line = @lift @view $data[:, M ÷ 2]
    ys_line = @lift @view $data[N ÷ 2, :]

    fig = Figure()

    ax = Axis(fig[1, 1])
    heatmap!(ax, xs, ys, data)

    ax1 = Axis(fig[2, 1])
    lines!(ax1, xs, xs_line)

    ax2 = Axis(fig[2, 2])
    lines!(ax2, ys, ys_line)

    fig[0, :] = Label(fig, @lift("t = $($t)"))

    return fig
end

fig = plot_state(Observable(0.0), Observable(u₀), xs, ys)

function create_video_implicit(filename, G!, f!, xs, ys, u, p, Δt, t_stop, frame_kwargs)
    ts = 0.0:Δt:t_stop
    _u = Observable(u)
    _t = Observable(first(ts))

    fig = plot_state(_t, _u, xs, ys)
    return record(fig, filename; frame_kwargs...) do io
        function callback(__u)
            _u[] = u
            _t[] += Δt
            # autolimits!(ax) # update limits
            recordframe!(io)
            return yield()
        end
        solve(G!, f!, u, p, Δt, ts; callback, verbose = 1, krylov_kwargs = (; verbose = 1, reorthogonalization = true))
    end
end

create_video_implicit(
    joinpath(@__DIR__, "implicit_euler.mp4"),
    G_Euler!, diffusion!, xs, ys, copy(u₀), (a, Δx, Δy, bc_zero!), Δt, 10.0, (; framerate = 30)
)

create_video_implicit(
    joinpath(@__DIR__, "implicit_midpoint.mp4"),
    G_Midpoint!, diffusion!, xs, ys, copy(u₀), (a, Δx, Δy, bc_zero!), Δt, 10.0, (; framerate = 30)
)

create_video_implicit(
    joinpath(@__DIR__, "implicit_trapezoid.mp4"),
    G_Trapezoid!, diffusion!, xs, ys, copy(u₀), (a, Δx, Δy, bc_zero!), Δt, 10.0, (; framerate = 30)
)

# TODO:
create_video_implicit(
    joinpath(@__DIR__, "implicit_euler_periodic.mp4"),
    G_Trapezoid!, diffusion!, xs, ys, copy(u₀), (a, Δx, Δy, bc_periodic!), Δt, 2 * Δt, (; framerate = 30)
)
"/home/runner/work/Ariadne.jl/Ariadne.jl/docs/build/generated/implicit_euler_periodic.mp4"

This page was generated using Literate.jl.