Source code for laser_measles.biweekly.components.process_infection
from typing import Any
import numpy as np
from pydantic import Field
from laser_measles.base import BaseLaserModel
from laser_measles.components import BaseInfectionParams
from laser_measles.components import BaseInfectionProcess
from laser_measles.mixing.gravity import GravityMixing
class InfectionParams(BaseInfectionParams):
"""Parameters specific to the infection process component."""
beta: float = Field(
default=1 * 8 / 14, description="Base transmission rate (infections per day)", ge=0.0
) # beta = R0 / (mean infectious period)
seasonality: float = Field(default=0.0, description="Seasonality factor, default is no seasonality", ge=0.0, le=1.0)
season_start: int = Field(default=0, description="Season start tick (0-25)", ge=0, le=25)
mixer: Any = Field(default_factory=lambda: GravityMixing(), description="Mixing object")
@property
def beta_per_tick(self) -> float:
return (self.beta * 365) / 26
[docs]
class InfectionProcess(BaseInfectionProcess):
"""
Component for simulating the spread of infection in the model.
This class implements a stochastic infection process that models disease transmission
between different population groups. It uses a seasonally-adjusted transmission rate
and accounts for mixing between different population groups.
The infection process follows these steps:
1. Calculates expected new infections based on:
- Base transmission rate (beta)
- Seasonal variation
- Population mixing matrix
- Current number of infected individuals
2. Converts expected infections to probabilities
3. Samples actual new infections from a binomial distribution
4. Updates population states:
- Moves current infected to recovered (configurable recovery period)
- Adds new infections to infected population
- Removes new infections from susceptible population
Parameters
----------
model : object
The simulation model containing population states and parameters
verbose : bool, default=False
Whether to print detailed information during execution
params : InfectionParams | None, default=None
Component-specific parameters. If None, will use default parameters
Notes
-----
The infection process uses a configurable recovery period and seasonal
transmission rate that varies sinusoidally over time.
"""
def __init__(self, model: BaseLaserModel, verbose: bool = False, params: InfectionParams | None = None) -> None:
super().__init__(model, verbose)
if params is None:
params = InfectionParams()
self.params = params
self.params.mixer.scenario = model.scenario
def __call__(self, model: BaseLaserModel, tick: int) -> None:
# state counts
states = model.patches.states
# prevalence in each patch
prevalence = states.I # / states.sum(axis=0) # I_j / N_j
lambda_i = (
self.params.beta_per_tick
* (1 + self.params.seasonality * np.sin(2 * np.pi * (tick - self.params.season_start) / 26.0))
* prevalence
) @ self.params.mixer.mixing_matrix
# normalize by the population of the patch
lambda_i /= states.sum(axis=0)
prob = 1 - np.exp(-lambda_i) # already per-susceptible
dI = model.prng.binomial(states[0], prob).astype(states.dtype)
# move all currently infected to recovered (using configurable recovery period)
states[2] += states[1]
states[1] = 0
# update susceptible and infected populations
states[1] += dI # add new infections to I
states[0] -= dI # remove new infections from S
return