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 ode_euler(f, y0; tfinal=10., h=0.1) y = copy(y0) t = 0. thist = [t] yhist = [y0] while t < tfinal tnext = min(t+h, tfinal) h = tnext - t y += h * f(t, y) t = tnext push!(thist, t) push!(yhist, y) end thist, hcat(yhist...) end f1(t, y; k=10) = -k * (y .- cos(t)) thist, yhist = ode_euler(f1, [1.], tfinal=20, h=.2) plot(thist, yhist[1,:], marker=:circle) plot!(cos) f2(t, y) = [0 1; -1 0] * y thist, yhist = ode_euler(f2, [0., 1], h=.1, tfinal=80) scatter(thist, yhist') plot!([cos, sin]) eigvals([0 1; -1 0]) thist, yhist = ode_rk_explicit(f2, [0., 1], h=2.9, tfinal=50) scatter(thist, yhist') plot!([cos, sin], size=(800, 500)) 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(z -> 1 + z, "Forward Eulor") plot_stability(z -> rk_stability(4z, rk4), "RK4") plot_stability(z -> rk_stability(2z, heun), "Heun's method") plot_stability(z -> 1/(1-z), "Backward Euler") plot_stability(z -> Rz_theta(z, .5), "Midpoint method") 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)) 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 # Test on oscillator A = [0 1; -1 0] theta = .5 thist, uhist = ode_theta_linear(A, [0., 1], h=.6, theta=theta, tfinal=20) scatter(thist, uhist') plot!([cos, sin]) k = 500000 theta = .7 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)) 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(5) n = 200 A = heat_matrix(n) x = LinRange(-1, 1, n+1)[1:end-1] u0 = exp.(-200 * x .^ 2) @time thist, uhist = ode_theta_linear(A, u0, h=.1, theta=.5, tfinal=1); nsteps = size(uhist, 2) plot(x, uhist[:, 1:5]) 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) n = 50 A = advect_matrix(n, upwind=false) x = LinRange(-1, 1, n+1)[1:end-1] u0 = exp.(-9 * x .^ 2) @time thist, uhist = ode_theta_linear(A, u0, h=.04, theta=1, tfinal=1.); nsteps = size(uhist, 2) plot(x, uhist[:, 1:(nsteps÷8):end]) theta=.5 h = .1 plot_stability(z -> Rz_theta(z, theta), "\$\\theta=$theta, h=$h\$") ev = eigvals(Matrix(h*advect_matrix(20, upwind=true))) scatter!(real(ev), imag(ev)) theta=.5 h = .2 / 4 plot_stability(z -> Rz_theta(z, theta), "\$\\theta=$theta, h=$h\$") ev = eigvals(Matrix(h*heat_matrix(20))) scatter!(real(ev), imag(ev))