# Find project root and include ensure_packages.jl
project_root = let
current = pwd()
while !isfile(joinpath(current, "Project.toml")) && !isfile(joinpath(current, "_quarto.yml"))
parent = dirname(current)
parent == current && break
current = parent
end
current
end
include(joinpath(project_root, "scripts", "ensure_packages.jl"))
@auto_using DifferentialEquations CairoMakie
# SIR model parameters
β = 0.3 # Transmission rate
γ = 0.1 # Recovery rate
ν = 0.05 # Vaccination rate (when intervention is active)
# Original SIR model (no intervention)
function sir_original!(du, u, p, t)
"""SIR epidemic model: Susceptible → Infected → Recovered."""
S, I, R = u
du[1] = -β * S * I # dS/dt
du[2] = β * S * I - γ * I # dI/dt
du[3] = γ * I # dR/dt
end
# SIR model under intervention do(V = 1) (vaccination active)
function sir_intervened!(du, u, p, t)
"""SIR model with vaccination intervention: do(V = 1)."""
S, I, R = u
# Intervention: do(V = 1) means vaccination is active
du[1] = -β * S * I - ν # dS/dt: vaccination moves S → R
du[2] = β * S * I - γ * I # dI/dt: unchanged
du[3] = γ * I + ν # dR/dt: vaccination adds to recovered
end
# Initial conditions
u0 = [0.99, 0.01, 0.0] # 99% susceptible, 1% infected, 0% recovered
tspan = (0.0, 100.0)
# Solve original system
prob_original = ODEProblem(sir_original!, u0, tspan)
sol_original = solve(prob_original, Tsit5())
# Solve intervened system
prob_intervened = ODEProblem(sir_intervened!, u0, tspan)
sol_intervened = solve(prob_intervened, Tsit5())
# Visualise comparison
let
fig = Figure(size = (1000, 600))
ax1 = Axis(fig[1, 1], title = "Susceptible (S)", xlabel = "Time", ylabel = "Proportion")
ax2 = Axis(fig[1, 2], title = "Infected (I)", xlabel = "Time", ylabel = "Proportion")
ax3 = Axis(fig[2, 1], title = "Recovered (R)", xlabel = "Time", ylabel = "Proportion")
ax4 = Axis(fig[2, 2], title = "Peak Infection Comparison", xlabel = "Time", ylabel = "Infected")
lines!(ax1, sol_original.t, [u[1] for u in sol_original.u], label = "Original", linewidth = 2)
lines!(ax1, sol_intervened.t, [u[1] for u in sol_intervened.u], label = "Intervened (do(V=1))", linewidth = 2, linestyle = :dash)
axislegend(ax1)
lines!(ax2, sol_original.t, [u[2] for u in sol_original.u], label = "Original", linewidth = 2)
lines!(ax2, sol_intervened.t, [u[2] for u in sol_intervened.u], label = "Intervened (do(V=1))", linewidth = 2, linestyle = :dash)
axislegend(ax2)
lines!(ax3, sol_original.t, [u[3] for u in sol_original.u], label = "Original", linewidth = 2)
lines!(ax3, sol_intervened.t, [u[3] for u in sol_intervened.u], label = "Intervened (do(V=1))", linewidth = 2, linestyle = :dash)
axislegend(ax3)
lines!(ax4, sol_original.t, [u[2] for u in sol_original.u], label = "Original", linewidth = 2)
lines!(ax4, sol_intervened.t, [u[2] for u in sol_intervened.u], label = "Intervened (do(V=1))", linewidth = 2, linestyle = :dash)
axislegend(ax4)
fig # Only this gets displayed
end
# Compare peak infection
peak_original = maximum([u[2] for u in sol_original.u])
peak_intervened = maximum([u[2] for u in sol_intervened.u])
println("Peak infection rate:")
println(" Original: ", round(peak_original, digits=3))
println(" Intervened (do(V=1)): ", round(peak_intervened, digits=3))
println(" Reduction: ", round((1 - peak_intervened/peak_original) * 100, digits=1), "%")