|
| 1 | +# [Homework 12 - The Runge-Kutta ODE Solver](@id hw12) |
| 2 | + |
| 3 | +There exist many different ODE solvers. To demonstrate how we can get |
| 4 | +significantly better results with a simple update to `Euler`, you will |
| 5 | +implement the second order Runge-Kutta method `RK2`: |
| 6 | + |
| 7 | +```math |
| 8 | +\begin{align*} |
| 9 | +\tilde x_{n+1} &= x_n + hf(x_n, t_n)\\ |
| 10 | + x_{n+1} &= x_n + \frac{h}{2}(f(x_n,t_n)+f(\tilde x_{n+1},t_{n+1})) |
| 11 | +\end{align*} |
| 12 | +``` |
| 13 | + |
| 14 | +`RK2` is a 2nd order method. It uses not only $f$ (the slope at a given point), |
| 15 | +but also $f'$ (the derivative of the slope). With some clever manipulations you |
| 16 | +can arrive at the equations above with make use of $f'$ without needing an |
| 17 | +explicit expression for it (if you want to know how, see |
| 18 | +[here](https://web.mit.edu/10.001/Web/Course_Notes/Differential_Equations_Notes/node5.html)). |
| 19 | +Essentially, `RK2` computes an initial guess $\tilde x_{n+1}$ to then average |
| 20 | +the slopes at the current point $x_n$ and at the guess $\tilde x_{n+1}$ which |
| 21 | +is illustarted below. |
| 22 | + |
| 23 | + |
| 24 | +The code from the lab that you will need for this homework is given below. |
| 25 | +As always, put all your code in a file called `hw.jl`, zip it, and upload it |
| 26 | +to BRUTE. |
| 27 | + |
| 28 | +```@example hw |
| 29 | +struct ODEProblem{F,T<:Tuple{Number,Number},U<:AbstractVector,P<:AbstractVector} |
| 30 | + f::F |
| 31 | + tspan::T |
| 32 | + u0::U |
| 33 | + θ::P |
| 34 | +end |
| 35 | +
|
| 36 | +abstract type ODESolver end |
| 37 | +
|
| 38 | +struct Euler{T} <: ODESolver |
| 39 | + dt::T |
| 40 | +end |
| 41 | +
|
| 42 | +function (solver::Euler)(prob::ODEProblem, u, t) |
| 43 | + f, θ, dt = prob.f, prob.θ, solver.dt |
| 44 | + (u + dt*f(u,θ), t+dt) |
| 45 | +end |
| 46 | +
|
| 47 | +function solve(prob::ODEProblem, solver::ODESolver) |
| 48 | + t = prob.tspan[1]; u = prob.u0 |
| 49 | + us = [u]; ts = [t] |
| 50 | + while t < prob.tspan[2] |
| 51 | + (u,t) = solver(prob, u, t) |
| 52 | + push!(us,u) |
| 53 | + push!(ts,t) |
| 54 | + end |
| 55 | + ts, reduce(hcat,us) |
| 56 | +end |
| 57 | +
|
| 58 | +# Define & Solve ODE |
| 59 | +
|
| 60 | +function lotkavolterra(x,θ) |
| 61 | + α, β, γ, δ = θ |
| 62 | + x₁, x₂ = x |
| 63 | +
|
| 64 | + dx₁ = α*x₁ - β*x₁*x₂ |
| 65 | + dx₂ = δ*x₁*x₂ - γ*x₂ |
| 66 | +
|
| 67 | + [dx₁, dx₂] |
| 68 | +end |
| 69 | +nothing # hide |
| 70 | +``` |
| 71 | + |
| 72 | +::: danger Homework (2 points) |
| 73 | + |
| 74 | +Implement the 2nd order Runge-Kutta solver according to the equations given above |
| 75 | +by overloading the call method of a new type `RK2`. |
| 76 | + |
| 77 | +```julia |
| 78 | +(solver::RK2)(prob::ODEProblem, u, t) |
| 79 | +``` |
| 80 | + |
| 81 | +::: |
| 82 | + |
| 83 | +```@setup hw |
| 84 | +struct RK2{T} <: ODESolver |
| 85 | + dt::T |
| 86 | +end |
| 87 | +
|
| 88 | +function (solver::RK2)(prob::ODEProblem, u, t) |
| 89 | + f, θ, dt = prob.f, prob.θ, solver.dt |
| 90 | + du = f(u,θ) |
| 91 | + uh = u + du*dt |
| 92 | + u + dt/2*(du + f(uh,θ)), t+dt |
| 93 | +end |
| 94 | +``` |
| 95 | + |
| 96 | +You should be able to use it exactly like our `Euler` solver before: |
| 97 | + |
| 98 | +```@example hw |
| 99 | +using Plots |
| 100 | +using JLD2 |
| 101 | +
|
| 102 | +# Define ODE |
| 103 | +function lotkavolterra(x,θ) |
| 104 | + α, β, γ, δ = θ |
| 105 | + x₁, x₂ = x |
| 106 | +
|
| 107 | + dx₁ = α*x₁ - β*x₁*x₂ |
| 108 | + dx₂ = δ*x₁*x₂ - γ*x₂ |
| 109 | +
|
| 110 | + [dx₁, dx₂] |
| 111 | +end |
| 112 | +
|
| 113 | +θ = [0.1,0.2,0.3,0.2] |
| 114 | +u0 = [1.0,1.0] |
| 115 | +tspan = (0.,100.) |
| 116 | +prob = ODEProblem(lotkavolterra,tspan,u0,θ) |
| 117 | +
|
| 118 | +# load correct data |
| 119 | +true_data = load("lotkadata.jld2") |
| 120 | +
|
| 121 | +# create plot |
| 122 | +p1 = plot(true_data["t"], true_data["u"][1,:], lw=4, ls=:dash, alpha=0.7, |
| 123 | + color=:gray, label="x Truth") |
| 124 | +plot!(p1, true_data["t"], true_data["u"][2,:], lw=4, ls=:dash, alpha=0.7, |
| 125 | + color=:gray, label="y Truth") |
| 126 | +
|
| 127 | +# Euler solve |
| 128 | +(t,X) = solve(prob, Euler(0.2)) |
| 129 | +plot!(p1,t,X[1,:], color=3, lw=3, alpha=0.8, label="x Euler", ls=:dot) |
| 130 | +plot!(p1,t,X[2,:], color=4, lw=3, alpha=0.8, label="y Euler", ls=:dot) |
| 131 | +
|
| 132 | +# RK2 solve |
| 133 | +(t,X) = solve(prob, RK2(0.2)) |
| 134 | +plot!(p1,t,X[1,:], color=1, lw=3, alpha=0.8, label="x RK2") |
| 135 | +plot!(p1,t,X[2,:], color=2, lw=3, alpha=0.8, label="y RK2") |
| 136 | +``` |
0 commit comments