Skip to content

Running Simulations

Once you've built a model, use the Simulation class to run it and analyze results.

Basic Simulation

from epimodel import Simulation

# Create simulation from model
simulation = Simulation(model)

# Run for 100 time steps
results = simulation.run(num_steps=100)

Output Formats

EpiModel supports two output formats for simulation results.

Dictionary of Lists (Default)

Each disease state maps to a list of values over time:

results = simulation.run(num_steps=100, output_format="dict_of_lists")

# Access results
susceptible = results["S"]  # [990, 985, 978, ...]
infected = results["I"]     # [10, 15, 22, ...]
recovered = results["R"]    # [0, 0, 0, ...]

# Get final values
final_S = results["S"][-1]
final_I = results["I"][-1]
final_R = results["R"][-1]

print(f"Final state: S={final_S:.0f}, I={final_I:.0f}, R={final_R:.0f}")

Best for: Plotting, time series analysis, accessing specific compartments

List of Lists

Each time step is a list of all compartment values:

results = simulation.run(num_steps=100, output_format="list_of_lists")

# results[time][compartment_index]
initial_state = results[0]     # [990, 10, 0]
midpoint_state = results[50]   # [450, 200, 350]
final_state = results[-1]      # [340, 50, 610]

# Iterate over time steps
for t, state in enumerate(results):
    total = sum(state)
    print(f"Step {t}: Total population = {total}")

Best for: Matrix operations, comparing states, exporting to CSV

Working with Stratifications

Stratifications create multiple compartments by combining disease states with stratification categories. Understanding how to access and analyze stratified results is crucial for complex models.

Understanding Stratified Compartment Names

When you add stratifications, EpiModel creates compartments by combining disease state IDs with stratification category names using underscore notation:

Pattern: {disease_state}_{category1}_{category2}_...

# Model with one stratification (age)
.add_disease_state(id="S", name="Susceptible")
.add_disease_state(id="I", name="Infected")
.add_stratification(id="age", categories=["young", "old"])

# Creates compartments: S_young, S_old, I_young, I_old
# Model with two stratifications (age and location)
.add_disease_state(id="I", name="Infected")
.add_stratification(id="age", categories=["child", "adult"])
.add_stratification(id="location", categories=["urban", "rural"])

# Creates compartments:
# I_child_urban, I_child_rural, I_adult_urban, I_adult_rural

Accessing Stratified Results

1. List All Compartments

results = simulation.run(num_steps=100)

# See all compartment names
print("All compartments:", list(results.keys()))
# Output: ['S_young', 'S_old', 'I_young', 'I_old', 'R_young', 'R_old']

# Count compartments
print(f"Total compartments: {len(results)}")

2. Access Specific Strata

# Access specific age groups
young_infected = results["I_young"]
old_infected = results["I_old"]

# Access specific combinations (multiple stratifications)
urban_child_infected = results["I_child_urban"]
rural_adult_infected = results["I_adult_rural"]

3. Filter Compartments by Pattern

# Get all infected compartments
infected_keys = [key for key in results.keys() if key.startswith("I_")]
print("Infected compartments:", infected_keys)

# Get all young compartments
young_keys = [key for key in results.keys() if "_young" in key]
print("Young compartments:", young_keys)

# Get all urban compartments (multi-stratification)
urban_keys = [key for key in results.keys() if "_urban" in key]

Aggregating Stratified Results

Sum Across One Stratification

# Total infected across all age groups
total_infected = np.array(results["I_young"]) + np.array(results["I_old"])

# Or using list comprehension
total_infected = [y + o for y, o in zip(results["I_young"], results["I_old"])]

Common Pitfalls

1. Case Sensitivity

# Compartment names use exact category names
.add_stratification(id="age", categories=["Young", "Old"])  # Capital Y and O
# Access with: results["I_Young"], results["I_Old"]

2. Order of Stratifications

# Categories combine in the order stratifications are added
.add_stratification(id="age", categories=["young", "old"])
.add_stratification(id="location", categories=["urban", "rural"])

# Creates: I_young_urban, I_young_rural, I_old_urban, I_old_rural
# NOT: I_urban_young, I_rural_young, etc.

3. Missing Compartments

# Always check compartments exist before accessing
key = "I_young"
if key in results:
    data = results[key]
else:
    print(f"Compartment {key} not found. Available: {list(results.keys())}")

Next Steps