Interventions¶
This tutorial shows how to add testing and treatment interventions to an STIsim simulation. We'll start with a simple gonorrhea example, then show how HIV interventions work.
Simple STI treatment¶
The simplest intervention pattern is a treatment with an eligibility function that determines who gets treated. Here we treat gonorrhea patients when they seek care for symptoms:
import stisim as sti
import starsim as ss
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Baseline: no treatment
sim_base = sti.Sim(diseases='ng', n_agents=2000, start=2010, stop=2030)
sim_base.run(verbose=0)
# With treatment: symptomatic care-seekers get treated
ng = sti.Gonorrhea(init_prev=0.01)
ng_tx = sti.GonorrheaTreatment(
name='ng_tx',
eligibility=lambda sim: sim.diseases.ng.symptomatic & (sim.diseases.ng.ti_seeks_care == sim.ti),
)
sim_intv = sti.Sim(diseases=ng, interventions=[ng_tx], n_agents=2000, start=2010, stop=2030)
sim_intv.run(verbose=0)
Initializing sim with 2000 agents
Initializing sim with 2000 agents
Sim(n=2000; 2010—2030; networks=structuredsexual, maternalnet; interventions=ng_tx; diseases=ng)
fig, ax = plt.subplots()
ax.plot(sim_base.timevec, sim_base.results.ng.prevalence, label='No treatment')
ax.plot(sim_intv.timevec, sim_intv.results.ng.prevalence, label='Treatment for care-seekers')
ax.set_xlabel('Year')
ax.set_ylabel('NG prevalence')
ax.set_title('Effect of treatment on gonorrhea')
ax.legend()
fig
HIV testing and ART¶
HIV interventions use HIV-specific classes. HIVTest tests undiagnosed agents, and ART puts diagnosed agents on treatment with coverage targets over time.
Note: ART includes logic to protect infants of treated mothers, so we need to include demographics (pregnancy and deaths) in the sim.
# Create HIV testing: 10% annual probability, starting in 2000
hiv_test = sti.HIVTest(test_prob_data=0.1, start=2000, name='hiv_test')
# Create ART: coverage ramps from 0% to 90% over time
art_data = pd.DataFrame(
index=np.arange(2000, 2031),
data={'p_art': np.clip(np.linspace(0, 0.9, 31), 0, 1)}
)
art = sti.ART(coverage_data=art_data)
# Demographics needed for ART (pregnancy protection)
demographics = [sti.Pregnancy(fertility_rate=20), ss.Deaths(death_rate=10)]
# Run with and without interventions
sim_base = sti.Sim(diseases='hiv', demographics=[sti.Pregnancy(fertility_rate=20), ss.Deaths(death_rate=10)],
n_agents=3000, start=2000, stop=2030)
sim_intv = sti.Sim(diseases='hiv', demographics=demographics, interventions=[hiv_test, art],
n_agents=3000, start=2000, stop=2030)
sim_base.run(verbose=0)
sim_intv.run(verbose=0)
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[3], line 12 9 art = sti.ART(coverage_data=art_data) 11 # Demographics needed for ART (pregnancy protection) ---> 12 demographics = [sti.Pregnancy(fertility_rate=20), ss.Deaths(death_rate=10)] 14 # Run with and without interventions 15 sim_base = sti.Sim(diseases='hiv', demographics=[sti.Pregnancy(fertility_rate=20), ss.Deaths(death_rate=10)], 16 n_agents=3000, start=2000, stop=2030) AttributeError: module 'stisim' has no attribute 'Pregnancy'
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].plot(sim_base.timevec, sim_base.results.hiv.new_infections, label='No ART', alpha=0.7)
axes[0].plot(sim_intv.timevec, sim_intv.results.hiv.new_infections, label='Testing + ART', alpha=0.7)
axes[0].set_xlabel('Year')
axes[0].set_ylabel('New HIV infections')
axes[0].legend()
axes[1].plot(sim_intv.timevec, sim_intv.results.hiv.p_on_art)
axes[1].set_xlabel('Year')
axes[1].set_ylabel('Proportion on ART')
axes[1].set_title('ART coverage')
plt.tight_layout()
fig
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) File /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages/sciris/sc_odict.py:175, in odict.__getitem__(self, key, allow_default) 174 try: --> 175 output = dict.__getitem__(self, key) 176 return output KeyError: 'hiv' During handling of the above exception, another exception occurred: KeyNotFoundError Traceback (most recent call last) File /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages/sciris/sc_odict.py:1250, in objdict.__getitem__(self, attr, exception) 1249 try: # Try retrieving normally -> 1250 return odict.__getitem__(self, attr, allow_default=False) # Do not allow odict to handle default 1251 except Exception as E2: # If that fails, raise the original exception File /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages/sciris/sc_odict.py:196, in odict.__getitem__(self, key, allow_default) 195 else: errormsg = f'Key {key} not found since odict is empty' --> 196 raise sc.KeyNotFoundError(errormsg) 197 else: # Exception raised wasn't a key error -- just raise it again KeyNotFoundError: odict key "hiv" not found; available keys are: timevec structuredsexual maternalnet ng n_alive n_female new_deaths new_emigrants cum_deaths During handling of the above exception, another exception occurred: AttributeError Traceback (most recent call last) Cell In[4], line 3 1 fig, axes = plt.subplots(1, 2, figsize=(10, 4)) ----> 3 axes[0].plot(sim_base.timevec, sim_base.results.hiv.new_infections, label='No ART', alpha=0.7) 4 axes[0].plot(sim_intv.timevec, sim_intv.results.hiv.new_infections, label='Testing + ART', alpha=0.7) 5 axes[0].set_xlabel('Year') File /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages/sciris/sc_odict.py:1231, in objdict.__getattribute__(self, attr) 1229 return odict.__getattribute__(self, attr) 1230 except Exception as E: # If that fails, try to get it as a dict item, but pass along the original exception -> 1231 return self.__getitem__(attr, exception=E) File /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages/sciris/sc_odict.py:1265, in objdict.__getitem__(self, attr, exception) 1263 else: 1264 if exception: -> 1265 raise exception 1266 else: 1267 raise E2 File /opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/site-packages/sciris/sc_odict.py:1229, in objdict.__getattribute__(self, attr) 1227 else: 1228 try: # First, try to get the attribute as an attribute -> 1229 return odict.__getattribute__(self, attr) 1230 except Exception as E: # If that fails, try to get it as a dict item, but pass along the original exception 1231 return self.__getitem__(attr, exception=E) AttributeError: 'Results' object has no attribute 'hiv'
Targeting interventions¶
Use the eligibility parameter to target interventions to specific populations. For example, FSW-targeted testing:
# Higher testing rate for FSWs
fsw_test = sti.HIVTest(
test_prob_data=0.3,
start=2000,
name='fsw_test',
eligibility=lambda sim: sim.networks.structuredsexual.fsw,
)
# Lower testing rate for the general population
gp_test = sti.HIVTest(
test_prob_data=0.05,
start=2000,
name='gp_test',
eligibility=lambda sim: ~sim.networks.structuredsexual.fsw,
)
art2 = sti.ART(coverage_data=art_data)
sim = sti.Sim(
diseases='hiv',
demographics=[sti.Pregnancy(fertility_rate=20), ss.Deaths(death_rate=10)],
interventions=[fsw_test, gp_test, art2],
n_agents=3000,
start=2000,
stop=2030,
)
sim.run(verbose=0)
sim.plot(key=['hiv.prevalence', 'hiv.new_infections', 'hiv.p_on_art'])
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[5], line 21 10 gp_test = sti.HIVTest( 11 test_prob_data=0.05, 12 start=2000, 13 name='gp_test', 14 eligibility=lambda sim: ~sim.networks.structuredsexual.fsw, 15 ) 17 art2 = sti.ART(coverage_data=art_data) 19 sim = sti.Sim( 20 diseases='hiv', ---> 21 demographics=[sti.Pregnancy(fertility_rate=20), ss.Deaths(death_rate=10)], 22 interventions=[fsw_test, gp_test, art2], 23 n_agents=3000, 24 start=2000, 25 stop=2030, 26 ) 27 sim.run(verbose=0) 28 sim.plot(key=['hiv.prevalence', 'hiv.new_infections', 'hiv.p_on_art']) AttributeError: module 'stisim' has no attribute 'Pregnancy'
Exercises¶
- Add VMMC to the HIV sim using
sti.VMMC(). How does it affect new infections compared to ART alone? - Modify the gonorrhea example to use a higher treatment efficacy (
treat_eff=0.99). How much does prevalence change? - Create a sim with both gonorrhea and chlamydia, and use a single
STITreatment(diseases=['ng', 'ct'])to treat both. Compare prevalence with and without treatment.