Strain stratification

Summer provides a special disease-strain stratification class (StrainStratification) that treats each stratum as a separate strain of the infection. This allows you to model multiple strains of an infection (or multiple infectious diseases with similar compartmental structures) which are able to infect people separately.

They key difference between StrainStratification vs. a normal Stratification is that the strain stratification adjusts the count of infectious people per strain. For example, in a normal stratification with an age-based split into ‘young’ and ‘old’ you will have frequency-dependent infection flows calculated as follows:

# Find a common force of infection for both young and old
num_infected = num_old_infected + num_young_infected
force_of_infection = contact_rate * num_infected / num_pop

# Use that common force of infection to get flow rates for young/old infection
infect_rate_young = force_of_infection * num_young_susceptible
infect_rate_old = force_of_infection * num_old_susceptible

Consider now how this will be calculated for two strains (“mild” and “wild”) when applied to an unstratified susceptible compartment:

# Find a different force of infection for mild and wild
force_of_infection_mild = contact_rate * num_infected_mild / num_pop
force_of_infection_wild = contact_rate * num_infected_wild / num_pop

# Use the different force of infection values to get flow rates for mild/wild infection
infect_rate_mild = force_of_infection_mild * num_susceptible
infect_rate_wild = force_of_infection_wild * num_susceptible

Let’s work through a code example. For starters, let’s create an SIR model:

[1]
from summer2 import CompartmentalModel
import pandas as pd

pd.options.plotting.backend = "plotly"

def build_model():
    """Returns a model for the stratification examples"""
    model = CompartmentalModel(
        times=[1990, 2010],
        compartments=["S", "I", "R"],
        infectious_compartments=["I"],
        timestep=0.1,
    )

    # Add people to the model
    model.set_initial_population(distribution={"S": 990, "I": 10})

    # Susceptible people can get infected.
    model.add_infection_frequency_flow(name="infection", contact_rate=2, source="S", dest="I")

    # Infectious people take 3 years, on average, to recover.
    model.add_transition_flow(name="recovery", fractional_rate=1/3, source="I", dest="R")

    # Add an infection-specific death flow to the I compartment.
    model.add_death_flow(name="infection_death", death_rate=0.05, source="I")
    return model

Lets see what this model looks like without any stratifications:

[2]
model = build_model()
model.run()
model.get_outputs_df().plot()

Now we can add a strain stratification to the infected (I) and recovered (R) compartments. We will assume immunity to one strain gives you immunity to the other.

[3]
from summer2 import StrainStratification

strata = ['mild', 'wild']
strat = StrainStratification(name="strain", strata=strata, compartments=['I', 'R'])

# At the start of the simulation, 20% of infected people have wild strain.
strat.set_population_split({'mild': 0.8, 'wild': 0.2})

model = build_model()
model.stratify_with(strat)
model.run()
model.get_outputs_df().plot()

Note that despite the stratification, the model results are the same in aggregate, because we have not applied any adjustments to the flows or strata infectiousness yet. Let’s do that:

[4]
from summer2 import StrainStratification, Multiply

strata = ['mild', 'wild']
strat = StrainStratification(name="strain", strata=strata, compartments=['I', 'R'])

# Again, 20% of infected people have wild strain at the start.
strat.set_population_split({'mild': 0.8, 'wild': 0.2})

# The wild strain kills at 1.2x the rate as the mild strain does.
strat.set_flow_adjustments("infection_death", {
    "mild": None,
    "wild": Multiply(1.2),
})


# Wild strain is twice as infectious than the mild strain (or equivalently, people are twice as susceptible to it).
strat.set_flow_adjustments("infection", {
    "mild": None,
    "wild": Multiply(2),
})


model = build_model()
model.stratify_with(strat)
model.run()
model.get_outputs_df().plot()
[ ]