This open-source COVID-19 simulator suggests that fewer deaths in the total population will occur if nurses strike until Personal Protective Equipment (PPE) arrives, compared to the scenario of nurses continuing to work without PPE. When nurses strike until PPE arrives, the number of new infections per day show a "flattening the curve" effect compared to nurses continuing to work without PPE. The scenario of PPE arriving automatically later without a strike shows a similar curve to nurses continuing to work without PPE. This suggests that protecting nurses from infection has the greatest impact on reducing the total number of deaths in the population.
Variables such as probability of infection, probability of an infection requiring hospitalization, maximum number of patients per nurse, number of ventilators per total population, etc. are configurable, and default values are shown in covid19sim.py. To illustrate how the simulator works, a walkthrough of an individual simulation of transmission on a two-dimensional (2D) spatial grid will be displayed visually. At the end, graphs of aggregate data from multiple simulation runs will display the results of comparing different scenarios (nurses striking until PPE arrives versus nurses continuing to work without PPE).
sim.run()
runs a simulation using the configurable parameters and returns the results of the entire simulation in a DataFrame. You can show a snapshot of the situation on a particular day by calling sim.showSpread(df, day)
, where df
is the returned DataFrame, and day
is the day of the infection simulation.
import sys
sys.path.append('../src/')
import covid19sim as sim
df = sim.run()
sim.showSpread(df, 1)
The simulation occurs on a 2D spatial grid, where an initial infection occurs in the centre of the grid. Nurses are symbolized with a cross (+), while non-nurses are symbolized with a dot. Nurses are spread out evenly in the population.
When a person is infected with a severe case of the disease, they need to be hospitalized and are assigned a nurse. The simulation assumes that a hospitalized patient requires either a non-invasive ventilator (unlimited supply) or an invasive (intubating) ventilator (limited supply). In the default simulation parameters where nurses do not have PPE, this causes some nurses to be infected by their patient(s) after 15 days.
sim.showSpread(df, 15)
After 30 days, some deaths occur. The default number of invasive (intubating) ventilators per population in this simulation is based on Canadian data on ICU beds (13.5/100000), and in this simulation of 2048 individuals that fit onto a 2D grid, the number of ventilators is rounded up to 1. The simulator assumes that a patient who requires an invasive ventilator and cannot get one will die, and that any hospitalized patient who cannot get a nurse will die.
sim.showSpread(df, 30)
Aggregate data can be collected by calling sim.aggregations(df)
, which returns a DataFrame aggregating data tpym the raw simulation results. When using Plotly to plot the infection day against the number of new infections requiring hospitalization, we can see a graph that resembles a bell curve.
import plotly.express as px
dfAggregations = sim.aggregations(df)
px.line(dfAggregations, x="Day", y="New Infections Requiring Hospitalization")
Now we have walked through an example of a single simulation run, we can run multiple simulations that compare four scenarios: "Nurses have no PPE"; "Nurses strike but no PPE arrives"; "Nurses strike until PPE arrives"; and "Nurses do not strike and PPE arrives anyway". To simulate infection based on probability of infection, the result of Python's random()
function is one input into whether a person is ultimately infected from an exposure. The final number of infections is highly sensitive to random chance due to the exponential growth function of communicable diseases, so for each of the four scenarios, we run the simulation eight times to see the general trend of each scenario.
When plotting the aggregate results of Day versus New Infections, we find that "Nurses strike until PPE arrives" flattens the curve, while the other three scenarios have similar curves that occur earlier and have a higher maximum.
import math
import pandas as pd
d = 4
n = d * 8
dfMaster = None
for i in range(n):
if i < math.floor(n*1/d):
sim.strikeDays = 0
sim.ppeArrivalDay = 999999
stats = sim.aggregations(sim.run())
cat = "Nurses have no PPE"
elif i < math.floor(n*2/d):
sim.strikeDays = 30
sim.ppeArrivalDay = 999999
stats = sim.aggregations(sim.run())
cat = "Nurses strike but no PPE arrives"
elif i < math.floor(n*3/d):
sim.strikeDays = 30
sim.ppeArrivalDay = 30
stats = sim.aggregations(sim.run())
cat = "Nurses strike until PPE arrives"
else:
sim.strikeDays = 0
sim.ppeArrivalDay = 30
stats = sim.aggregations(sim.run())
cat = "Nurses do not strike and PPE arrives anyway"
dfI = pd.DataFrame(data=stats)
dfI["Run"] = dfI.apply(lambda row: i, axis=1)
dfI["Scenario"] = dfI.apply(lambda row: cat, axis=1)
if dfMaster is None:
dfMaster = dfI
else:
dfMaster = dfMaster.append(dfI)
px.line(dfMaster, x="Day", y="New Infections", color="Scenario")
When we plot Day versus Total Dead for the four scenarios on a line graph, it is difficult to see the general trend.
px.line(dfMaster, x="Day", y="Total Dead", color="Scenario")
We can use an animated "Gapminder-style" scatterplot graph to visualize the relationship of four variables: Time (Day) on the time axis, Total Nurse Infections on the x-axis, Total Dead on the y-axis, and Total Infections represented by the size of the scatterplot point.
maxDeaths = dfMaster[dfMaster["Day"] == sim.totalDays]["Total Dead"].max()
px.scatter(dfMaster, x="Total Nurse Infections", y="Total Dead", animation_frame="Day", animation_group="Run",
size="Total Infections", color="Scenario", hover_name="Run",
log_x=False, size_max=50,
range_x=[-1,len(sim.hospital.nurses)*1.1],
range_y=[-1,maxDeaths*1.1])
This graph suggests that nurses striking until PPE arrives results in fewer deaths in the mid-term, and slightly fewer deaths in the long-term, compared to the other scenarios. The reason may be that given the small number of nurses in the general population and the assumption that a hospitalized patient will die if no nurse is available to care for them, a nurse's death or hospitalization will negatively impact the life outcome of many infected individuals per nurse. This simulation suggests that protecting nurses (or in real world, heathcare workers in general) from infection reduces the total number of deaths in the population and can itself "flatten the curve" of new infections per day.