diff --git a/DemoNotebooks/COVID-19.ipynb b/DemoNotebooks/COVID-19.ipynb index f22ad10..b2192ea 100644 --- a/DemoNotebooks/COVID-19.ipynb +++ b/DemoNotebooks/COVID-19.ipynb @@ -4,14 +4,17 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# MuMoT Demonstration Notebook: SEIR-Derived Epidemiological Model of COVID-19 and Social Distancing\n", + "# MuMoT Demonstration Notebook: SEIR-Derived Epidemiological Models of COVID-19 and Social Distancing\n", "\n", "## Multiscale Modelling Tool \n", "\n", "*James A. R. Marshall, Department of Computer Science, University of Sheffield*\n", "\n", "# Introduction\n", - " \n" + " \n", + "This notebook aims to help explain the possible epidemiology of the COVID-19 novel coronavirus infectious disease, caused by the SARS-CoV-2 virus (WHO, 2020). Models are presented to show how different health policies may impact on the progression of the disease through a population. The models are simple 'toy' models for illustrative purposes, although model parameters are taken from the literature on the disease.\n", + "\n", + "_**If you are a non-specialist, you may simply want to go to the `Kernel` menu above, select `Restart & Run All`, then skip to the interactive plots and use these, along with reading their accompanying bold text.**_" ] }, { @@ -29,7 +32,7 @@ "source": [ "# Model Definitions\n", "\n", - "Our model is adapted from the standard SEIR model (Susceptiple, Exposed, Infectious, Recovered). The SEIR model is a model used to represent infectious diseases with a latent period following infection, before an individual becomes infectious, and has been used in modelling COVID-19 (Wu et al., 2020). However, since COVID-19 also exhibits infection by asymptomatic individuals (Wu et al., 2020), we also include an infection process from members of the $E$ class, which is not usual in SEIR models. Since asymptomatic individuals apparently can also recover directly without becoming symptomatic, we also adapt the SEIR model to allow direct recovery from the exposed class $E$. For simplicity, we assume the same infection rate $r$ regardless of whether a person is in $E$ or $I$.\n", + "Our model is adapted from the standard SEIR model (Susceptiple, Exposed, Infectious, Recovered). The SEIR model is a model used to represent infectious diseases with a latent period following infection, before an individual becomes infectious, and has been used in modelling COVID-19 (e.g.Wu et al., 2020). However, since COVID-19 may exhibit significant presymptomatic infectiousness (Anderson et al., 2020; Du et al.2020, Tindal et al., 2020), we also include an infection process from members of the $E$ class, which is not usual in SEIR models; comparatively few models of COVID-19 to date have explicitly included infection by asymptomatic individuals (see Cao et al., 2020 for an exception). Since asymptomatic individuals apparently can also recover directly without becoming symptomatic, we also adapt the SEIR model to allow direct recovery from the exposed class $E$. For simplicity, we assume the same infection rate $r$ regardless of whether a person is in $E$ or $I$.\n", "\n", "For COVID-19 infection rates are assumed to be higher than recovery rates.\n", "\n", @@ -46,8 +49,8 @@ "source": [ "%%model\n", "$\n", - "S + I -> I + E: r\n", - "S + E -> E + E: r\n", + "S + I -> I + E: r * s\n", + "S + E -> E + E: r * (1 - s)\n", "E -> I: a\n", "E -> R: g\n", "I -> R: g\n", @@ -61,7 +64,26 @@ "outputs": [], "source": [ "fullmodel = mumot.parseModel(In[-2])\n", - "fullmodel.show()" + "finitemodel = fullmodel.substitute('S = N - I - E - R')\n", + "finitemodel.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Results\n", + "In the following, we analyse the models using both infinite-population ODEs, and finite population spatial stochastic simulations. _The latter are particularly good for allowing us to vary *social distancing* and see the effects on the infection's progress_.\n", + "\n", + "## Social Distancing Across the Population\n", + "We parameterise the model to approximate the characteristics of COVID-19; the purpose of this demonstration is to show the effects of social distancing, so exact parameterisation is not required but could be found from the literature (see sample literature in References). Here we choose $r=2.5$ and $a=g=1/2$; estimates of infectiousness vary but are higher than previous comparable diseases (see Liu et al., 2020, for a review) estimates of asymptomatic transmissions share of total infections vary between being a minority (Du et al., 2020) and, potentially, a majority Tindal et al., 2020); we assume infectiousness for members of $E$ is half that of members in $I$ ($s=2/3$). We then present a controller in which there is a single slider to change the degree of social isolation. This enables us to easily compare the effects of no social isolation (a 'well-mixed' population, left) with social isolation (a spatial, partially connected population, right)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_**You can see how social distancing affects the spread of the disease within the population by looking inside the population as individuals mingle and interact. Open the 'Advanced options' tab and vary between interaction range between 0 (full social distancing) and 1 (no social distancing), then observe the effect on the population. Recall that green individuals are the ones who may need hospital treatment, and the red individuals are recovered. Implementing social distancing means the virus has fewer opportunities to jump between individuals, so doesn't spread as fast.**_" ] }, { @@ -70,33 +92,77 @@ "metadata": {}, "outputs": [], "source": [ - "finitemodel = fullmodel.substitute('S = N - I - E - R')" + "agentcont1 = finitemodel.multiagent(ylab = 'cases', initialState = {'E': 0.05, 'I': 0.0, 'R': 0.0, 'S': 0.95}, initWidgets={'netParam':[1.0, 0, 1.0, 0.05]}, maxTime = 10.0, timestepSize = 0.25, randomSeed = 731529356, netType = 'dynamic', motionCorrelatedness = 0.5, particleSpeed = 0.01, showTrace = False, showInteractions = False, visualisationType = 'graph', plotProportions = True, realtimePlot = True, runs = 1, aggregateResults = True, bookmark = False, silent = False, params = [('s', 2/3),('a', 0.5), ('g', 0.5), ('r', 2.0), ('plotLimits', 1), ('systemSize', 100.0)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "# Results\n", + "Open the 'Advanced options' tab again and vary the interaction range between 0 (full social distancing) and 1 (no social distancing), then observe the effect on a sample infection trajectory in the right hand plot, compared to the left hand reference plot (no social distancing). The plot shows the fractions of the total population that are in the different disease states, indicated by the figure lengends. Note that social distancing not only reduces the peak number of cases needing medical attention, but also the total number of infections.\n", "\n", - "In the following, we analyse the models using both infinite-population ODEs, and finite population spatial stochastic simulations. _The latter allow us to vary *social isolation* and see the effects on the infection's progress_.\n", - "\n", - "We parameterise the model to approximate the characteristics of COVID-19; the purpose of this demonstration is to show the effects of social distancing, so exact parameterisation is not required but could be found from the literature (e.g. Wu et al., 2020). Here we choose $r=2$ and $a=g=1/2$. We then present a controller in which there is a single slider to change the degree of social isolation. This enables us to easily compare the effects of no social isolation (a 'well-mixed' population, left) with social isolation (a spatial, partially connected population, right). Open the 'Advanced options' tab and vary between interaction range between 0 (full social distancing) and 1 (no social distancing), then observe the effect on a sample infection trajectory in the right hand plot, compared to the left hand reference plot (no social distancing). Note that social distancing not only reduces the peak number of cases needing medical attention, but also the total number of infections.\n", - "\n" + "_**As you vary the interaction range slider between full social contact (1.0) and no social contact (0.0) you should observe a threshold effect at around 0.10, where the progress of the disease is drastically reduced; the maximum proportion of cases at any point in time (green line) reduces.**_" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [], "source": [ "multicont1 = mumot.MuMoTmultiController(\n", " [finitemodel.integrate(showStateVars=['E', 'I', 'R'], ylab = 'cases', initialState = {'E': 0.05, 'I': 0.0, 'R': 0.0, 'S': 0.95}, maxTime = 10.0, plotProportions = True, silent = True, conserved = True, bookmark = False),\n", - " finitemodel.multiagent(ylab = 'cases', initialState = {'E': 0.05, 'I': 0.0, 'R': 0.0, 'S': 0.95}, maxTime = 10.0, timestepSize = 0.25, randomSeed = 731529356, netType = 'dynamic', motionCorrelatedness = 0.5, particleSpeed = 0.01, showTrace = False, showInteractions = False, visualisationType = 'evo', plotProportions = True, realtimePlot = False, runs = 1, aggregateResults = True, silent = True, bookmark = False)], params = [('a', 0.5), ('g', 0.5), ('r', 2.0), ('plotLimits', 1), ('systemSize', 100.0)],\n", + " finitemodel.multiagent(ylab = 'cases', initialState = {'E': 0.05, 'I': 0.0, 'R': 0.0, 'S': 0.95}, initWidgets={'netParam':[1.0, 0, 1.0, 0.05]}, maxTime = 10.0, timestepSize = 0.25, randomSeed = 731529356, netType = 'dynamic', motionCorrelatedness = 0.5, particleSpeed = 0.01, showTrace = False, showInteractions = False, visualisationType = 'evo', plotProportions = True, realtimePlot = False, runs = 1, aggregateResults = True, silent = True, bookmark = False)], params = [('s', 2/3),('a', 0.5), ('g', 0.5), ('r', 2.5), ('plotLimits', 1), ('systemSize', 100.0)],\n", " choose_yrange = [0, 1])" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Social Distancing vs Isolating Symptomatic Cases\n", + "\n", + "We now adapt our model to examine the possible relative effectiveness of social distancing, compared to isolating only symptomatic individuals. The basic infectiousness of the disease $r$ can be varied, as can the share of infections generated by symptomatic individuals $I$ ($s$). We make a new model in which symptomatic individuals are isolated so reduce their infectivity ten-fold, and introduce a social distancing parameter $d$ that scales contacts between asymptomatic individuals ($E$) and susceptible individuals ($S$). For this scenario we assume a majority of infections occuring asymptomatically (Tindal et al., 2020), $s=1/3$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%model\n", + "$\n", + "S + I -> I + E: r * s / 10\n", + "S + E -> E + E: r * d * (1 - s)\n", + "E -> I: a\n", + "E -> R: g\n", + "I -> R: g\n", + "$" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "_**Vary the social distancing parameter $d$ to see the difference that social distancing by asymptotic carriers makes (righthand plot), on top of isolation only of symptomatic cases (lefthand plot). You can also vary the infectiousness of the disease ($r$) and proportion of infections due to symptomatic carriers ($s$).**_" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "compmodel = mumot.parseModel(In[-2]).substitute('S = N - I - E - R')\n", + "multicont2 = mumot.MuMoTmultiController(\n", + " [compmodel.integrate(showStateVars=['E', 'I', 'R'], ylab = 'cases', initialState = {'E': 0.05, 'I': 0.0, 'R': 0.0, 'S': 0.95}, maxTime = 10.0, choose_yrange = [0, 1], plotProportions = True, silent = True, conserved = True, bookmark = False, params = [('d', 1)]),\n", + " compmodel.integrate(showStateVars=['E', 'I', 'R'], ylab = 'cases', initialState = {'E': 0.05, 'I': 0.0, 'R': 0.0, 'S': 0.95}, maxTime = 10.0, choose_yrange = [0, 1], plotProportions = True, silent = True, conserved = True, bookmark = False)],\n", + " initWidgets={'r':[2.5, 0, 4, 0.1],'s':[0.35, 0, 1, 0.05], 'd':[1, 0.5, 1, 0.05]}, params = [('a', 0.5), ('g', 0.5), ('plotLimits', 1), ('systemSize', 10)])" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -104,7 +170,13 @@ "# References \n", "\n", "