tb
tb
TB natural history model. State definitions and transition diagram are in the API docs (tbsim.tb).
Classes
| Name | Description |
|---|---|
| TB | Agent-based TB natural history adapting the LSHTM compartmental structure [1] (Schwalb et al. 2025). |
| TBS | TB state labels. |
| choice2d | Version of ss.choice() allowing different per-agent probabilities. |
TB
tb.TB(pars=None, **kwargs)Agent-based TB natural history adapting the LSHTM compartmental structure [1] (Schwalb et al. 2025). States in TBS span the spectrum from susceptibility to active disease and treatment. Infectious states are TBS.ASYMPTOMATIC and TBS.SYMPTOMATIC; the force of infection depends on pars.beta and the prevalence of those states, with pars.trans_asymp (kappa) giving the relative infectiousness of asymptomatic vs symptomatic TB. Progression out of latent INFECTION uses constant per-year hazards by default, but can optionally be front-loaded via k_asy (and k_non): the INFECTION -> ASYMPTOMATIC (and INFECTION -> NON_INFECTIOUS) hazard then declines exponentially with time since infection (see progression_rates). Reinfectable state (TBS.CLEARED) uses per-agent rr_reinfection, set on entry from each pathway (rr_reinfection_cleared, rr_reinfection_rec, rr_reinfection_treat). Per-agent modifiers rr_activation, rr_clearance, rr_death scale selected rates.
Args (pars): Transmission and reinfection
- ``init_prev``: Initial seed infections into latent ``INFECTION`` (prevalence).
- ``init_prev_active``: Initial seed infections into active ``SYMPTOMATIC`` TB (prevalence). Default 0. Mirrors the ODE model's symptomatic seeding, so that both models can start from identical active-TB prevalence.
- ``beta``: Transmission rate per year.
- ``trans_asymp``: Relative transmissibility, asymptomatic vs symptomatic. (kappa)
- ``rr_reinfection_rec``: Relative risk of reinfection after recovering from NON_INFECTIOUS. (pi)
- ``rr_reinfection_treat``: Relative risk of reinfection after completing treatment. (rho)
- ``rr_reinfection_cleared``: Relative risk of reinfection after clearing latent infection. Default: 1.0
- ``dur_reinfection_protection``: Distribution of duration of reinfection protection. If None, never wanes.
*From INFECTION (latent)*
- ``inf_cle``: Infection -> Cleared (no active TB).
- ``inf_non``: Infection -> Non-infectious TB.
- ``inf_asy``: Infection -> Asymptomatic TB.
- ``k_asy``: Optional exponential decline shape parameter of ``inf_asy`` with time since infection tau: ``inf_asy(tau) = inf_asy * exp(-k_asy * tau)``. Default 0 (constant hazard). Set > 0 to front-load progression to active TB (the recommended one-parameter form).
- ``k_non``: Optional exponential decline shape parameter of ``inf_non`` with time since infection, analogous to ``k_asy``. Default 0 (constant hazard).
*From NON_INFECTIOUS*
- ``non_rec``: Non-infectious -> Recovered.
- ``non_asy``: Non-infectious -> Asymptomatic.
*From ASYMPTOMATIC*
- ``asy_non``: Asymptomatic -> Non-infectious.
- ``asy_sym``: Asymptomatic -> Symptomatic.
*From SYMPTOMATIC*
- ``sym_asy``: Symptomatic -> Asymptomatic.
- ``sym_dead``: Symptomatic -> Dead (TB-specific mortality, mu_TB).
*Background (general mortality is handled by* ``ss.Deaths`` *demographics, not this module)*
- ``cxr_asymp_sens``: CXR sensitivity for screening asymptomatic (0-1).
Attributes
| Name | Type | Description |
|---|---|---|
| - | susceptible (BoolState, default=True |
Whether the agent is susceptible to TB. |
| - | infected (BoolState, default=False |
Whether the agent is currently infected. |
| - | ever_infected (BoolState, default=False |
Whether the agent has ever been infected. |
| - | on_treatment (BoolState, default=False |
Whether the agent is on TB treatment. |
| - | state (FloatArr, default=TBS.SUSCEPTIBLE |
Current TB state (TBS value). |
| - | ti_infected (FloatArr, default=-inf |
Time of infection (never infected = -inf). |
| - | rel_sus (FloatArr, default=1.0 |
Relative susceptibility to TB. |
| - | rel_trans (FloatArr, default=1.0 |
Relative transmissibility of TB. |
| - | rr_activation (FloatArr, default=1.0 |
Multiplier on INFECTION -> NON_INFECTIOUS / ASYMPTOMATIC. |
| - | rr_clearance (FloatArr, default=1.0 |
Multiplier on NON_INFECTIOUS -> CLEARED. |
| - | rr_death (FloatArr, default=1.0 |
Multiplier on SYMPTOMATIC -> DEAD. |
| - | rr_reinfection (FloatArr, default=1.0 |
Per-agent relative reinfection risk; set on entry to CLEARED. |
| - | ti_rr_reinfection_wane (FloatArr, default=np.inf |
Sim time at which rr_reinfection resets to 1.0. |
Example
::
import starsim as ss
import tbsim
sim = ss.Sim(diseases=tbsim.TB(), pars=dict(start='2000', stop='2020'))
sim.run()
sim.plot()
References
[1] Schwalb et al. (2025) Potential impact, costs, and benefits of population-wide screening interventions for tuberculosis in Viet Nam. PLOS Glob Public Health. https://doi.org/10.1371/journal.pgph.0005050
Methods
| Name | Description |
|---|---|
| finalize_results | Compute cumulative series from new-event series after the run. |
| init_post | Seed initial infections, then (optionally) promote a fraction to active TB. |
| init_pre | Resolve the reinfection-RR default coupling once all pars are final (spec §reinfection). |
| init_results | Define result time series. |
| plot | Plot TB result time series using tbsim.plot(). |
| progression_rates | Per-agent INFECTION-exit rates to NON_INFECTIOUS and ASYMPTOMATIC. |
| set_prognoses | Set prognoses for newly infected agents (called when transmission occurs). |
| step | Advance TB state machine one timestep. |
| step_bookkeeping | Update infection flags, request TB deaths, reset risk modifiers, and set the |
| step_die | Apply death for the given agents and update TB state. |
| step_transitions | Evaluate the natural-history state transitions (each mutates self.state in place). |
| transition | Evaluate competing exponential transitions over one dt and apply immediately. |
| update_results | Record current time-step values for all result series. |
finalize_results
tb.TB.finalize_results()Compute cumulative series from new-event series after the run.
init_post
tb.TB.init_post()Seed initial infections, then (optionally) promote a fraction to active TB.
The base starsim.Infection.init_post seeds init_prev cases into latent INFECTION (via set_prognoses). We additionally seed init_prev_active cases directly into SYMPTOMATIC active TB, matching the compartmental ODE, which seeds its initial cases as symptomatic rather than latent. Active seeding draws from agents still susceptible after latent seeding, so the two seeds do not overlap.
init_pre
tb.TB.init_pre(sim)Resolve the reinfection-RR default coupling once all pars are final (spec §reinfection).
rr_reinfection_inf (σ_L) defaults to rr_reinfection_rec; rr_reinfection_non (σ_N) defaults to rr_reinfection_inf. Done here (not in __init__) so it uses the fully-resolved pars regardless of subclass construction order (TBResistant applies its pars after super().__init__).
init_results
tb.TB.init_results()Define result time series.
plot
tb.TB.plot(**kwargs)Plot TB result time series using tbsim.plot().
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| **kwargs | Forwarded to :func:tbsim.plots.plot. Common options include select, title, n_cols, row_height, style, filename, and show. |
{} |
Returns
| Name | Type | Description |
|---|---|---|
| matplotlib.figure.Figure |
progression_rates
tb.TB.progression_rates(uids)Per-agent INFECTION-exit rates to NON_INFECTIOUS and ASYMPTOMATIC.
Returns (inf_non, inf_asy) as rate objects with per-agent values, applying rr_activation and the optional time-since-infection decline::
inf_non(tau) = inf_non * rr_activation * exp(-k_non * tau)
inf_asy(tau) = inf_asy * rr_activation * exp(-k_asy * tau)
where tau is years since each agent entered INFECTION (from ti_infected, so reinfection restarts the clock). With k_non = k_asy = 0 (default) the decline factors are exactly 1 and the rates reduce to the constant inf_non/inf_asy scaled only by rr_activation.
set_prognoses
tb.TB.set_prognoses(uids, sources=None)Set prognoses for newly infected agents (called when transmission occurs).
The base starsim.Infection calls this when a susceptible agent acquires infection. A primary infection (from SUSCEPTIBLE / CLEARED) enters latent INFECTION; a reinfection of an already-infected latent (INFECTION) or non-infectious (NON_INFECTIOUS) agent only resets the ti_infected clock — its state is left unchanged (clock reset, no state change). Transitions are evaluated per dt each timestep in step.
step
tb.TB.step()Advance TB state machine one timestep.
- Transmission (via
super().step()): handles force of infection. - Evaluate transitions (
step_transitions): competing-risk transitions, applied immediately; agents may cascade through multiple states in one step. - Bookkeeping (
step_bookkeeping): update flags, modifiers, deaths, and the transmission state (susceptible/rel_sus/rel_trans) for next step.
The work is split into step_transitions and step_bookkeeping so that strain-aware subclasses (see tbsim.resistance) can override the natural history and the transmission set-up independently.
step_bookkeeping
tb.TB.step_bookkeeping()Update infection flags, request TB deaths, reset risk modifiers, and set the transmission state (susceptible/rel_sus/rel_trans) used next step.
step_die
tb.TB.step_die(uids)Apply death for the given agents and update TB state.
Called by the framework when agents die (e.g. background mortality). Sets state to DEAD, clears flags, and stops transmission.
step_transitions
tb.TB.step_transitions()Evaluate the natural-history state transitions (each mutates self.state in place).
transition
tb.TB.transition(uids, to, rng)Evaluate competing exponential transitions over one dt and apply immediately.
For each agent in uids, computes the probability of transitioning to each destination in to during one timestep. Uses a single uniform draw per agent to decide (a) whether the agent transitions and (b) which destination it goes to. State is updated immediately.
update_results
tb.TB.update_results()Record current time-step values for all result series.
TBS
tb.TBS()TB state labels.
- Each agent is in exactly one of these states.
- Transitions are driven by exponential rates in
TB.
choice2d
tb.choice2d(a=None, p=None, replace=True, **kwargs)Version of ss.choice() allowing different per-agent probabilities.
Temporary location; to be ported to Starsim and potentially merged with ss.choice().
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| a | 1D array | the values to choose from (default: np.arange(p.shape[1])) | None |
| p | 2D array | the probability of each choice for each agent | None |
Example: # Choose between specified options each with a specified probability (must sum to 1) p = np.array([ # Per-agent array of outcome probabilities across 3 options [0.1, 0.5, 0.4], [0.2, 0.3, 0.5], [0.3, 0.4, 0.3], [0.4, 0.2, 0.4], [0.5, 0.3, 0.2], [0.8, 0.1, 0.1], ]) n = len(p) choices = tbsim.choice2d(p=p, strict=False)(n)
Methods
| Name | Description |
|---|---|
| ppf | Always use ppf to allow per-agent probabilities |
ppf
tb.choice2d.ppf(rands)Always use ppf to allow per-agent probabilities
Functions
| Name | Description |
|---|---|
| get_tb | Helper to get the TB infection module from a sim |
get_tb
tb.get_tb(sim, which=None)Helper to get the TB infection module from a sim
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| sim | Sim | the simulation to search for the TB module | required |
| which | type | the class of TB module to get (e.g. TB); if None, returns the first BaseTB subclass found | None |