2023-04-24 Adaptivity
Contents
2023-04-24 Adaptivity#
Last time#
Implicit and explicit methods
Stiff equations
Today#
More on stiffness
PDE examples
Accuracy
Adaptivity
using LinearAlgebra
using Plots
default(linewidth=4, legendfontsize=12)
struct RKTable
A::Matrix
b::Vector
c::Vector
function RKTable(A, b)
s = length(b)
A = reshape(A, s, s)
c = vec(sum(A, dims=2))
new(A, b, c)
end
end
function rk_stability(z, rk)
s = length(rk.b)
1 + z * rk.b' * ((I - z*rk.A) \ ones(s))
end
rk4 = RKTable([0 0 0 0; .5 0 0 0; 0 .5 0 0; 0 0 1 0], [1, 2, 2, 1] / 6)
heun = RKTable([0 0; 1 0], [.5, .5])
Rz_theta(z, theta) = (1 + (1 - theta)*z) / (1 - theta*z)
function ode_rk_explicit(f, u0; tfinal=1, h=0.1, table=rk4)
u = copy(u0)
t = 0.
n, s = length(u), length(table.c)
fY = zeros(n, s)
thist = [t]
uhist = [u0]
while t < tfinal
tnext = min(t+h, tfinal)
h = tnext - t
for i in 1:s
ti = t + h * table.c[i]
Yi = u + h * sum(fY[:,1:i-1] * table.A[i,1:i-1], dims=2)
fY[:,i] = f(ti, Yi)
end
u += h * fY * table.b
t = tnext
push!(thist, t)
push!(uhist, u)
end
thist, hcat(uhist...)
end
function plot_stability(Rz, method; xlim=(-3, 2), ylim=(-1.5, 1.5))
x = xlim[1]:.02:xlim[2]
y = ylim[1]:.02:ylim[2]
plot(title="Stability: $method", aspect_ratio=:equal, xlim=xlim, ylim=ylim)
heatmap!(x, y, (x, y) -> abs(Rz(x + 1im*y)), c=:bwr, clims=(0, 2))
contour!(x, y, (x, y) -> abs(Rz(x + 1im*y)), color=:black, linewidth=2, levels=[1.])
plot!(x->0, color=:black, linewidth=1, label=:none)
plot!([0, 0], [ylim...], color=:black, linewidth=1, label=:none)
end
plot_stability (generic function with 1 method)
The \(\theta\) method#
Forward and backward Euler are bookends of the family known as \(\theta\) methods.
which, for linear problems, is solved as
\(\theta=0\) is explicit Euler, \(\theta=1\) is implicit Euler, and \(\theta=1/2\) are the midpoint or trapezoid rules (equivalent for linear problems). The stability function is
Rz_theta(z, theta) = (1 + (1-theta)*z) / (1 - theta*z)
theta = .5
plot_stability(z -> Rz_theta(z, theta), "\$\\theta=$theta\$",
xlim=(-20, 1))
\(\theta\) method for the oscillator#
function ode_theta_linear(A, u0; forcing=zero, tfinal=1, h=0.1, theta=.5)
u = copy(u0)
t = 0.
thist = [t]
uhist = [u0]
while t < tfinal
tnext = min(t+h, tfinal)
h = tnext - t
rhs = (I + h*(1-theta)*A) * u .+ h*forcing(t+h*theta)
u = (I - h*theta*A) \ rhs
t = tnext
push!(thist, t)
push!(uhist, u)
end
thist, hcat(uhist...)
end
ode_theta_linear (generic function with 1 method)
# Test on oscillator
A = [0 1; -1 0]
theta = .5
thist, uhist = ode_theta_linear(A, [0., 1], h=2, theta=theta, tfinal=20)
scatter(thist, uhist')
plot!([cos, sin])
\(\theta\) method for the cosine decay#
k = 4000
theta = 1
thist, uhist = ode_theta_linear(-k, [.2], forcing=t -> k*cos(t), tfinal=5, h=.1, theta=theta)
scatter(thist, uhist[1,:], title="\$\\theta = $theta\$")
plot!(cos, size=(800, 500))
Stability classes and the \(\theta\) method#
Definition: \(A\)-stability#
A method is \(A\)-stable if the stability region
Definition: \(L\)-stability#
A time integrator with stability function \(R(z)\) is \(L\)-stable if
Heat equation as linear ODE#
How do different \(\theta \in [0, 1]\) compare in terms of stability?
Are there artifacts even when the solution is stable?
using SparseArrays
function heat_matrix(n)
dx = 2 / n
rows = [1]
cols = [1]
vals = [0.]
wrap(j) = (j + n - 1) % n + 1
for i in 1:n
append!(rows, [i, i, i])
append!(cols, wrap.([i-1, i, i+1]))
append!(vals, [1, -2, 1] ./ dx^2)
end
sparse(rows, cols, vals)
end
heat_matrix(8)
8×8 SparseMatrixCSC{Float64, Int64} with 24 stored entries:
-32.0 16.0 ⋅ ⋅ ⋅ ⋅ ⋅ 16.0
16.0 -32.0 16.0 ⋅ ⋅ ⋅ ⋅ ⋅
⋅ 16.0 -32.0 16.0 ⋅ ⋅ ⋅ ⋅
⋅ ⋅ 16.0 -32.0 16.0 ⋅ ⋅ ⋅
⋅ ⋅ ⋅ 16.0 -32.0 16.0 ⋅ ⋅
⋅ ⋅ ⋅ ⋅ 16.0 -32.0 16.0 ⋅
⋅ ⋅ ⋅ ⋅ ⋅ 16.0 -32.0 16.0
16.0 ⋅ ⋅ ⋅ ⋅ ⋅ 16.0 -32.0
n = 400
A = heat_matrix(n)
x = LinRange(-1, 1, n+1)[1:end-1]
u0 = exp.(-100 * x .^ 2)
@time thist, uhist = ode_theta_linear(A, u0, h=.01, theta=.5, tfinal=1);
nsteps = size(uhist, 2)
plot(x, uhist[:, 1:5])
0.012178 seconds (8.32 k allocations: 28.983 MiB)
Advection as a linear ODE#
function advect_matrix(n; upwind=false)
dx = 2 / n
rows = [1]
cols = [1]
vals = [0.]
wrap(j) = (j + n - 1) % n + 1
for i in 1:n
append!(rows, [i, i])
if upwind
append!(cols, wrap.([i-1, i]))
append!(vals, [1., -1] ./ dx)
else
append!(cols, wrap.([i-1, i+1]))
append!(vals, [1., -1] ./ 2dx)
end
end
sparse(rows, cols, vals)
end
advect_matrix(5)
5×5 SparseMatrixCSC{Float64, Int64} with 11 stored entries:
0.0 -1.25 ⋅ ⋅ 1.25
1.25 ⋅ -1.25 ⋅ ⋅
⋅ 1.25 ⋅ -1.25 ⋅
⋅ ⋅ 1.25 ⋅ -1.25
-1.25 ⋅ ⋅ 1.25 ⋅
n = 50
A = advect_matrix(n, upwind=true)
@show eigvals(Matrix(A))[1]
x = LinRange(-1, 1, n+1)[1:end-1]
u0 = exp.(-40 * x .^ 2)
@time thist, uhist = ode_theta_linear(A, u0, h=.04, theta=0, tfinal=1.);
nsteps = size(uhist, 2)
plot(x, uhist[:, 1:(nsteps÷8):end], legend=:none)
(eigvals(Matrix(A)))[1] = -50.00000000000011 + 0.0im
0.000373 seconds (965 allocations: 434.438 KiB)
Spectrum of operators#
theta = .5
h = .05
plot_stability(z -> Rz_theta(z, theta), "\$\\theta=$theta, h=$h\$")
ev = eigvals(Matrix(h*advect_matrix(20, upwind=false)))
scatter!(real(ev), imag(ev))
theta=.5
h = .2 / 32
plot_stability(z -> Rz_theta(z, theta), "\$\\theta=$theta, h=$h\$", xlim=(-20, 1))
ev = eigvals(Matrix(h*heat_matrix(20)))
scatter!(real(ev), imag(ev), legend=:none)
Stiffness#
Stiff equations are problems for which explicit methods don’t work. (Hairer and Wanner, 2002)
“stiff” dates to Curtiss and Hirschfelder (1952)
We’ll use the cosine relaxation example \(y_t = -k(y - \cos t)\) using the \(\theta\) method, varying \(k\) and \(\theta\).
function ode_error(h; theta=.5, k=10)
u0 = [.2]
thist, uhist = ode_theta_linear(-k, u0, forcing=t -> k*cos(t), tfinal=3, h=h, theta=theta)
T = thist[end]
u_exact = (u0 .- k^2/(k^2+1)) * exp(-k*T) .+ k*(sin(T) + k*cos(T))/(k^2 + 1)
uhist[1,end] .- u_exact
end
ode_error (generic function with 1 method)
hs = .5 .^ (1:8)
errors = ode_error.(hs, theta=0, k=100)
plot(hs, norm.(errors), marker=:circle, xscale=:log10, yscale=:log10, ylim=(1e-6, 2))
plot!(hs, hs, label="\$h\$", legend=:topleft)
plot!(hs, hs.^2, label="\$h^2\$", ylabel="error", xlabel="\$\\Delta t\$")
Examples of ODE systems#
Stiff problems posess multiple time scales and the fastest scale is “not interesting”
Stiff#
The ocean
Non-stiff#
The ocean
Adaptive time integrators#
The Oregonator mechanism in chemical kinetics describes an oscillatory chemical system. It consists of three species with concentrations \(\mathbf x = [x_0,x_1,x_2]^T\) (scaled units) and the evolution equations
Our last few sessions#
Topics available#
Answer questions/work problems of your choosing
Large-scale problems (sparse linear algebra, parallel algorithms)
Fast Fourier Transform
Monte Carlo for integration
Using libraries
Dive into performance
Career discussion
May 1-3 (Mon, Wed): projects#
I’ll post a sign-up sheet. If you wish to coordinate with another group looking at a similar topic, we can schedule you together.
If you prefer to pre-record, do that and share a link on Zulip.
If you have a group, but it hasn’t been mentioned on Zulip, please identify yourself so I can check in.