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
[docs] 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