Coding Stories: Coding for COVID

It has taken me a long time to write this blog post. One might be surprised given that the current pandemic is rich with computational applications. The major news outlets have been voraciously popularizing computational models and mathematical thinking like never before. Later in this post I will share some of my favorite examples. But given all these opportunities why am I struggling to start writing and coding on COVID-19? To add to the surprise, despite my fairly busy role supporting remote learning plan for a preK-12 private school, and spending extra time with my wife and two teenage children in a tiny NYC apartment, I still seem to have extra time on my hands. I have been going on runs, bikes, long walks, listening to audio books, and reading more news than I should. I even seem to be getting sleep. And yet every time I have sat down to start this post I have struggled.

I am not sure this post is the right place to fully explain what has been holding me back, but suffice it to say that COVID-19 is a very emotional experience for me and really for everyone involved. Despite the fact that I seem to be doing less than I normally do in a week I am extremely tired. The act of staying inside, adapting to a different routine, being away from colleagues, students and friends is a drain on my soul. No matter how many Zoom meetings I attend I don’t feel the usual energy that I get from my work. This might represent some critique of the technological work, or that I should perhaps limit the time I spend with computational models, but instead of dwelling on the philosophical, I am going to seize this moment now that my mind is finally ready to start engaging in this work once again.

I first became aware of the transformative effect of COVID-19 while reading an article in the NYT about the importance of a logarithmic scale. In this article by Kenneth Chang he explains how the slope of the logarithmically scaled y-axis can show the doubling time frame for infections. In this way the slope of the line gives an accurate measure of the severity of the situation in different locations.

As I was reading the article I wondered whether a New York Times columnist had tackled such an obviously data driven idea previously. Is a a global pandemic what is required to promote mathematical fluency in the public sphere? I am not sure how well logarithmically scaled axes were understood by the majority of the readership but I was impressed that the NYT was making the attempt. And then I started looking around for other ways the pandemic was being explored.

The very popular YouTube math program Numberphile put out a social distancing episode where two of the hosts shared a story remotely from their shelter in place living quarters.

In the episode a simple equation for the spread of an infectious disease is shared as a series of differential equations and then using Geogebra, a popular mathematics tool, he animates the change over time of those who are susceptible, infected and removed from the infection system. I particularly like this one because it takes a purely mathematical look at the disease. The red line shown below is the curve that keeps getting discussed in the news and ultimately needs to be flattened so that we don’t run out of respirators.

Harry Stevens of the Washington Post also created a very powerful post using small circles to represent how a disease moves through a population.

What I love about this simulation is that is creates a 2D model of how infection spreads from one person to the next. It is actually quite amazing that out of this simple model arises something very similar to the mathematical equations that Numberphile created.

Looking at this simulation I quickly decided that this is how I will begin modeling the infection. I was particularly drawn to the formatting of the graph at the top and considered how I would do that.

If you want to explore the limits of what a model can do the 3b1b series has a show that does not disappoint in exploring many different configurations of a similarly structured 2D disease spread model.

In this video I particularly like how he simulates different real world approaches to flatten the curve such as social distancing, travel, etc. I routinely come back to this channel for inspiring connections between computation, mathematics and the natural world.

After reading some inspiring sources I was now ready to create my own disease simulation. I will walk you through the process here. What is great about models such as this is that they can be built up simply and then adjusted to account for any scenario that you want to model.

Let’s start with Harry Stevens model of the outbreak using a collection of circles on the screen. In his simulation the circles bounce off each other (in surprising non physical ways). In my model I will just allow the circles to move freely across the canvas. The best way to implement an arbitrary number of circles that have functionality is to define a class. In fact you might notice that I have used a similar class in previous posts. I am calling it the Disc class.

class Disc { // these are the individuals who are being infected
constructor() {
// assign a random position
this.pos = createVector(random(width), random(height));
this.state = 'susceptible';
}
show() { // display the individual
noStroke();
fill(color(colors[this.state])); // color depends on .state
circle(this.pos.x, this.pos.y, diameter);
}
}

Ideally this class is defined in its own file which could be called disc.js. Now I just need to instantiate a set of 100 discs in my sketch.js file and create some global variables so that the simulation can function.

