Diseases are the core of most Starsim models: they define the states an agent can be in (e.g. susceptible, infected, recovered), how agents progress between those states, and — for communicable diseases — how infection spreads across the network. This page covers the disease class hierarchy, the key states and parameters every infection shares (including rel_sus and rel_trans), and the patterns for customizing or building your own disease.
Includes transmission logic via the infect() method
Used for all communicable diseases
Handles network-based transmission automatically
Applies network-specific betas and agent susceptibility/transmissibility
Important: Almost all diseases should inherit from ss.Infection. Do not write your own infect() method unless you have very specific requirements - the built-in method correctly handles:
Looping over agents in each network
Applying network- and disease-specific transmission probabilities
Managing agent transmissibility and susceptibility
Mixing pool logic
Key implementation methods
Method
Purpose
When to override
define_states()
Initialize disease states (S, I, R, etc.)
Always for custom diseases
set_prognoses()
Set outcomes for newly infected people
(Almost) always for custom diseases
step_state()
Update states each timestep
When adding new state transitions
step_die()
Handle deaths
When disease has custom states
infect()
Handle transmission
Rarely - use built-in version
Core states and parameters
Every ss.Infection shares a common set of per-agent states and parameters that govern transmission. The most important are:
Base transmission probability per contact (per network)
init_prev
parameter
Initial fraction of the population infected
Susceptibility and transmissibility
When ss.Infection processes transmission, the probability that infection passes along a single network edge from an infectious source to a susceptible target is, in essence:
where \(\beta\) is the per-contact transmission probability for that network (scaled to the timestep). This factorization is the key invariant to understand:
Only infectious agents contribute their rel_trans; everyone else contributes 0.
Only susceptible agents contribute their rel_sus; everyone else contributes 0.
rel_sus and rel_trans both default to 1.0, so by default transmission is governed entirely by beta and the network structure.
These two arrays are the standard “knobs” that the rest of the framework turns to represent heterogeneity in risk. For example:
A vaccine product typically reduces rel_sus for vaccinated agents.
A treatment might reduce rel_trans for treated agents.
A connector between two diseases might increase rel_sus to one disease for agents infected with another (co-infection).
Because they’re ordinary per-agent arrays, you can also set them directly. Here we fully protect everyone under 20 by setting their rel_sus to 0, and see that they avoid infection (aside from the handful seeded as infected at the start via init_prev):
import starsim as ssss.options(jupyter=True)sir = ss.SIR(init_prev=0.01)sim = ss.Sim(n_agents=2000, diseases=sir, networks='random', copy_inputs=False, verbose=0)sim.init()# Fully protect under-20s: rel_sus = 0 means they cannot acquire infectionyoung = (sim.people.age <20).uidsolder = (sim.people.age >=20).uidssir.rel_sus[young] =0.0sim.run()ever_infected = sir.ti_infected.notnanprint(f'Protected (under 20): {ever_infected[young].sum()} of {len(young)} ever infected')print(f'Unprotected (20+): {ever_infected[older].sum()} of {len(older)} ever infected')
Protected (under 20): 2 of 609 ever infected
Unprotected (20+): 1340 of 1391 ever infected
Note
beta can be specified per network and per direction. For sexually transmitted infections, for example, beta={'mf': [0.25, 0.15]} sets different male→female and female→male transmission probabilities on the mf network. See the Networks page for more.
Warning
beta is a per-contact transmission probability — a bare float (or a dict/list of floats), not a rate. Do not wrap it in ss.peryear() or ss.perday(): doing so reinterprets the number as a hazard and rescales it by the timestep, corrupting the transmission scale. Use beta=0.1, not beta=ss.perday(0.1). (Most other per-agent event parameters, such as p_death, are probabilities or distributions; the time-aware rate types are for quantities like birth and death rates. See Time.)
Implementation patterns
Pattern 1: Extending existing diseases
When you need to modify an existing disease model, inherit from it and override specific methods:
import starsim as ssss.options(jupyter=True)class MyCustomSIR(ss.SIR):def__init__(self, **kwargs):super().__init__()# Add custom parametersself.define_pars(my_param=0.5)self.update_pars(**kwargs)def set_prognoses(self, uids, sources=None):# Custom progression logicsuper().set_prognoses(uids, sources)# Additional custom logic here
Pattern 2: Adding new states
To add states to an existing disease:
class MySEIR(ss.SIR):def__init__(self, **kwargs):super().__init__()# Add new parametersself.define_pars(dur_exp=ss.lognorm_ex(0.5))self.update_pars(**kwargs)# Add new statesself.define_states( ss.BoolState('exposed', label='Exposed'), ss.FloatArr('ti_exposed', label='Time of exposure'), )@propertydef infectious(self):# Define who can transmit (both infected and exposed)returnself.infected |self.exposeddef step_state(self):# Call parent state updates firstsuper().step_state()# Add custom state transitions transitioning =self.exposed & (self.ti_infected <=self.ti)self.exposed[transitioning] =Falseself.infected[transitioning] =True