diseases
Diseases: the base class (ss.Disease), diseases with transmission written as code (ss.AgentDisease), the record of who infected whom (InfectionLog), and the textbook models (ss.SIR, ss.SIS, ss.SEIR)
ss.Disease exists so that one class can hold a disease declared from states and transitions, written as code, or both. A declared disease runs with any solver; ss.AgentDisease and other code that works on individual agents runs as agents.
Used with: agents and compartments; declared and coded modules
Classes
| Name | Description |
|---|---|
| AgentDisease | A transmitted disease written as code, as agents: the plain array operations of an infection, each a method to override |
| BuiltAfterInit | Build what a disease declares in its __init__() (define_states(), define_transitions()) once that has finished |
| Disease | A disease: states people are in and the transitions between them (declared), code, or both |
| InfectionLog | A record of who infected whom, and when: kept by a disease when an analyzer needs it (e.g. ss.infection_log), to trace chains of transmission |
| SEIR | An SEIR model: as ss.SIR, with an exposed (latent) state before people become infectious |
| SIR | An SIR model: susceptible, infected, and recovered, optionally with deaths from infection |
| SIS | An SIS model: people recover from infection and are susceptible again |
AgentDisease
diseases.AgentDisease(pars=None, strains=None, **kwargs)A transmitted disease written as code, as agents: the plain array operations of an infection, each a method to override
Gives the susceptible, infected (and infectious, by default the same), rel_sus, rel_trans, and ti_infected states; seeds init_prev of the people at the start; and each step transmits from transmissibility() to susceptibility() over the sim’s networks (transmit()), counts the new cases in the infection result, and gives them their prognoses (set_prognoses()). A subclass overrides only what it needs: usually set_prognoses(), for what happens to those infected, and step_state(), for how their states change; those infected before birth (over ss.PrenatalNet) go to set_congenital() instead, which a disease with outcomes at birth overrides. Anything else is plain code on the arrays. The betas of the networks are route_betas(), which can be overridden too (e.g. by sex).
With strains=ss.Strains(...), each person carries one strain at a time (self.strain, the index of the strain of their current or last infection, -1 if none), which the disease’s code reads (e.g. in set_prognoses(), with strain_value() for a strain’s values). The strains are transmitted in one pass, each by those infectious with it at its rel_trans; each new case is infected by one of those who reached them in the step, at random, and takes their strain, unless protected against it (strain_sus(): protection from ss.immunity(due_to=<state>, protection=matrix) by the strain they had, or the disease’s own), as a declared disease’s strains are passed on.
An infection in a step happens at its end, so a course scheduled from it starts at the next step: schedule a move dur steps after it at self.ti + 1 + dur, in an ss.TimeArr, and take it when due(). The seeds are dated by the step before the first (self.ti is -1 while they are set), so they have the same course.
A disease that can be written as states and transitions is shorter declared (see ss.SIR), and runs with every solver.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| pars | dict |
parameters, e.g. beta and init_prev (a fraction, or an ss.bernoulli) |
None |
| strains | ss.Strains |
the strains, carried one at a time, sharing the disease’s states (also define_strains()) |
None |
| kwargs | dict |
parameters |
Example
class SIR(ss.AgentDisease):
def __init__(self, **kwargs):
super().__init__()
self.define_pars(beta=ss.perday(0.05), init_prev=0.01, dur_inf=10)
self.define_states(ss.BoolState('recovered'), ss.TimeArr('ti_recovered'))
self.update_pars(**kwargs)
def step_state(self):
done = (self.infected & self.ti_recovered.due()).uids
self.infected[done] = False
self.recovered[done] = True
def set_prognoses(self, uids, sources=None):
super().set_prognoses(uids, sources)
self.ti_recovered[uids] = self.ti + 1 + self.pars.dur_inf
sim = ss.Sim(diseases=SIR(), networks='random', unit='day', stop=100).run()
# With two strains, the second more transmissible; recovering from one protects fully against it, and partly against the other
class SIRS(SIR):
def step_state(self):
super().step_state()
self.susceptible[self.recovered] = True # Protected only by the immunity of their strain
def set_prognoses(self, uids, sources=None):
super().set_prognoses(uids, sources)
self.recovered[uids] = False
strains = ss.Strains(['H1', 'H3'], rel_trans=[1, 1.5], immunity=ss.immunity(due_to='recovered', protection=[[1, 0.5], [0.5, 1]]))
sim = ss.Sim(diseases=SIRS(strains=strains, name='flu'), networks='random', unit='day', stop=100).run()
sim.diseases.flu.strain # The strain of each person's current or last infectionMethods
| Name | Description |
|---|---|
| define_strains | Give the disease strains (ss.Strains, also as strains=), carried one at a time: the strain of each person (self.strain, added when the |
| infect | Infect some agents now: those susceptible, counted in infection (except the seeds, which are prevalent), and given their prognoses |
| init_post | Seed init_prev of the people born by the start (not the fetuses of pregnancies begun before it), dated by the step before the first |
| init_results | The counts of the states (one for each ss.BoolState), and the prevalence and new infections |
| make_naive | Reset agents to never having had the disease, for dynamic rescaling: every state to its default, except skip_states |
| not_naive | The agents who have ever been infected, for dynamic rescaling (see ss.Sim.rescale()) |
| record_states | The counts of the states, and the prevalence, at the start of the step |
| set_congenital | What happens to those infected before birth (over ss.PrenatalNet; see set_outcomes()): by default, the same as anyone infected (set_prognoses()) |
| set_outcomes | Give those just infected their outcomes: those infected before birth (age 0 or less, e.g. over ss.PrenatalNet) by set_congenital(), the rest by set_prognoses() |
| set_prognoses | What happens to those infected, at the end of this step: by default, they are infected and no longer susceptible; override this for more |
| step | Transmit (transmit()), and infect the new cases |
| strain_sus | The fraction of their susceptibility to strain k that each agent (by UID) keeps: 1 − protection from each ss.immunity of the strains, |
| strain_value | The value of key (given to ss.Strains, e.g. dur=[0.5, 1]) for the strain each of some agents carries (1 if they have none) |
| susceptibility | The susceptibility of each agent, by UID: rel_sus, 0 for those not susceptible |
| transmissibility | The transmissibility of each agent, by UID: rel_trans, 0 for those not infectious |
| transmit | Who is infected in this step: by default, from transmissibility() to susceptibility() (see ss.Disease.transmit()); nobody, for a disease with no beta (e.g. one infected only from code, with infect()) |
| update_pars | As ss.Module.update_pars(), and strains= for define_strains(), so a subclass that passes its keywords on here takes it too (e.g. ssl.AgentSIR(strains=...)) |
define_strains
diseases.AgentDisease.define_strains(strains)Give the disease strains (ss.Strains, also as strains=), carried one at a time: the strain of each person (self.strain, added when the sim is initialized, and set for those infected by transmission, at the start, and by infect(); code that infects people itself sets it)
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| strains | Strains | the strains; those infected at the start, or by infect() with no strain, have one drawn by its fractions |
required |
infect
diseases.AgentDisease.infect(uids, log=True, strain=None)Infect some agents now: those susceptible, counted in infection (except the seeds, which are prevalent), and given their prognoses
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | uids | the agents (e.g. imported cases); those not susceptible are left as they are | required |
| log | bool |
record them in an ss.infection_log, with no source (-1) |
True |
| strain | str / int / array |
with strains, the strain they are infected with: its name or index, or one for each agent; by default drawn by the strains’ fractions |
None |
Returns
| Name | Type | Description |
|---|---|---|
| The agents infected |
Example
flu.infect(sim.people.auids[:20], strain='H3') # 20 imported cases of H3init_post
diseases.AgentDisease.init_post()Seed init_prev of the people born by the start (not the fetuses of pregnancies begun before it), dated by the step before the first
init_results
diseases.AgentDisease.init_results()The counts of the states (one for each ss.BoolState), and the prevalence and new infections
make_naive
diseases.AgentDisease.make_naive(uids, skip_states=('rel_sus', 'rel_trans'))Reset agents to never having had the disease, for dynamic rescaling: every state to its default, except skip_states
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | uids | the agents | required |
| skip_states | list |
the names of the states to keep (by default rel_sus and rel_trans, which other modules, e.g. vaccines, may have set) |
('rel_sus', 'rel_trans') |
not_naive
diseases.AgentDisease.not_naive()The agents who have ever been infected, for dynamic rescaling (see ss.Sim.rescale())
record_states
diseases.AgentDisease.record_states()The counts of the states, and the prevalence, at the start of the step
set_congenital
diseases.AgentDisease.set_congenital(uids, sources=None)What happens to those infected before birth (over ss.PrenatalNet; see set_outcomes()): by default, the same as anyone infected (set_prognoses())
A disease with outcomes at birth overrides this, e.g. ssl.CongenitalDisease (import starsim.library as ssl), which schedules a birth outcome (a stillbirth, a congenital infection) for each.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | uids | the people infected before birth | required |
| sources | array |
who infected them, if known | None |
set_outcomes
diseases.AgentDisease.set_outcomes(uids, sources=None)Give those just infected their outcomes: those infected before birth (age 0 or less, e.g. over ss.PrenatalNet) by set_congenital(), the rest by set_prognoses()
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | uids | the agents infected | required |
| sources | array |
who infected them, if known | None |
set_prognoses
diseases.AgentDisease.set_prognoses(uids, sources=None)What happens to those infected, at the end of this step: by default, they are infected and no longer susceptible; override this for more
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | uids | the agents infected | required |
| sources | array |
who infected them, if known | None |
step
diseases.AgentDisease.step()Transmit (transmit()), and infect the new cases
strain_sus
diseases.AgentDisease.strain_sus(k)The fraction of their susceptibility to strain k that each agent (by UID) keeps: 1 − protection from each ss.immunity of the strains, for those in its state (e.g. due_to='recovered') by the strain they had; a disease with protection of its own (e.g. from antibodies) overrides this
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| k | int |
the index of the strain | required |
strain_value
diseases.AgentDisease.strain_value(key, uids)The value of key (given to ss.Strains, e.g. dur=[0.5, 1]) for the strain each of some agents carries (1 if they have none)
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| key | str |
the name of the value | required |
| uids | array |
the agents | required |
susceptibility
diseases.AgentDisease.susceptibility()The susceptibility of each agent, by UID: rel_sus, 0 for those not susceptible
transmissibility
diseases.AgentDisease.transmissibility()The transmissibility of each agent, by UID: rel_trans, 0 for those not infectious
transmit
diseases.AgentDisease.transmit(rel_trans=None, rel_sus=None, beta=None)Who is infected in this step: by default, from transmissibility() to susceptibility() (see ss.Disease.transmit()); nobody, for a disease with no beta (e.g. one infected only from code, with infect())
update_pars
diseases.AgentDisease.update_pars(pars=None, **kwargs)As ss.Module.update_pars(), and strains= for define_strains(), so a subclass that passes its keywords on here takes it too (e.g. ssl.AgentSIR(strains=...))
BuiltAfterInit
diseases.BuiltAfterInit()Build what a disease declares in its __init__() (define_states(), define_transitions()) once that has finished
Disease
diseases.Disease(
*args,
pars=None,
strains=None,
stratify=None,
solver=None,
_names=None,
**kwargs,
)A disease: states people are in and the transitions between them (declared), code, or both
Declared from its states and transitions, no methods need to be defined, and the disease can be run with any solver (see ss.Sim):
S, I, R = ss.states(3)
sir = ss.Disease(
infection = S >> I @ 0.3 * I,
recovery = ss.Transition(I, R, dur=10),
init_prev = 0.01,
)
The same disease can be declared in a class body (the class form), which can also add behavior by defining methods; ss.SIR, ss.SIS, and ss.SEIR are written this way.
A disease can also be written as code: a subclass that declares nothing, defines its per-agent arrays (define_states()), and changes them in its own step_state() and step(); it runs as agents. One class can mix the two, e.g. a declared natural history and a coded array that its step() updates after super().step() has taken the declared transitions. Each lifecycle method runs the declared part through the disease’s engine (which, for a disease that declares nothing, has nothing to run), so an override calls super() as for any module. ss.AgentDisease adds transmission written as code: transmit(rel_trans=..., rel_sus=...), the same array operation as a declared transmission’s (see transmit()).
Note: CRN safe as agents.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| args | list |
transitions and outcomes, named from the variables they are assigned to (one by one, or in a list) | |
| pars | dict |
parameter values by name | None |
| transitions | list |
transitions, as a list (for longer models) | |
| results | list |
extra results: sums and ratios of states | |
| outcomes | list |
ss.outcome objects |
|
| init_prev | float |
the initial prevalence, a fraction of people (up to 1), seeded through the transmission (by default 0.01, unless a state is given an initial value) | |
| init | dict |
the fraction of people who start in each state, e.g. dict(S=0.90, I=0.01, R=0.09), and, for a state that is not people, its amount, which can be read from the people (e.g. dict(Sv=m*N), with N = (S + I).n); each value can be an ss.by or a function of the sim (in the class form, init = dict(I=0.01) in the class body; likewise init_n and init_prev) |
|
| init_n | int / dict |
instead, a number of people: one number is seeded through the transmission, as init_prev is (e.g. init_n=10, a parameter, like init_prev); a dict is the number who start in each state, e.g. dict(I=1) for one person |
|
| default_state | str / State |
the state that everyone not put elsewhere starts in, and is born into (its name, or the state): by default, the one named S or susceptible, else the first state of people made (in the class form, default_state = 'N' in the class body). A state with a name of its own given under another keyword (e.g. first=S) raises, rather than being renamed |
|
| strains | Strains | its strains (ss.Strains), which share its states; then dis.strains |
None |
| stratify | dict |
the dimensions, as stratify() takes them, e.g. dict(age={0: 0.3, 15: 0.6, 65: 0.1}, risk=ss.levels(['low', 'high'], states=[I])) |
None |
| solver | abm | how this disease runs as agents, whatever the sim’s solver says, e.g. ss.solvers.abm(step='wholestep') (see below); an error for a compartmental run |
None |
| kwargs | dict |
one keyword per state, transition, parameter, result, function, or modifier (ss.modify(), of its own quantities, e.g. fading=ss.modify('rel_trans', multiply=0.5, who=I)); also name, label, and dt |
A disease’s own solver= is useful when one disease of a sim needs a different way of running as agents from the others:
ss.solvers.abm(step='wholestep'): by steps, with rates applied over whole steps to those in the state at the start of the step, as code withss.bernoulli()does; faster, and a little less exact when the step is coarsess.solvers.abm(step='events'): event by event, in continuous time- by default: the sim’s, if it names a step; else the class’s
suggested_solver(e.g.suggested_solver = ss.solvers.abm(step='wholestep')in the class body); else'midstep'(seess.solvers.abm()andss.solvers.agent_solver())
The results are the number in each state (I), the flow along each transition by its name (infection, with its cumulative total as res.infection.cum), and the results and outcomes declared (e.g. active=(E + I).frac). Its infected states also give the results infected, the number in them, and prevalence, their fraction of everyone in the disease’s states, unless it defines its own: those of its force of infection (E and I in S >> E @ beta*I), or those it says are infected (infected = E + I + H, ss.State(infected=True), or ss.states('S E I H R', infected='E I H')).
Examples
# Operator form: declare the states, then the transitions
S, I, R = ss.states(3)
infection = S >> I @ 0.3 * I
recovery = I >> R @ ss.days(10)
sir = ss.Disease(infection, recovery, init_prev=0.01)
ss.Sim(sir, stop=100).run().plot()
# Class form: for when there is behavior that is not a transition
class SIR(ss.Disease):
S, I, R = ss.states(3)
infection = S >> I @ 0.3 * I
recovery = I >> R @ ss.days(10)
# Written as code: a disease that is not transmitted
class Anemia(ss.Disease):
def __init__(self, **kwargs):
super().__init__()
self.define_pars(p_onset=ss.bernoulli(0.01))
self.update_pars(**kwargs)
self.define_states(ss.BoolState('anemic', label='Anemic'))
def step_state(self):
self.anemic[self.pars.p_onset.filter()] = TrueAttributes
| Name | Description |
|---|---|
| abm | Whether it runs as agents |
| custom_transmit | Whether the disease is declared and has its own transmit(), which the engine calls for its transmissions |
| states | Its declared states (ss.State) by name, e.g. 'second_line' in tb.states or [s for s in tb.states.values() if s.people]; empty for a disease written as code. (The arrays behind them are state_dict.) |
| strats | Its stratifications by name, e.g. tb.strats.age.edges or tb.strats.risk.labels (as a condition, a level is tb.age) |
| transitions | The table of declared transitions; printing it also shows the matrix of sources by destinations |
Methods
| Name | Description |
|---|---|
| R0 | The basic reproduction number, from the next-generation matrix at the disease-free state |
| Rt | The reproduction number over the course of a run, as a result: the number one infectious person infects at each time point |
| add_callback | Call fn(uids) at the end of each step with the agents who went through a declared transition or event of this disease in it, from a module written as code (agents only) |
| add_code_pars | Parameters its own code reads, not its graph’s: see define_pars() |
| also_happened | As agents, others an event (a transition, by name) also happens to, as UIDs (e.g. the fetuses of a mother at a delivery); None for nobody else |
| build_graph | Build the graph from what was declared: when the disease is made, or for one declared in its init(), after that (see BuiltAfterInit) |
| burnin_plan | People part way through at the start: None for none (the default), or the plan for them (see ss.Pregnancy.burnin_plan() and ss.solvers.Agents.backfill_burnin()) |
| call_transmit | The disease’s own transmit() for one of its transmissions, with or without the transition as its argument (see transmit()) |
| check_par_members | A parameter given by keyword under the name it already has (name=), and read by a transition, as if it were a result (e.g. eir=eir) is an error: a result needs a name of its own |
| compartmental_form | A module that runs only as agents unless it defines this: make its small compartmental form, used only when a compartmental solver is named, and return the note that says what it leaves out |
| current_state | The state each agent is in, as a read-only ss.IntArr (by UID) of its index among states, or -1 for none |
| current_value | rel_sus or rel_trans (key) of each agent with the modifiers and products active on it applied (ss.modify(dis.rel_sus, ...)), as agents: |
| declare | Add declared members (states, transitions, results), to be built with the rest; see define_transitions() |
| define_pars | Create or merge parameters; see ss.Module.define_pars() |
| define_results | Define results: for a declared disease, sums and ratios of states by name (e.g. prevalence=I/(S + I + R), or active=(E + I).frac); otherwise ss.Result objects (see ss.Module.define_results()) |
| define_states | Define its states: declared states (ss.State), or for a disease written as code, arrays (see ss.Module.define_states()) |
| define_transitions | Define its transitions, in its __init__(), after its states |
| describe | The resolved model, with every guessed default marked, as text that shows itself (ss.display.Text), as diagram() is |
| diagram | The state-transition diagram: drawn as a Matplotlib figure, or as the text of a Mermaid flowchart (https://mermaid.js.org) |
| equations | The ODE system of the declared transitions, with symbolic parameter names, as text that shows itself (ss.display.Text; print() it in a script) |
| finish_step | Once the step’s flows are recorded (and the analyzers have run), its engine starts counting them afresh |
| has_structure | Whether these keywords declare part of a model, not just a parameter’s value |
| infect | Infect some agents now, from code (e.g. a strain introduced on a date): those in the first state move where its transmission puts them, counted in its flow and dated as its infections are (ti_I.isclose(sim.ti)), with a source of -1 in an ss.infection_log |
| init_graph | Build the graph of declared states and transitions; see ss.graph.Graph |
| init_post | Initialize the values of the states, and set the initial conditions of the declared states |
| init_pre | Link the disease to the sim, create its engine and objects, initialize results, and start its infection log if an analyzer needs it (e.g. ss.infection_log); see ss.Module.init_pre() |
| make_children | As agents, the children of the parents who went through a transition with births (ss.birth(when=)); returns their UIDs |
| make_naive | Put agents back in the first state, as if they had never had the disease; for dynamic rescaling (see ss.Sim.rescale()) |
| move | Move people between this disease’s states from code, the same way under every solver: ss.move() with the disease given |
| not_naive | The agents who have left the first state (e.g. who are no longer susceptible), for dynamic rescaling (see ss.Sim.rescale()) |
| on_death | What happens to agents when they die: as agents, they leave every declared state |
| record_events | The flows of the step, and the results worked out from them |
| record_states | The counts of the states at the start of the step; for a disease written as code, the count of each ss.BoolState (see ss.Module.record_states()) |
| reuse | States and transitions that already belong to another disease are copied (together, so they refer to each other, and with their parameters), so one set can build several independent diseases |
| route_betas | The beta of each of the sim’s networks (by name), as a pair (p1 to p2, p2 to p1), that transmit(rel_trans=..., rel_sus=...) uses when not given beta=: by default pars.beta, as a number, rate, or dict by network, checked against them |
| run_callbacks | Call each function of add_callback() with the agents its event happened to since it was last called |
| step | Take the declared transitions of the step, those that are not contact-mediated and then those that are, followed by any custom functions; a disease written as code overrides this |
| step_state | State changes at the start of the step, before the networks and interventions, by a disease written as code (e.g. recovery) |
| stratify | Stratify the disease by a property of the people in it (age, risk, place, …), in place, and return it |
| transmit | Who is infected in this step by transmission: by one of the disease’s declared transmissions (transition), or, as agents, from the |
| transmit_arrays | The transmission of transmit(rel_trans=..., rel_sus=...): the new cases, with their sources and routes recorded in an ss.infection_log |
R0
diseases.Disease.R0(matrix=False)The basic reproduction number, from the next-generation matrix at the disease-free state
By definition it is in a population with no infection and no immunity: the people infected at the start are left out, and everyone else is taken to be in the first state (e.g. S), each group keeping its size, whoever starts immune (init=dict(R=0.9)). So where transmission is density-dependent (beta*I.n), R0 is for the population less the people seeded, which matters only when many are (half the people seeded halves it). If no transition links some states to the first (a chain of states for each host species), their people are in the state their own chain starts from. People filled by a constant inflow (births of a number a day) and counts that are not people (mosquitoes) are at their equilibrium, which the inflow and the rates out of them set (Λ/μ). The reproduction number at the start, with any immunity, is in the line a run prints as it starts, when it differs (R0 = 15.2 (R = 1.52 at the start, with 10% susceptible)).
The model is laid out as an ODE: if the disease is in a sim, a copy of that sim is made with solver='ode', with its mixing and stratification; otherwise a sim with the disease alone, under homogeneous mixing, with the default number of people. Values that vary in time (a seasonal β) are taken at the start, so this is R0 at the start time. A sim runs a copy of the disease it is given, so the R0 of a sim’s model is sim.diseases.<name>.R0(); this matters where a count is a number rather than a fraction of the people (mosquitoes per person).
This is the largest eigenvalue of the next-generation matrix. Where infection passes through a vector (people infect mosquitoes, which infect people), each passage is a generation, so R0 is the square root of the classical Ross–Macdonald value. Shedding into a reservoir that is a count (such as water) is not an infection, so there R0 is the number of people infected through the reservoir by one person.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| matrix | bool |
if True, return the next-generation matrix (over the infected compartments of every group) rather than its largest eigenvalue | False |
Example
S, I, R = ss.states(3)
sir = ss.Disease(infection=S >> I @ 0.3*I, recovery=ss.Transition(I, R, dur=10))
sir.R0() # 3.0Rt
diseases.Disease.Rt()The reproduction number over the course of a run, as a result: the number one infectious person infects at each time point
This is R0 at the state of the run at each time point: the largest eigenvalue of the next-generation matrix with the people in each state of each group as they were then (so fewer when some are immune), and the rates as they were then (so lower while an ss.modify() halves β). It falls below 1 as an outbreak peaks. As in R0(), the model is laid out as an ODE, a copy of the sim with solver='ode', stepped alongside the run; a run as agents is taken by the counts in each state of each group. A state with several compartments (the stages of a dwell time) is split among them as in the copy; and the copy is what an intervention triggered by the state of the run sees.
Example
S, I, R = ss.states(3)
sir = ss.Disease(infection=S >> I @ 0.3*I, recovery=ss.Transition(I, R, dur=10), name='sir')
sim = ss.Sim(sir, total_pop=10_000, stop=150).run()
sim.diseases.sir.Rt().plot() # From 3 at the start, below 1 after the peakadd_callback
diseases.Disease.add_callback(event, fn)Call fn(uids) at the end of each step with the agents who went through a declared transition or event of this disease in it, from a module written as code (agents only)
A declared module reacts to the same events with ss.on() instead. Pass a bound method (e.g. self.on_delivery), or register from init_pre(): the sim runs its own copy of each module, and a lambda made in __init__() would still refer to the original.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| event | Transition / str |
the transition or event, e.g. preg.stillbirth, preg.loss, or its name |
required |
| fn | func |
called with the agents’ UIDs (once for each time, so an agent can appear twice), only on a step when there are some | required |
Example
preg.add_callback(preg.stillbirth, lambda uids: self.n_stillbirths.__setitem__(uids, self.n_stillbirths[uids] + 1))add_code_pars
diseases.Disease.add_code_pars(keys)Parameters its own code reads, not its graph’s: see define_pars()
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| keys | list |
the names of the parameters | required |
also_happened
diseases.Disease.also_happened(name, uids)As agents, others an event (a transition, by name) also happens to, as UIDs (e.g. the fetuses of a mother at a delivery); None for nobody else
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| name | str |
the name of the transition | required |
| uids | array |
the agents it happened to | required |
build_graph
diseases.Disease.build_graph()Build the graph from what was declared: when the disease is made, or for one declared in its init(), after that (see BuiltAfterInit)
burnin_plan
diseases.Disease.burnin_plan(engine)People part way through at the start: None for none (the default), or the plan for them (see ss.Pregnancy.burnin_plan() and ss.solvers.Agents.backfill_burnin())
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| engine | Engine |
the engine running this module | required |
call_transmit
diseases.Disease.call_transmit(tr)The disease’s own transmit() for one of its transmissions, with or without the transition as its argument (see transmit())
check_par_members
diseases.Disease.check_par_members(args, members, kwargs)A parameter given by keyword under the name it already has (name=), and read by a transition, as if it were a result (e.g. eir=eir) is an error: a result needs a name of its own
compartmental_form
diseases.Disease.compartmental_form(sim)A module that runs only as agents unless it defines this: make its small compartmental form, used only when a compartmental solver is named, and return the note that says what it leaves out
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| sim | Sim | the sim being initialized | required |
current_state
diseases.Disease.current_state(states=None)The state each agent is in, as a read-only ss.IntArr (by UID) of its index among states, or -1 for none
The names of the states, in order, are its .labels, so arr.labels[i] is the name of index i.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| states | list |
the states to look in, as names or states (default: the module’s states of people, in order) | None |
Example
cs = sir.current_state() # 0 for S, 1 for I, 2 for R
cs.labels[cs[uid]] # The name of an agent's statecurrent_value
diseases.Disease.current_value(key, uids=None)rel_sus or rel_trans (key) of each agent with the modifiers and products active on it applied (ss.modify(dis.rel_sus, ...)), as agents: what transmission uses, whereas reading dis.rel_sus gives the base values, which a modifier never changes. Or a parameter (key is its name) with the modifiers active on it, in its own type, declared or written as code: one value, or one for each agent if they differ between agents.
A disease written as code that reads one in its own code (including by transmit(rel_trans=..., rel_sus=...)) says so in its class, allow_modifiers = ('rel_sus',), so that modifiers of it are applied rather than refused. A parameter it doesn’t say this of is read from self.pars with the modifiers applied, which is one value, so a modifier of it can’t differ between agents.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| key | str |
‘rel_sus’, ‘rel_trans’, or the name of a parameter | required |
| uids | uids | the agents to give the values of (default: an array of every agent, by UID) | None |
Example
sim.diseases.sir.current_value('rel_sus') # With a vaccine's efficacy applied
class Diabetes(ss.Disease):
allow_modifiers = ('rel_sus',) # ss.modify(diabetes.rel_sus, ...) scales its onset
def step(self):
uids = self.at_risk.uids
onset = self.pars.p_onset.rvs(uids)*self.current_value('rel_sus', uids) # ...declare
diseases.Disease.declare(args=(), kwargs=None, names=None, overwrite=False)Add declared members (states, transitions, results), to be built with the rest; see define_transitions()
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| args | list |
the members to add, without names | () |
| kwargs | dict |
the members to add, by name | None |
| names | dict |
the names of the variables the members were assigned to, if known | None |
| overwrite | bool |
whether a member can replace one of the same name | False |
define_pars
diseases.Disease.define_pars(inherit=True, **kwargs)Create or merge parameters; see ss.Module.define_pars()
For a declared disease, a parameter defined here that is not one of its graph’s (e.g. by a subclass, after super().__init__(), for its own code to read) is added to code_pars, so the sim does not reject it as one that nothing reads. A parameter set any other way (e.g. a misspelling in pars=) is still rejected.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| inherit | bool |
whether to add to the parameters that exist, rather than replace them | True |
| kwargs | dict |
the parameters, by name |
define_results
diseases.Disease.define_results(*args, **kwargs)Define results: for a declared disease, sums and ratios of states by name (e.g. prevalence=I/(S + I + R), or active=(E + I).frac); otherwise ss.Result objects (see ss.Module.define_results())
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| args | list |
the results, as ss.Result objects or expressions |
|
| kwargs | dict |
the results of a declared disease, by name |
define_states
diseases.Disease.define_states(*args, overwrite=False, **kwargs)Define its states: declared states (ss.State), or for a disease written as code, arrays (see ss.Module.define_states())
Declared states are given in its __init__(), as for define_transitions(), and each is an attribute at once (self.S).
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| args | list |
the states (ss.states('S I R'), or held in variables), or lists of them; or arrays (ss.BoolState, …) |
|
| overwrite | bool |
replace a state of the same name, rather than raising | False |
| kwargs | dict |
states by name (S=ss.State(label='Susceptible')); or for arrays, aliases |
define_transitions
diseases.Disease.define_transitions(*args, overwrite=False, **kwargs)Define its transitions, in its __init__(), after its states
For a declared disease whose structure depends on its arguments, so that it cannot be written in a class body: the states (define_states()), transitions, and results (define_results()) are built together once its __init__() has finished. Each is an attribute at once (self.infection).
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| args | list |
transitions and outcomes, or lists of them, named by name= or by the variable they were assigned to |
|
| overwrite | bool |
replace a transition of the same name (e.g. one whose rate is changed), rather than raising | False |
| kwargs | dict |
transitions by name |
Examples
class SIRS(ss.Disease):
def __init__(self, waning=True, **kwargs):
super().__init__(init_prev=0.01, **kwargs)
self.define_states(ss.states('S I R'))
beta = ss.par(0.3)
self.define_transitions(
infection = self.S >> self.I @ beta*self.I,
recovery = self.I >> self.R @ ss.days(10),
)
if waning:
self.define_transitions(waning=self.R >> self.S @ ss.days(100))describe
diseases.Disease.describe(out=None)The resolved model, with every guessed default marked, as text that shows itself (ss.display.Text), as diagram() is
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| out | ss.display.Description |
if given, add the description to it and return it | None |
diagram
diseases.Disease.diagram(kind='plot', **kwargs)The state-transition diagram: drawn as a Matplotlib figure, or as the text of a Mermaid flowchart (https://mermaid.js.org)
People states are rounded and counts square (slanted in the text); transmissions are dashed; each arrow is labeled with its transition’s name and value (recovery: 10 days), and the fraction that takes it for a transition with p=. For every disease of a sim, and how they act on each other, see sim.diagram().
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| kind | str |
‘plot’ to draw it, or ‘text’ for the Mermaid text, which a notebook draws and print() shows (see ss.display.Diagram) |
'plot' |
| kwargs | dict |
for a plot, passed to ss.return_fig() (e.g. filename=, do_show=) |
Example
S, I, R = ss.states(3)
sir = ss.Disease(infection=S >> I @ 0.3*I, recovery=ss.Transition(I, R, dur=ss.days(10)))
sir.diagram()
print(sir.diagram('text'))equations
diseases.Disease.equations()The ODE system of the declared transitions, with symbolic parameter names, as text that shows itself (ss.display.Text; print() it in a script)
finish_step
diseases.Disease.finish_step()Once the step’s flows are recorded (and the analyzers have run), its engine starts counting them afresh
has_structure
diseases.Disease.has_structure(kwargs)Whether these keywords declare part of a model, not just a parameter’s value
infect
diseases.Disease.infect(uids, strain=None)Infect some agents now, from code (e.g. a strain introduced on a date): those in the first state move where its transmission puts them, counted in its flow and dated as its infections are (ti_I.isclose(sim.ti)), with a source of -1 in an ss.infection_log
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | uids | the agents; those not in the first state are left where they are | required |
| strain | str / Strain |
for a disease with strains in shared states, the strain they are infected with (by default, as the split says) | None |
Returns
| Name | Type | Description |
|---|---|---|
| The agents infected |
Example
covid.infect(sim.people.auids[:20], strain='alpha') # 20 imported cases of alphainit_graph
diseases.Disease.init_graph(args, members, pars, kwargs, names=None)Build the graph of declared states and transitions; see ss.graph.Graph
init_post
diseases.Disease.init_post()Initialize the values of the states, and set the initial conditions of the declared states
init_pre
diseases.Disease.init_pre(sim)Link the disease to the sim, create its engine and objects, initialize results, and start its infection log if an analyzer needs it (e.g. ss.infection_log); see ss.Module.init_pre()
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| sim | Sim | the sim being initialized | required |
make_children
diseases.Disease.make_children(tr, parents, ti)As agents, the children of the parents who went through a transition with births (ss.birth(when=)); returns their UIDs
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| tr | Transition | the transition with births | required |
| parents | array |
the parents | required |
| ti | int |
the step of the births | required |
make_naive
diseases.Disease.make_naive(uids)Put agents back in the first state, as if they had never had the disease; for dynamic rescaling (see ss.Sim.rescale())
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | ss.uids |
the agents to make naive | required |
move
diseases.Disease.move(*args, **kwargs)Move people between this disease’s states from code, the same way under every solver: ss.move() with the disease given
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| args | list |
passed to ss.move(): what to move, from where, and to where |
|
| kwargs | dict |
passed to ss.move(), e.g. p or uids |
Examples
sir.move(sir.I, sir.R, p=0.5) # Cure half of those infectious
sir.move(uids, sir.R) # These agents, from wherever they are (or sir.move(uids, 'R'))
self.move(self.I, self.R, uids=uids) # In the disease's own code: these agents, if they are infectiousnot_naive
diseases.Disease.not_naive()The agents who have left the first state (e.g. who are no longer susceptible), for dynamic rescaling (see ss.Sim.rescale())
on_death
diseases.Disease.on_death(uids)What happens to agents when they die: as agents, they leave every declared state
It is called once the step’s deaths are settled, before the analyzers run. A disease written as code overrides it for what changes at death (e.g. clearing infected). A death is only requested during the step (e.g. in step_state(), as ssl.AgentSIR does), so an intervention on the same step can still avert it, and nothing needs undoing, since these changes are made only if the death happens. The sim does not call it directly: people.step_die() does, once it knows who died.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | array |
the agents who died | required |
record_events
diseases.Disease.record_events()The flows of the step, and the results worked out from them
record_states
diseases.Disease.record_states()The counts of the states at the start of the step; for a disease written as code, the count of each ss.BoolState (see ss.Module.record_states())
reuse
diseases.Disease.reuse(args, members, names=None)States and transitions that already belong to another disease are copied (together, so they refer to each other, and with their parameters), so one set can build several independent diseases
route_betas
diseases.Disease.route_betas()The beta of each of the sim’s networks (by name), as a pair (p1 to p2, p2 to p1), that transmit(rel_trans=..., rel_sus=...) uses when not given beta=: by default pars.beta, as a number, rate, or dict by network, checked against them
Override it for betas worked out by code, e.g. by sex or genotype, starting from super().route_betas().
Example:
class STI(ss.AgentDisease):
def route_betas(self):
betamap = super().route_betas()
betamap['sexual'] = [self.pars.beta_m2f, self.pars.beta_f2m] # Male to female and female to male, on ss.SexualNet
return betamaprun_callbacks
diseases.Disease.run_callbacks()Call each function of add_callback() with the agents its event happened to since it was last called
step
diseases.Disease.step()Take the declared transitions of the step, those that are not contact-mediated and then those that are, followed by any custom functions; a disease written as code overrides this
step_state
diseases.Disease.step_state()State changes at the start of the step, before the networks and interventions, by a disease written as code (e.g. recovery)
The declared transitions are all taken together in step(), after the interventions, so a delivery sees the same people under every solver (as agents and as compartments).
stratify
diseases.Disease.stratify(inplace=True, **kwargs)Stratify the disease by a property of the people in it (age, risk, place, …), in place, and return it
Every keyword is a dimension, with its levels: bin edges (for a continuous property such as age) or labels, or a dict of the fraction of the population in each level (if not given: from a named population or geography; for age groups, the world’s ages; for a boolean dimension, or one that people are moved along, everyone at the first level; otherwise equal). ss.levels() adds the options that are needed less often: the states it applies to (states=), the people it applies to (who=), its kind, and the movement between places. A value by level, e.g. a rate, is ss.by(): S >> I @ beta*ss.by(risk=[1, 3])*I, or for a disease already written, ss.modify(sir.infection, multiply=ss.by(risk=[1, 3])).
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| inplace | bool |
if False, return a stratified copy and leave this disease unchanged | True |
| kwargs | dict |
each dimension and its levels, e.g. age=[0, 15, 65], risk={'low': 0.8, 'high': 0.2}, or severity=ss.levels(['mild', 'severe'], states=[I]); an ss.transfer passed as a keyword is added under that name |
Examples
sir.stratify(age={0: 0.3, 15: 0.6, 65: 0.1}) # With the fraction of people in each age group
sir.stratify(age=[0, 15, 65], risk={'low': 0.8, 'high': 0.2})
sir.stratify(sex=['female', 'male'], parity=ss.levels(['0', '1-2', '3+'], who=ss.person.female)) # Only women have a parity
sir.stratify(severity=ss.levels({'mild': 0.9, 'severe': 0.1}, states=[sir.I]))transmit
diseases.Disease.transmit(
transition=None,
rel_trans=None,
rel_sus=None,
beta=None,
)Who is infected in this step by transmission: by one of the disease’s declared transmissions (transition), or, as agents, from the transmissibility and susceptibility of each agent (rel_trans and rel_sus), over the sim’s networks and other routes
Declared. A declared disease’s transmissions are its transitions whose rate depends on contact with infectious people (e.g. S >> I @ beta*I). Override this to write one as code, and call super().transmit(transition) for what the solver would do, e.g. to add a route of its own or remove some of the cases. The engine calls it once a step for each transmission, and moves those it gives along that transition, counted in its flow. A disease with one transmission can leave out the argument (def transmit(self):); with several, transition says which (e.g. if transition.name == 'infection':). As agents, it gives the new cases’ UIDs (those not in one of the transition’s source states are not moved); as compartments, the number infected in each group (or one number), taken from the source states in proportion. Under the ODE, it is taken once a step, at the start, as a move of that many (as ss.move() from code is), so the ODE no longer integrates that transition within the step: give a rate (e.g. S >> I @ (lambda self, sim: ...)) to keep it exact. Anything that needs individuals (UIDs) raises an error in a compartmental run; if isinstance(self.sim.solver, ss.solvers.ode): can give each its own.
As array operations. With rel_trans and rel_sus, it is the same transmission as an operation that code calls, as agents (in a disease written as code, or in a declared one’s own code): each edge of a network from u to v transmits with probability 1 − exp(−rel_trans[u]·rel_sus[v]·H), where H is the hazard of the network’s chance for the step (see ss.Network.transmit()), with the modifiers and products on rel_trans and rel_sus applied (so the disease says allow_modifiers = ('rel_sus', 'rel_trans')), and the draws of each edge from the disease’s trans_rng; other routes (e.g. a mixing pool) give their own (compute_transmission()). Someone reached by several is infected by the first, which is their source, recorded with the route in an ss.infection_log. Nobody is moved: the code that calls it says what happens to those infected.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| transition | ss.Transition |
the declared transmission; may be left out for a disease with only one | None |
| rel_trans | array |
the transmissibility of each agent, by UID, 0 for those not infectious (e.g. self.rel_trans.raw*self.I.raw) |
None |
| rel_sus | array |
the susceptibility of each agent, by UID, 0 for those not susceptible | None |
| beta | float/ss.Rate/dict |
with rel_trans and rel_sus, the beta (a rate, or a number in the sim’s unit; a dict gives one per network); by default self.pars.beta |
None |
Returns
| Name | Type | Description |
|---|---|---|
As agents, the UIDs of the new cases (ss.uids); as compartments, the number of them in each group |
Examples
class Imported(ss.SIR): # A declared disease, with 2 imported cases a step added to its transmission
def transmit(self, transition):
new = super().transmit(transition)
if isinstance(self.sim.solver, ss.solvers.abm): # And 2 imported cases a step, as agents
return new | self.sim.people.auids[self.S.raw[self.sim.people.auids]][:2]
return new + 2/self.sim.groups.n_groups
class SIR(ss.Disease): # Written as code: the S >> I @ beta*I of ss.SIR, as agents
allow_modifiers = ('rel_sus', 'rel_trans') # Read here, by transmit()
...
def step(self):
new = self.transmit(rel_trans=self.rel_trans.raw*self.I.raw, rel_sus=self.rel_sus.raw*self.S.raw)
self.S[new] = False
self.I[new] = Truetransmit_arrays
diseases.Disease.transmit_arrays(rel_trans, rel_sus, beta=None)The transmission of transmit(rel_trans=..., rel_sus=...): the new cases, with their sources and routes recorded in an ss.infection_log
InfectionLog
diseases.InfectionLog(disease=None, networks=None, timeline=None)A record of who infected whom, and when: kept by a disease when an analyzer needs it (e.g. ss.infection_log), to trace chains of transmission
The infection log records transmission events and optionally other data associated with each transmission. Entries are stored as one chunk of arrays per call, so logging is cheap; they are combined when the log is read. Basic functionality is to track transmission with
Disease.infection_log.add_entries(targets, sources, t)
or, for a single infection,
Disease.infection_log.append(source, target, t)
Seed infections can be recorded with a source of None (or NaN), although all infections should have a target and a time. Other data can be captured in the log, either at the time of creation, or later on. For example
Disease.infection_log.add_entries(targets, sources, t, variant=variants)
records extra data for each infection (ss.Disease.transmit() records the network this way). Modules can optionally add per-infection outcomes later as well, for example
Disease.infection_log.add_data(uids, t_dead=2024.25)
This would be equivalent to having specified the data at the original time the log entry was created - however, it is more useful for tracking events that may or may not occur after the infection and could be modified by interventions (e.g., tracking diagnosis, treatment, notification etc.)
A table of outcomes can be returned using InfectionLog.to_df(), and a NetworkX graph with InfectionLog.to_graph(). An analyzer that follows transmission as the sim runs reads each step’s entries with to_df(ti=self.ti):
new = sim.diseases.sir.infection_log.to_df(ti=self.ti) # In an analyzer’s step()
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| disease | str |
the name of the disease being logged | None |
| networks | list |
the names of the networks, used to convert the network IDs recorded by ss.Disease.transmit() to names in to_df() |
None |
| timeline | ss.Timeline |
the disease’s timeline, so that each entry has the time index it was logged at, for to_df(ti=...) |
None |
Methods
| Name | Description |
|---|---|
| add_data | Record extra infection data |
| add_entries | Record new infections |
| append | Record a single infection |
| disp | Full display of the infection log |
| to_df | Return a tabular representation of the log as a line list dataframe |
| to_graph | Return the log as a NetworkX MultiDiGraph, with sources and targets as nodes and the time as the edge key |
add_data
diseases.InfectionLog.add_data(uids, **kwargs)Record extra infection data
This method can be used to add data to an existing transmission event. The most recent transmission event for each agent will be used.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | array |
The UIDs of the target nodes (the agents that were infected) | required |
| kwargs | dict |
Remaining arguments are stored as data for each entry, either a scalar or one value per UID |
add_entries
diseases.InfectionLog.add_entries(uids, sources=None, time=None, **kwargs)Record new infections
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| uids | array |
the UIDs of the infected agents (targets) | required |
| sources | array / int |
the UIDs of the infecting agents; None or NaN for seed infections, or -1 (as a declared disease records its seeds) | None |
| time | any |
the time of infection (all entries in the chunk share it); by default the time point of the current step (the start, for seeds set before the first) | None |
| kwargs | dict |
extra data to store, either a scalar or one value per UID |
append
diseases.InfectionLog.append(source, target, t, **kwargs)Record a single infection
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| source | int |
who did the infecting | required |
| target | int |
who was infected | required |
| t | int |
the time of the infection | required |
| kwargs | dict |
other data to store with the entry |
disp
diseases.InfectionLog.disp(**kwargs)Full display of the infection log
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| kwargs | dict |
passed to sc.pr() |
to_df
diseases.InfectionLog.to_df(ti=None)Return a tabular representation of the log as a line list dataframe
This function returns a dataframe containing columns for all quantities recorded in the log. Note that the log will contain NaN for quantities that are defined for some edges and not others (and which are missing for a particular entry)
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| ti | int |
if given, only the entries logged at this time index of the disease (e.g. ti=self.ti in an analyzer’s step(), for the infections of the step just taken; at ti=0, the seeds); it reads only those entries, so is quick to call every step |
None |
Example
class new_infections(ss.Analyzer):
def init_results(self):
super().init_results()
self.define_results(ss.Result('known', label='Infections from a known source', kind='flow'))
def step(self):
df = self.sim.diseases.sir.infection_log.to_df(ti=self.ti)
self.results.known[self.ti] = (df.source >= 0).sum() # Not seeds (-1), nor those with no source (NA)
sim = ss.Sim(diseases='sir', networks='random', analyzers=[ss.infection_log(), new_infections()]).run()to_graph
diseases.InfectionLog.to_graph()Return the log as a NetworkX MultiDiGraph, with sources and targets as nodes and the time as the edge key
SEIR
diseases.SEIR(
*args,
pars=None,
strains=None,
stratify=None,
solver=None,
_names=None,
**kwargs,
)An SEIR model: as ss.SIR, with an exposed (latent) state before people become infectious
Note: CRN safe as agents.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| beta | float/ss.per/dict |
the transmission rate (per contact, if there are networks, over each step of the contact, so ss.probperday(p) gives each contact a chance p in a day; a dict gives one per network, and default= for the networks it does not name) |
|
| init_prev | float |
the fraction of people who start infected, who start exposed, in E (for a number of people, give init_n= instead); to start people infectious, give init=dict(I=0.01) or init_n=dict(I=10) instead |
|
| dur_exp | float/ss.Duration/ss.Dist |
the mean time that people are exposed (latent) for (by default 3 days; a number is in the model’s unit), as an exponential | |
| dur_inf | float/ss.Duration/ss.Dist |
the mean time that people are infectious for (by default 10 days), as an exponential | |
| p_death | float |
the probability of death from infection, at the end of the infection (by default 0) | |
| kwargs | dict |
passed to ss.Disease, e.g. name |
|
| pars | dict |
the same parameters, as a dict | None |
As in ss.SIR, the defaults are those of a respiratory infection, each duration is exponential unless a distribution is given, and nobody dies of the infection unless p_death is given.
An SEIR model written as code, with lognormal durations and p_death=0.01, is ssl.AgentSEIR.
SIR
diseases.SIR(
*args,
pars=None,
strains=None,
stratify=None,
solver=None,
_names=None,
**kwargs,
)An SIR model: susceptible, infected, and recovered, optionally with deaths from infection
Declared from states and transitions (see ss.Disease), so it runs with any solver: as agents over networks, or as compartments. It is the textbook SIR: the infectious period is exponential, as any declared duration is, and nobody dies of the infection unless p_death is given. Its defaults are those of a respiratory infection: infectious for 10 days, with an R0 of 3 without a network (β × the duration); with a network, β is per contact, so R0 is that many times the number of contacts.
Note: CRN safe as agents.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| beta | float/ss.per/dict |
the transmission rate (per contact, if there are networks, over each step of the contact, so ss.probperday(p) gives each contact a chance p in a day; a dict gives one per network, and default= for the networks it does not name) |
|
| init_prev | float |
the fraction of people who start infected (for a number of people, give init_n= instead, e.g. init_n=10) |
|
| dur_inf | float/ss.Duration/ss.Dist |
the mean time that people are infected for (by default 10 days), as an exponential; give a distribution (e.g. ss.lognormal(mean=ss.days(10))) for another shape |
|
| p_death | float |
the probability of death from infection, at the end of the infection (by default 0) | |
| kwargs | dict |
passed to ss.Disease, e.g. name |
|
| pars | dict |
the same parameters, as a dict | None |
An SIR model written as code, with a lognormal dur_inf and p_death=0.01, is ssl.AgentSIR.
Example
sim = ss.Sim(diseases=ss.SIR(beta=0.03, dur_inf=ss.days(10)), networks='random', n_agents=5000) # R0 of 3 with 10 contacts each
sim.run().plot()SIS
diseases.SIS(
*args,
pars=None,
strains=None,
stratify=None,
solver=None,
_names=None,
**kwargs,
)An SIS model: people recover from infection and are susceptible again
Declared from states and transitions (see ss.Disease), so it runs with any solver. Its defaults are those of a respiratory infection that leaves no immunity: infectious for 10 days, with an R0 of 2 without a network, so that half of people are infected once it is endemic.
Note: CRN safe as agents.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| beta | float/ss.per/dict |
the transmission rate (per contact, if there are networks, over each step of the contact, so ss.probperday(p) gives each contact a chance p in a day; a dict gives one per network, and default= for the networks it does not name) |
|
| init_prev | float |
the fraction of people who start infected (for a number of people, give init_n= instead, e.g. init_n=10) |
|
| dur_inf | float/ss.Duration/ss.Dist |
the mean time that people are infected for (by default 10 days), as an exponential unless a distribution is given | |
| kwargs | dict |
passed to ss.Disease, e.g. name |
|
| pars | dict |
the same parameters, as a dict | None |
An SIS model with a per-agent immunity level, boosted by each infection and waning over time (waning=, imm_boost=), which has no declared form, is ssl.AgentSIS, written as code.
Functions
| Name | Description |
|---|---|
| check_init_prev | Check the starting infections: init_prev= is a fraction of people (up to 1; for a number of people, init_n=), so a whole number 1 means everyone and warns; init_n= is not negative |
| check_stratify | The stratify= of a disease or a sim: one dict of dimensions, as stratify() takes them |
| rebind_member | A member given to an instance of a declared class (e.g. ssl.Measles(rash=ss.outcome(0.9*ssl.Measles.onset))), reading the instance’s copies of the class’s states, transitions, and parameters (memo, see ss.expressions.rebind()); a transition is copied only if it reads any of them |
check_init_prev
diseases.check_init_prev(init_prev, seed_n, name)Check the starting infections: init_prev= is a fraction of people (up to 1; for a number of people, init_n=), so a whole number 1 means everyone and warns; init_n= is not negative
check_stratify
diseases.check_stratify(stratify, where)The stratify= of a disease or a sim: one dict of dimensions, as stratify() takes them
rebind_member
diseases.rebind_member(obj, memo)A member given to an instance of a declared class (e.g. ssl.Measles(rash=ss.outcome(0.9*ssl.Measles.onset))), reading the instance’s copies of the class’s states, transitions, and parameters (memo, see ss.expressions.rebind()); a transition is copied only if it reads any of them