Source code for episia.models.seir
"""
models/seir.py - SEIR compartmental epidemic model.
dS/dt = -β * S * I / N
dE/dt = β * S * I / N - σ * E
dI/dt = σ * E - γ * I
dR/dt = γ * I
The latent (exposed) compartment E accounts for the incubation period,
making SEIR more realistic than SIR for diseases like influenza or COVID-19.
References
----------
Anderson & May (1991). Infectious Diseases of Humans. Oxford University Press.
"""
from __future__ import annotations
from typing import Any, Dict, List
import numpy as np
from .base import CompartmentalModel
from .parameters import SEIRParameters
[docs]
class SEIRModel(CompartmentalModel):
"""
SEIR epidemic model with explicit incubation compartment.
Args:
parameters: SEIRParameters instance.
Example::
from episia.models import SEIRModel
from episia.models.parameters import SEIRParameters
params = SEIRParameters(
N=1_000_000,
I0=1,
E0=10,
beta=0.35,
sigma=1/5.2, # COVID-19-like incubation
gamma=1/14,
t_span=(0, 365),
)
result = SEIRModel(params).run()
result.plot().show()
"""
[docs]
def __init__(self, parameters: SEIRParameters):
if not isinstance(parameters, SEIRParameters):
raise TypeError(
f"Expected SEIRParameters, got {type(parameters).__name__}."
)
super().__init__(parameters)
@property
def compartment_names(self) -> List[str]:
return ["S", "E", "I", "R"]
def _initial_state(self) -> np.ndarray:
p = self.parameters
return np.array([p.S0, p.E0, p.I0, p.R0_init])
def _derivatives(self, t: float, y: np.ndarray) -> np.ndarray:
S, E, I, R = y
N = float(self.parameters.N)
beta = self.parameters.beta
sigma = self.parameters.sigma
gamma = self.parameters.gamma
new_exposed = beta * S * I / N
new_infected = sigma * E
dS = -new_exposed
dE = new_exposed - new_infected
dI = new_infected - gamma * I
dR = gamma * I
return np.array([dS, dE, dI, dR])
def _compute_metrics(
self,
t: np.ndarray,
solution: np.ndarray,
) -> Dict[str, Any]:
S, E, I, R = solution
N = float(self.parameters.N)
p = self.parameters
peak_idx = int(np.argmax(I))
peak_infected = float(I[peak_idx])
peak_time = float(t[peak_idx])
final_size = float(R[-1]) / N
# Peak exposed
peak_exposed_idx = int(np.argmax(E))
peak_exposed = float(E[peak_exposed_idx])
from .solver import estimate_herd_immunity
hit = estimate_herd_immunity(p.r0)
return {
"r0": p.r0,
"peak_infected": peak_infected,
"peak_time": peak_time,
"final_size": final_size,
"peak_exposed": peak_exposed,
"herd_immunity_threshold": hit,
"t_incubation": p.t_incubation,
"t_infectious": p.t_infectious,
}
__all__ = ["SEIRModel"]