Model structure

Starsim models are designed to capture disease dynamics within a population of agents, which typically represent people (but may represent animals or other things). In keeping with this, the basic ingredients of a Starsim model are the People class, which store all the relevant attributes about people, a collection of Modules that determine what happens to people on each time step, and the Sim class, which pulls all the components together, runs the simulation, and stores the Results.

Overview of People

More details on the People class are in the separate user guide page, but we give a basic introduction here since people are so central to the model structure. When people are created, by default they come with basic states that are stored for each person. These basic states include age, sex, and whether the person is alive. All of these states are stored as arrays, so the basic structure of the People class can be easily exported to a dataframe, e.g.:

import starsim as ss

sim = ss.Sim(n_agents=10)
sim.init()
df = sim.people.to_df()
print(df)
Initializing sim with 10 agents
   uid  slot  alive        age  female  ti_dead  ti_removed  scale
0    0     0   True  25.152369   False      NaN         NaN    1.0
1    1     1   True   4.988294    True      NaN         NaN    1.0
2    2     2   True  58.149414   False      NaN         NaN    1.0
3    3     3   True   0.130237   False      NaN         NaN    1.0
4    4     4   True  43.819874   False      NaN         NaN    1.0
5    5     5   True  42.690937   False      NaN         NaN    1.0
6    6     6   True  54.292294    True      NaN         NaN    1.0
7    7     7   True  52.604046   False      NaN         NaN    1.0
8    8     8   True   7.076855   False      NaN         NaN    1.0
9    9     9   True  27.229174   False      NaN         NaN    1.0

When a module is added to a sim, this can add additional states to people. Tracking and updating the states of people is one of the main ways in which Starsim models disease dynamics. For example:

import starsim as ss

sim = ss.Sim(n_agents=20, diseases=dict(type='sis', init_prev=0.2), networks='random')
sim.run()
df = sim.people.to_df()
df.disp()
Initializing sim with 20 agents
  Running 2000.01.01 ( 0/51) (0.00 s)  ———————————————————— 2%
  Running 2010.01.01 (10/51) (0.26 s)  ••••———————————————— 22%
  Running 2020.01.01 (20/51) (0.28 s)  ••••••••———————————— 41%
  Running 2030.01.01 (30/51) (0.30 s)  ••••••••••••———————— 61%
  Running 2040.01.01 (40/51) (0.32 s)  ••••••••••••••••———— 80%
  Running 2050.01.01 (50/51) (0.34 s)  •••••••••••••••••••• 100%

    uid  slot  alive      age  female  ti_dead  ti_removed  scale  randomnet.participant  sis.susceptible  sis.infected  sis.rel_sus  sis.rel_trans  sis.ti_infected  sis.ti_recovered  sis.immunity
0     0     0   True  25.1524   False      NaN         NaN    1.0                  False             True         False       0.3449            1.0             33.0           42.7465        0.6551
1     1     1   True   4.9883    True      NaN         NaN    1.0                  False            False          True       0.0000            1.0             42.0           52.0214        1.0051
2     2     2   True  58.1494   False      NaN         NaN    1.0                  False             True         False       0.6627            1.0             23.0           31.5178        0.3373
3     3     3   True   0.1302   False      NaN         NaN    1.0                  False            False          True       0.0000            1.0             49.0           59.3261        1.5559
4     4     4   True  43.8199   False      NaN         NaN    1.0                  False             True         False       0.1787            1.0             38.0           48.7212        0.8213
5     5     5   True  42.6909   False      NaN         NaN    1.0                  False             True         False       0.3620            1.0             29.0           37.6566        0.6380
6     6     6   True  54.2923    True      NaN         NaN    1.0                  False             True         False       0.1526            1.0             38.0           47.5738        0.8474
7     7     7   True  52.6040   False      NaN         NaN    1.0                  False             True         False       0.1201            1.0             39.0           49.7212        0.8799
8     8     8   True   7.0769   False      NaN         NaN    1.0                  False             True         False       0.6627            1.0             22.0           31.8954        0.3373
9     9     9   True  27.2292   False      NaN         NaN    1.0                  False             True         False       0.6816            1.0             18.0           28.5382        0.3184
10   10    10   True   4.0500    True      NaN         NaN    1.0                  False             True         False       0.5393            1.0             29.0           38.6003        0.4607
11   11    11   True  42.6985   False      NaN         NaN    1.0                  False             True         False       0.7392            1.0             16.0           23.6503        0.2608
12   12    12   True  25.2828    True      NaN         NaN    1.0                  False             True         False       0.6321            1.0             24.0           33.8575        0.3679
13   13    13   True  27.6266    True      NaN         NaN    1.0                  False             True         False       0.6454            1.0             23.0           32.7132        0.3546
14   14    14   True  24.6072   False      NaN         NaN    1.0                  False             True         False       0.5097            1.0             30.0           40.1969        0.4903
15   15    15   True  48.8226   False      NaN         NaN    1.0                  False             True         False       0.3416            1.0             32.0           42.4071        0.6584
16   16    16   True  16.9486   False      NaN         NaN    1.0                  False             True         False       0.3998            1.0             31.0           40.2152        0.6002
17   17    17   True  19.6193    True      NaN         NaN    1.0                  False             True         False       0.5808            1.0             28.0           37.0291        0.4192
18   18    18   True  40.8453    True      NaN         NaN    1.0                  False             True         False       0.3479            1.0             29.0           39.7353        0.6521
19   19    19   True  25.0690   False      NaN         NaN    1.0                  False            False          True       0.7392            1.0             50.0           60.8258        1.2608

We can see even in this very simple example with only one disease and 20 agents, a lot of data is generated!

Overview of Modules

Starsim contains the following kinds of modules, listed below in the order that they are typically updated:

  • Demographics
  • Diseases
  • Connectors
  • Networks
  • Interventions
  • Analyzers

Modules typically store parameters (e.g. the transmission probability), states of people (e.g. whether they are susceptible, infected, or recovered), and results (e.g. the number of people infected at each point in time).

Overview of a Sim

The Sim object is responsible for storing assembling, initializing, and running the model. The Sim class contains some top-level parameters (including the number of agents in the simulation, the start and stop times, and the random seed) and results (e.g. the population size over time), but almost all other parameters and results are specific to modules and stored within them. There are more details on the Sim on the linked page.

What happens when you add a module?

When you add a module to a Sim, the module’s parameters, states, and results will be added to the centralized collections of parameters, states, and results that are maintained within the Sim. To illustrate this, let’s create a Sim with an SIR disease module and a random contact network:

import starsim as ss 
sir = ss.SIR(dur_inf=10, beta=0.2, init_prev=0.4, p_death=0.2)
sim = ss.Sim(diseases=sir, networks='random')
sim.init()  # Initialize the sim to create 
Initializing sim with 10000 agents
Sim(n=10000; 2000—2050; networks=randomnet; diseases=sir)

The call to sim.init() means that the SIR module gets added to sim.diseases and the RandomNet network gets added to sim.networks. In addition, the following updates are made: * the parameters of the modules are added to the sim’s centralized parameter dictionary, so you can access them via either sim.pars.sir.init_prev or sim.diseases.sir.pars.init_prev * the states specific to each module are added to People, so you can access them via sim.diseases.sir.infected or sim.people.sir.infected * the results specific to each module are added to the centralized Results dictionary of the Sim, so you can access them via sim.diseases.sir.results.n_infected or sim.results.sir.n_infected.

Overview of Results

Once you’ve run a Sim, all the results are stored under sim.results. This is structured similarly to a nested dictionary, with results specific to each module stored in their own dictionaries, like the sim.results.sir.n_infected example above.