Within-Host Dynamics and the Immune Response
The SIR and SEIR models track a pathogen as it spreads between hosts, but the same compartmental thinking describes what happens inside a single host. Here the compartments are target cells, infected cells, and free virus, and the immune system plays the role that recovery and removal play at the population scale. This page builds the standard within-host model and adds an explicit B-cell (antibody) and T-cell (CTL) response, following Nowak and May.
The target-cell model
Let be uninfected target cells, infected cells, and free virus. Target cells are produced at rate , die at per-capita rate , and become infected on contact with virus at rate . Infected cells die at rate and shed virus at rate ; free virus is cleared at rate .
This is the exact analogue of the between-host compartmental model: susceptible target cells replace susceptible hosts, and infected cells replace infectious hosts. The uninfected steady state is , and the infection can establish only when the within-host basic reproductive ratio exceeds one.
The interpretation is the same as at the population scale: counts how many new infected cells one infected cell produces, through the burst of virus it releases, in an otherwise uninfected host.
Adding B-cell and T-cell responses
The immune system removes virus in two complementary ways. Cytotoxic T lymphocytes (CTL) kill infected cells, and antibodies produced by B cells neutralize free virus. We add a CTL compartment that grows in response to infected cells and an antibody compartment that grows in response to virus.
The term is extra killing of infected cells by CTL, and is neutralization of virus by antibody. The effector equations are the same predator-prey logic used elsewhere in ecology: CTL proliferate at rate in proportion to their infected-cell “prey” and decay at rate , while antibody proliferates at rate and decays at rate . This is why within-host immunology and predator-prey dynamics share so much mathematical structure.
Clearance versus persistence
The strength of the adaptive response decides the outcome. When CTL and antibody responses are strong, they drive infected cells and virus down to a low set point, far below the peak — functional clearance. When the adaptive response is weak or absent, the virus is limited only by the supply of target cells and settles at a high, persistent set point, the hallmark of a chronic infection. The figure above contrasts the two regimes for the same virus and the same initial seed, changing only the responsiveness of the effectors.
A worked example
Take scaled parameters , , , , , . The uninfected target-cell level is , so a vigorous infection. Without an effective adaptive response the virus reaches the target-cell-limited set point . With a strong response the antibody set point and the CTL set point both fall as the effectors grow, pushing viral load down by about two orders of magnitude.
In code
R
library(deSolve)
within_host <- function(t, y, p) {
with(as.list(c(y, p)), {
dx <- lam - d * x - beta * x * v
dy <- beta * x * v - a * y - pk * y * z
dv <- k * y - u * v - q * v * w
dz <- c * y * z - b * z
dw <- g * v * w - h * w
list(c(dx, dy, dv, dz, dw))
})
}
base <- c(lam = 1, d = 0.1, beta = 1, a = 0.5, k = 5, u = 3,
pk = 1, q = 1, b = 0.3, h = 0.3)
y0 <- c(x = 10, y = 0, v = 1e-2, z = 1e-3, w = 1e-3)
t <- seq(0, 200, by = 0.5)
strong <- ode(y0, t, within_host, c(base, c = 10, g = 10))
weak <- ode(y0, t, within_host, c(base, c = 0, g = 0))
tail(strong[, "v"], 1) # low set point (clearance)
tail(weak[, "v"], 1) # high set point (persistence)
Python
import numpy as np
from scipy.integrate import solve_ivp
lam, d, beta = 1.0, 0.1, 1.0
a, k, u = 0.5, 5.0, 3.0
p, q, b, h = 1.0, 1.0, 0.3, 0.3
def rhs(t, y, c, g):
x, yi, v, z, w = y
return [lam - d*x - beta*x*v,
beta*x*v - a*yi - p*yi*z,
k*yi - u*v - q*v*w,
c*yi*z - b*z,
g*v*w - h*w]
x0 = lam / d
R0 = beta * k * x0 / (a * u)
y0 = [x0, 0.0, 1e-2, 1e-3, 1e-3]
strong = solve_ivp(rhs, [0, 200], y0, args=(10.0, 10.0),
method="LSODA", rtol=1e-8, atol=1e-10, max_step=0.5)
weak = solve_ivp(rhs, [0, 200], y0, args=(0.0, 0.0),
method="LSODA", rtol=1e-8, atol=1e-10, max_step=0.5)
print(f"within-host R0 = {R0:.1f}")
print(f"strong immunity: set-point v = {strong.y[2][-1]:.3f}")
print(f"no adaptive immunity: set-point v = {weak.y[2][-1]:.3f}")
within-host R0 = 33.3
strong immunity: set-point v = 0.029
no adaptive immunity: set-point v = 3.233
Julia
using DifferentialEquations
function within_host!(du, y, p, t)
x, yi, v, z, w = y
lam, d, beta, a, k, u, pk, q, b, h, c, g = p
du[1] = lam - d*x - beta*x*v
du[2] = beta*x*v - a*yi - pk*yi*z
du[3] = k*yi - u*v - q*v*w
du[4] = c*yi*z - b*z
du[5] = g*v*w - h*w
end
y0 = [10.0, 0.0, 1e-2, 1e-3, 1e-3]
strong = solve(ODEProblem(within_host!, y0, (0.0, 200.0),
(1,0.1,1,0.5,5,3,1,1,0.3,0.3, 10, 10)), Rosenbrock23())
last(strong.u)[3] # low viral set point (stiff solver)
Why it matters
Within-host models connect the molecular events of infection to the epidemiology of transmission. The viral load they predict is what a diagnostic qPCR measures, what shapes the infectiousness profile over the course of an infection, and what antiviral therapy tries to suppress. They also set up the multi-scale view that links within-host selection to the between-host evolution of virulence: a virus that replicates fast may reach a high transmissible load but provoke a stronger immune response or kill its host sooner, the same trade-off written one scale down.