import matplotlib.pyplot as plt
from datetime import date
from functools import partial
import numpy as np
from sympy import Max
from epymorph.kit import *
from epymorph.adrio import acs5
Hospitalization IPM
SCENARIO:
In many human disease outbreaks, hospitals and hospitalization policies can have an enormous impact on overall outcomes, e.g., hospitalized persons may die at a lower rate, and may infect fewer others due to being isolated from the susceptible population. In addition, modeling hospitalization is vital for predicting evolving load on local hospital capacity, including the threat of exceeding that capacity.
The exercises in this vignette will show you how to add a basic hospitalization compartment to your model, and use it to explore some of the above hospitalization-related dynamics.
Exercise 1
class initSIRH_ipm(CompartmentModel):
= [
compartments 'S'),
compartment('I'),
compartment('R'),
compartment('H'),
compartment(
]= [
requirements 'beta', type=float, shape=Shapes.TxN,
AttributeDef(='infectivity'),
comment'h_rate', type=float, shape=Shapes.TxN,
AttributeDef(='progression from infected to hospitalized'),
comment'h_recovery', type=float, shape=Shapes.TxN,
AttributeDef(='progression from hospitalized to recovered'),
comment
]
def edges(self, symbols):
= symbols.all_compartments
[S, I, R, H] = symbols.all_requirements
[β, h_rate, h_rec]
= Max(1, S + I + R + H)
N
return [
=β * S * I / N),
edge(S, I, rate=h_rate * I),
edge(I, H, rate=h_rec * H),
edge(H, R, rate ]
= initSIRH_ipm()
sirh_ipm sirh_ipm.diagram()
# Use Coconino County, AZ
= CountyScope.in_counties(['Coconino, AZ'], year=2020)
scope = init.SingleLocation(location=0, seed_size=10)
my_init = mm.No() null_mm
- [] Create a RUME and run the simulation.
= SingleStrataRUME.build(
rume =initSIRH_ipm(),
ipm=null_mm,
mm=my_init,
init=scope,
scope=TimeFrame.of("2020-01-01", 200),
time_frame={
params'beta': 0.4,
'h_rate': 1 / 10,
'h_recovery': 1 / 50,
'population': acs5.Population(),
}
)
= BasicSimulator(rume)
sim with sim_messaging(live = False):
= sim.run(
out =default_rng(5),
rng_factory )
Loading gpm:all::init::population (epymorph.adrio.acs5.Population):
|####################| 100% (1.001s)
Running simulation (BasicSimulator):
• 2020-01-01 to 2020-07-18 (200 days)
• 1 geo nodes
|####################| 100%
Runtime: 0.048s
out.plot.line(=out.rume.scope.select.all(),
geo=out.rume.time_frame.select.all(),
time=out.rume.ipm.select.compartments("H"),
quantity="Hospitalized in Coconino County",
title )
out.plot.line(=out.rume.scope.select.all(),
geo=out.rume.time_frame.select.all(),
time=out.rume.ipm.select.events("I->H", "H->R"),
quantity="New hospitalizations and recovery events",
title )
Exercise 2
# create hospitalization ipm with fork
class SIRH_ipm(CompartmentModel):
= [
compartments 'S'),
compartment('I'),
compartment('R'),
compartment('H'),
compartment(
]= [
requirements 'beta', type=float, shape=Shapes.TxN,
AttributeDef(='infectivity'),
comment'h_rate', type=float, shape=Shapes.TxN,
AttributeDef(='progression from infected to hospitalized or recovered'),
comment'h_recovery', type=float, shape=Shapes.TxN,
AttributeDef(='progression from hospitalized to recovered'),
comment'h_prob', type=float, shape=Shapes.TxN,
AttributeDef(='probablity for hospital versus recovered'),
comment
]
def edges(self, symbols):
= symbols.all_compartments
[S, I, R, H] = symbols.all_requirements
[β, h_rate, h_rec, h_prob]
= Max(1, S + I + R + H)
N
return [
=β * S * I / N),
edge(S, I, rate
fork(=h_rate * I * h_prob),
edge(I, H, rate=h_rate * I * (1 - h_prob)),
edge(I, R, rate
),=h_rec * H)
edge(H, R, rate ]
= SIRH_ipm()
sirh_ipm2 sirh_ipm2.diagram()
= SingleStrataRUME.build(
rume =SIRH_ipm(),
ipm=null_mm,
mm=my_init,
init=scope,
scope=TimeFrame.of("2020-01-01", 200),
time_frame={
params'beta': 0.4,
'h_rate': 1 / 10,
'h_recovery': 1 / 50,
'h_prob': 0.10,
'population': acs5.Population(),
}
)
= BasicSimulator(rume)
sim with sim_messaging(live = False):
= sim.run(
out =default_rng(5),
rng_factory )
Loading gpm:all::init::population (epymorph.adrio.acs5.Population):
|####################| 100% (0.473s)
Running simulation (BasicSimulator):
• 2020-01-01 to 2020-07-18 (200 days)
• 1 geo nodes
|####################| 100%
Runtime: 0.044s
out.plot.line(=out.rume.scope.select.all(),
geo=out.rume.time_frame.select.all(),
time=out.rume.ipm.select.events("I->H", "I->R"),
quantity="New hospitalizations and recovery events",
title )
= []
stored_sim_results = [.01, .05, .10, .20]
probs
for p in probs:
= SingleStrataRUME.build(
rume =SIRH_ipm(),
ipm=null_mm,
mm=my_init,
init=scope,
scope=TimeFrame.of("2020-01-01", 200),
time_frame={
params'beta': 0.4,
'h_rate': 1 / 10,
'h_recovery': 1 / 50,
'h_prob': p,
'population': acs5.Population(),
}
)
= BasicSimulator(rume)
sim = sim.run(
out =default_rng(8),
rng_factory
) stored_sim_results.append(out)
= plt.subplots()
fig, ax
for j in stored_sim_results:
j.plot.line_plt(= ax,
ax =j.rume.scope.select.all(),
geo=j.rume.time_frame.select.all(),
time=j.rume.ipm.select.events("I->H"),
quantity
)
"day")
ax.set_xlabel("I to H events")
ax.set_ylabel("Varying the probability of hospitalization")
ax.set_title(False)
ax.grid(
fig.tight_layout() plt.show()
Exercise 4
Making the limits real. What would happen if the hospital bed capacity were actually reached? In simplest terms, the ‘H’ compartment would be full, and should not be allowed to grow anymore. Let’s explore how this could be modeled in epymorph. Change the static hospitalization rate (h_rate) on the I→H edge to be represented by a stepwise function, described by the following pseudo-code: “If H< capacity h_rate=value1, else h-rate=0”, to cut off flow to the ‘H’ compartment anytime the hospital is reaching capacity. Now run your simulation again, to see if the ‘H’ compartment ever exceeds its capacity.
from sympy import Piecewise
# custom ipm to use simpy to stop at the max bed capacity (stepwise or traditional function)
class SIRH_new(CompartmentModel):
= [
compartments 'S'),
compartment('I'),
compartment('R'),
compartment('H'),
compartment(
]
= [
requirements 'beta', type=float, shape=Shapes.TxN,
AttributeDef(='infectivity'),
comment'h_rate', type=float, shape=Shapes.TxN,
AttributeDef(='progression from infected to hospitalized or recovered'),
comment'h_recovery', type=float, shape=Shapes.TxN,
AttributeDef(='progression from hospitalized to recovered'),
comment'h_prob', type=float, shape=Shapes.TxN,
AttributeDef(='probablity for hospital versus recovered'),
comment'capacity', type=float, shape=Shapes.TxN,
AttributeDef(='allowed capacity of hospital beds'),
comment
]
def edges(self, symbols):
= symbols.all_compartments
[S, I, R, H] = symbols.all_requirements
[β, y, h_rec, h_prob, capacity]
= Max(1, S + I + R + H)
N
return [
=β * S * I / N),
edge(S, I, rate
fork(=Piecewise((y * I * h_prob, H < capacity), (0, True))),
edge(I, H, rate=y * I * (1 - h_prob)),
edge(I, R, rate
),=h_rec * H),
edge(H, R, rate
]
# store hospitalization ipm
# hospital bed capacity (hypothetical)
= 2000.0
capacity
= []
stored_sim_results = [.005, .01, .015, .05]
probs
for p in probs:
= SingleStrataRUME.build(
rume =SIRH_new(),
ipm=null_mm,
mm=my_init,
init=scope,
scope=TimeFrame.of("2020-01-01", 200),
time_frame={
params'beta': 0.4,
'h_rate': 1 / 10,
'h_recovery': 1 / 50,
'h_prob': p,
'capacity': capacity,
'population': acs5.Population(),
}
)
= BasicSimulator(rume)
sim = sim.run(
out =default_rng(8),
rng_factory
) stored_sim_results.append(out)
= plt.subplots()
fig, ax
for j in stored_sim_results:
j.plot.line_plt(= ax,
ax =j.rume.scope.select.all(),
geo=j.rume.time_frame.select.all(),
time=j.rume.ipm.select.compartments("H"),
quantity
)
"day")
ax.set_xlabel("Total Hospitalized")
ax.set_ylabel("Hospitalization capacity")
ax.set_title(False)
ax.grid(
="Hosp. Prob.")
plt.legend(probs, title=capacity, color='red', linestyle='--')
plt.axhline(y
fig.tight_layout() plt.show()