Final Size, the Herd Immunity Threshold, and Overshoot

Two of the most useful results in epidemic theory follow from R0R_0 alone, without solving the dynamics in detail. The final size relation fixes what fraction of the population is ultimately infected, and the herd immunity threshold fixes the level of immunity at which transmission stops growing. The gap between them is the overshoot: an uncontrolled epidemic does not stop when it reaches herd immunity, it sails past and infects more people than were strictly needed to end it.

Left: the final attack rate rises with R0 and always exceeds the herd immunity threshold 1 − 1/R0; the shaded gap between them is the overshoot. Right: an SIR time course at R0 = 2.5 where the susceptible fraction falls past the 1/R0 threshold down to its final value, the overshoot.
Figure 1. Left: the final attack rate rises with R0 and always exceeds the herd immunity threshold 1 − 1/R0; the shaded gap between them is the overshoot. Right: an SIR time course at R0 = 2.5 where the susceptible fraction falls past the 1/R0 threshold down to its final value, the overshoot.

The herd immunity threshold#

In an SIR model, incidence grows while the effective reproduction number Rt=R0SR_t = R_0 S exceeds one, where SS is the susceptible fraction. The epidemic peaks at the moment R0S=1R_0 S = 1, that is, when the susceptible fraction has fallen to

S\*=1R0.S^\* = \frac{1}{R_0}.

The complementary quantity, the fraction that must be immune for transmission to stop growing, is the herd immunity threshold,

H=11R0.H = 1 - \frac{1}{R_0}.

For R0=2.5R_0 = 2.5 this is 0.60.6: once 60% of the population is immune, each infection produces fewer than one successor on average and incidence turns over. This is the number behind critical vaccination coverage — vaccinate a fraction HH (adjusted for vaccine efficacy) and you prevent an epidemic without anyone being infected.

The final size relation#

The threshold is where incidence peaks, not where the epidemic stops. Infectious people are still circulating at the peak, so transmission continues and the susceptible pool keeps draining below S\*S^\*. For the closed SIR epidemic the total fraction ever infected, the attack rate Z=1S()Z = 1 - S(\infty), satisfies the transcendental final size equation

Z=1eR0Z,Z = 1 - e^{-R_0 Z},

which has a positive solution whenever R0>1R_0 > 1. It depends only on R0R_0, not on the specific rates, which is what makes it so useful: measure the growth rate, infer R0R_0, and you have the eventual attack rate before the epidemic is over. For R0=2.5R_0 = 2.5 the solution is Z0.89Z \approx 0.89, so about 89% of the population is infected in an uncontrolled epidemic.

Overshoot#

Now compare the two numbers. Herd immunity is reached at 1S\*=H=0.61 - S^\* = H = 0.6, but the epidemic infects Z0.89Z \approx 0.89. The difference,

overshoot=ZH,\text{overshoot} = Z - H,

here about 29% of the population, is infected after the herd immunity threshold has already been crossed. These infections are, in a sense, unnecessary: if transmission could be switched off the instant the threshold is reached, the epidemic would end with far fewer total cases. Overshoot is why letting an epidemic burn to natural herd immunity is so costly, and why the timing of any relaxation of control matters — lift measures right at the threshold and the residual momentum still carries a large overshoot. An implementation of the final size and overshoot calculations for applied use is available in the nccovid R package.

A worked example#

For R0=2.5R_0 = 2.5 we solve the final size equation for the attack rate, compute the herd immunity threshold, and take their difference as the overshoot.

In code#

R#

R
R0 <- 2.5

# Solve Z = 1 - exp(-R0 Z) for the attack rate on (0, 1).
final_size <- uniroot(function(z) 1 - exp(-R0 * z) - z,
                      interval = c(1e-9, 1 - 1e-9))$root
herd <- 1 - 1 / R0
overshoot <- final_size - herd

round(c(final_size = final_size, herd_threshold = herd,
        overshoot = overshoot), 3)

Python#

Python
from scipy.optimize import brentq
import numpy as np

R0 = 2.5

# Solve Z = 1 - exp(-R0 Z) for the attack rate on (0, 1).
final_size = brentq(lambda z: 1 - np.exp(-R0 * z) - z, 1e-9, 1 - 1e-9)
herd = 1 - 1 / R0
overshoot = final_size - herd

print(f"final size (attack rate) = {final_size:.3f}")
print(f"herd immunity threshold  = {herd:.3f}")
print(f"overshoot                = {overshoot:.3f}")
final size (attack rate) = 0.893
herd immunity threshold  = 0.600
overshoot                = 0.293

Julia#

Julia
using Roots

R0 = 2.5

# Solve Z = 1 - exp(-R0 Z) for the attack rate on (0, 1).
final_size = find_zero(z -> 1 - exp(-R0 * z) - z, (1e-9, 1 - 1e-9))
herd = 1 - 1 / R0
overshoot = final_size - herd

(final_size = final_size, herd_threshold = herd, overshoot = overshoot)

Why it matters#

The final size relation and the herd immunity threshold turn a single number, R0R_0, into two of the quantities decision-makers ask for first: how many people will be infected, and what immunity is needed to stop it. The overshoot between them is the quantitative case against uncontrolled epidemics — natural herd immunity arrives only after a large excess of infections that flattening the curve or vaccinating ahead of the threshold would avoid. All three follow from the same threshold structure that the next-generation matrix generalizes to structured populations.