let diameter = 10;
let population = 100;
let discs = [];
let colors = {“infected”: ‘#dddd00’,
“susceptible”: ‘#00aa00’,
“recovered”: ‘#00bbbb’,
“dead”: ‘#bb0000’ }
function setup() {
createCanvas(400,400);
for (let i=0;i<population;i++) {
discs.push(new Disc());
}
function draw() {
discs.forEach((d)=>d.show());
}

Now I just need to infect one of the discs by changing its state property

discs[0].state = ‘infected’

Okay we have a population of susceptible discs with one of them infected.

To start spreading the infection I need these discs to be moving. This is a bit more complex. I added the following to the Disc class.

let speed = 3;constructor() {
...
this.dir = createVector(speed, 0);
this.dir.rotate(random(2 * Math.PI));
...
}
move() {
this.dir.rotate(random(0.6) - 0.3);
this.pos.add(this.dir);
}
// and include discs.forEach((d)=>d.move()); in function draw()

Unfortunately this causes the discs to wander off the screen so I added the following lines to the .move() method so that the discs bounce off the edges of the canvas.

let newLoc = this.pos.copy().add(this.dir);
if (newLoc.x < 0 || newLoc.x > width) {
this.dir.x = -1*this.dir.x;
this.pos.add(this.dir);
}
else if (newLoc.y < 0 || newLoc.y > height) {
this.dir.y = -1*this.dir.y;
this.pos.add(this.dir);
}
this.pos.add(this.dir);

In order for the model to simulate disease I needed to create a way for the disease to spread. I wrote an update function for the Disc class

update() {
if (this.state == 'susceptible') {
for (let d of discs) {
if (d.state=='infected' &&
// check if the two discs are touching
d.pos.dist(this.pos)<diameter) {
this.state = "infected";
break; // once infected end the loop for efficiency
}
}
}
this.move(); // I include the move() method here
}

Now any “susceptible” disc that is touching an infected disc will be infected. Of course in a real infection this might be dictated by a probability but for the sake of simplicity all touching discs spread the infection.

Another feature of COVID-19 and really all viruses is that eventually the infection ends. To make this work I set an object property called this.startInfection to be the frameCount when the infection starts.

this.startInfection = frameCount;

Then I included another conditional in the update() method to chance the state to ‘recovered’ when the duration of the infection has passed.

let duration = 100; // global variable for duration of illness
if (this.state == 'infected') {
if ((simFrame - this.startInfection) > duration) {
this.state = 'recovered';
}
}

Now the simulation allows the infection to spread through the population. I added one more feature to the simulation.

if (random() < mortality / duration) {
this.state = 'dead';
}

Some of the people with the infection will not recover and will instead die. I noticed that Kenneth Chang left this aspect of the disease out. That might have been a prudent design choice given how current sensitivity levels. I decided to be true to reality and included a ‘dead’ state for my discs.

Now my simulation was ready to go. But I wanted to have the engaging stacked line plot at the top of the screen shown below.

So I turned to Plot.ly and wrote the following code to create it.

// global variables with the graph data
let current = {'susceptible':99, 'infected':1, 'recovered':0, 'dead':0};
let history = {'susceptible':[], 'infected':[], 'recovered':[], 'dead':[]};
function showGraph() {
let traces=[];
for (let key in colors) {
traces.push({
x:frames, y:history[key],
stackgroup:'one',
fillcolor: colors[key],
line: {width:0},
name:current[key]+' '+key
});
}
let layout= {
title: 'COVID-19 SIMULATION',
xaxis: {range: [0,500] },
yaxis: {range: [0,population]},
height:125,
width: 700,
margin: {
l: 40, r: 40, b: 17, t: 30, pad: 1
}
}
Plotly.newPlot('graph', traces, layout);
}

Finally I put in some sliders to control the variables that impact the course of the outbreak. I encourage you to experiment with the sliders to see each one impacts the results, and how they interact each other.

The media is full of explanations of how particular disease factors and human behavior factors impact the spread of an epidemic. In this model you can explore these ideas for yourself and draw your own conclusions. If you want to keep track of your results I encourage you to take screenshots capturing the values of the slider parameters and the shape of the stacked line graph. One challenging aspect of infectious disease is building an intuitive sense of how an outbreak changes over time. Between the counter-intuitive nature of geometric progressions and the multitude of factors that impact the spread it is very difficult to develop these intuitions. That is why computational models are so powerful. By creating and running the model instead of relying on your innate intuitions, you can simply experience the outcomes of the computer model. By developing an ability to create and modify such computational models we are in effect building the capacity for these intuitions despite the fact that they do not come naturally the architecture of our brains. So I would argue that computational modeling is a way of extending the types of thinking we are capable of, and the depth of knowledge we possess about the natural world.

While I hope you find this model interesting and useful, I totally understand if you decide to return to this topic at a time when COVID-19 is a less emotional trigger.

exploring the intersection of coding, education and disciplinary knowledge

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store