Minimal SIR

Minimal SIR preview image

1 collaborator

Default-person Zebedee Mason (Author)

Tags

covid-19 

Tagged by Zebedee Mason 4 days ago

epidemiology 

Tagged by Zebedee Mason 4 days ago

Visible to everyone | Changeable by the author
Model was written in NetLogo 6.4.0 • Viewed 96 times • Downloaded 5 times • Run 0 times
Download the 'Minimal SIR' modelDownload this modelEmbed this model

Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)


WHAT IS IT?

Do you know how an epidemic grows? One infects two, two infect four or three or two or one or none. All with different probabilities and a weighted average of logistic growth.

The Minimal SIR model is a discrete stochastic compartmental model of an epidemic that averages to the (ordinary differential equation) standard SIR model (Kermack-McKendrick, 1927). Each individual within a population is in one of three states known, in epidemic modelling, as compartments:

  • S - Susceptible to infection.
  • I - Infected and infectious.
  • R - Recovered and immune to re-infection.

HOW IT WORKS

Setup

There are sliders to vary:

  • The size of the population that will be divided into compartments.
  • The proportion of the population that has prior immunity.
  • The basic reproduction number.

The basic reproduction number, R0, is defined as the number of individuals that will be infected by one individual in an otherwise susceptible population.

Go

Initially one individual is infected. At each step the simulation proceeds by each infected individual selecting R0 others at random and irrespective of their state - the model is said to be "well mixed". At the end of the step the infected become recovered and those susceptible that were selected become infected. The simulation finishes when there are no longer any infected.

As the simulation progresses lines are drawn between the previous generation of infected and those individuals that they were in contact with. The colour of the individuals changes according to the code:

  • Susceptible - Blue.
  • Infected - Red.
  • Recovered - Green.

There are three plots of how the statistics vary as the iterations proceed. The first shows how the individuals are divided between the three compartments. The second how the reproduction number varies between zero and R0. The third shows how the three compartments vary with respect to each other.

HOW TO USE IT

Move the sliders then press setup. Perform a simulation and note how the epidemic curves vary.

THINGS TO NOTICE

Sigmoid curves

When plotted against time the number of susceptible always decrease. i.e. the curve is monotonic decreasing. Similarly, the number of recovered is monotonic increasing. For the standard SIR model these are both sigmoid (S-shaped) curves. For a given simulation the plots of the minimal SIR model may be more or less sigmoid.

Ternary plot

Since the sum of the number of individuals in the three compartments is constant, they can be plotted within a triangle. Note that the diagonal axis corresponds to the number of infected being zero.

Herd immunity

At the end of a simulation some individuals may still be susceptible. This is because of an effect known as herd immunity. Note that they are not immune and a subsequent outbreak could occur. To demonstrate this re-run with more prior immunity.

Variation of the reproduction number

If this were constant then the epidemic would be growing exponentially. For no prior immunity the initial step plots a value of R0 which matches its definition. Note that, in general, this does not decrease with time. When the reproduction number is equal to one then every infected individual infects only one other and thus there is a peak in the plot of the number of infected versus time.

If this were the standard SIR model instead then it would be the smoothly decreasing function R0 x S / N where S is the number of susceptible and N is the size of the population.

Selecting R0 others

Each infected picking the number of susceptible is equivalent to sampling (without replacement) R0 individuals from a hypergeometric probability distribution.

Exponential growth

Requires that only susceptible are picked. The probability of that happening can then be calculated by forming a fraction where the numerator is the numbers from the size of the population down to one all multiplied together and the denominator is the number of choices multiplied together. e.g. 31 x 30 x 29 x 28 x ... x 1 divided by 31 x 30 x 29 x 30 ... x 29 for a population of 31, no prior immunity and R0 equal to 2.

Evaluating the fraction results in roughly a two in a million million chance. Truncating it to correspond to the first 8 individuals results in roughly an even chance.

Note that at the end of exponential growth all the individuals have been infected.

Actual examples of exponential growth appear to be all man-made:

  • Compound interest, which dates back to the Babylonians at least
  • Grains of wheat on a chessboard, according to folklore from ancient India and Persia
  • Pyramid schemes

THINGS TO TRY

Average behaviour

The grey lines in the plots are the ensemble averaged behaviour of the current behaviour. There is also a plot of the reproduction rate calculated from the averaged behaviour versus the reproduction rate calculated from the standard SIR model (Kermack-McKendrick, 1927). The infection curve of which is formed by an imbalance between logistic growth and exponential decay:

dI/dt = (R0 x S / N - 1) x I x gamma

where N = S + I + R and gamma = 1

i.e. The reproduction rate is given by R0 x S / N

Set the population to the maximum with no prior immunity. Ensure the history is empty then create the average and see how it eventually coincides with the red line in the background of the plot.

Epidemiological tag

Take a group of children, tell them that when they are first tagged that they are to tag two others. Choose the first one then see whether, at the end of the game, they can be lined up in columns corresponding to generations of the epidemic.

Epidemic, the card game

Take two packs of playing cards, cut them both down to the same 31 cards. One will be used to shuffle and deal from, the other to track the states. A column of cards is formed by considering all the cards in the previous column. For each of those cards remove the corresponding card from the shuffle, shuffle then deal two cards. If that is the first time that a card has been dealt then add the corresponding card to the new column. Return all three cards to the pack for the next shuffle, if you don't then you'll get exponential growth.

EXTENDING THE MODEL

Social networks

In this model each individual has equal chance to infect every other. i.e. The population forms a complete graph. Extending to a sparse graph, specifically a social network, provides a better model. When performed on a network formed by voles the resultant ensemble average behaviour compared to a complete graph showed a lower number of infections and a greater number of steps. The reader might like to think of what this means in the contexts of lockdowns and of seasons.

Stochastic calculus

Repeated application of the hypergeometric distribution can be approximated by using a logit-normal distribution for the ratio of the reproduction number to the basic reproduction number. As such the model debunks various stochastic models whether they have a normal distribution for the reproduction number, use Gillespie’s algorithm or are based on the Reed-Frost model. The people who claimed that epidemics grow exponentially, however, were debunked in 1840 by Dr Farr if not before - exponential growth takes the number of infected to infinity unless some unspecified change happens at some unspecified time.

CREDITS AND REFERENCES

This software implementation was forked from

Copyright 2003 Uri Wilensky.

HOW TO CITE

If you mention this model or the NetLogo software in a publication, we ask that you include the citations below.

For the model itself: * Mason, Z. W. T. (2025). The minimal SIR model, validated then extended to social networks and stochastic differential equations. https://osf.io/y6ckv/. Stoch Answers Ltd, Sheffield, UK.

Please cite the NetLogo software as:

COPYRIGHT AND LICENSE

Copyright 2025 Zebedee Mason

CC BY-NC-SA 3.0

This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License. To view a copy of this license, visit https://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.

Comments and Questions

Please start the discussion about this model! (You'll first need to log in.)

Click to Run Model

extensions [array]
globals [reproduction-number old-infected new-infected
  time iteration
  iteration-s iteration-i iteration-r iteration-r0x
  sum-s sum-i sum-r
  average-s average-i average-r average-r0x
  kmck
]

breed [individuals individual]
individuals-own [
  state                   ;; my current state: "INFECTED", "RECOVERED", or "SUSCEPTIBLE"

  marked?                 ;; whether I'm currently marked

]

to clear-history [clear-all?]
  if clear-all?
  [
    ;; clear the ensemble

    set sum-s []
    set sum-i []
    set sum-r []
    set average-s []
    set average-i []
    set average-r []
    set average-r0x []
    set kmck []
    set iteration 0
  ]

  ;; clear the iteration

  set iteration-s []
  set iteration-i []
  set iteration-r []
  set iteration-r0x []
  set time 0
  set iteration (iteration + 1)
end 

to-report count-state [compartment]
  report (count individuals with [state = compartment])
end 

to-report last-or-zero [last-index lsum]
  ifelse last-index = -1
  [
    report 0
  ]
  [
    report item last-index lsum
  ]
end 

to extend-sums
  let last-index ((length sum-s) - 1)
  while [ length sum-s < length iteration-s]
  [
    set sum-s lput (last-or-zero last-index sum-s) sum-s
    set sum-i lput 0 sum-i
    set sum-r lput (last-or-zero last-index sum-r) sum-r

    set average-s lput 0 average-s
    set average-i lput 0 average-i
    set average-r lput 0 average-r
    set average-r0x lput 0 average-r0x
  ]
end 

to-report add-sum [t1 t2 lsum liter]
  report replace-item t1 lsum ((item t1 lsum) + (item t2 liter))
end 

to-report get-average [t lsum lav]
  report replace-item t lav ((item t lsum) / iteration)
end 

to-report compute-reproduction-number [t lav]
  ifelse t = 0
  [
    report basic-reproduction-number
  ]
  [
    report (item t lav) / (item (t - 1) lav)
  ]
end 

to add-sums
  let t 0
  while [ t < length iteration-s]
  [
    set sum-s (add-sum t t sum-s iteration-s)
    set sum-i (add-sum t t sum-i iteration-i)
    set sum-r (add-sum t t sum-r iteration-r)

    set average-s (get-average t sum-s average-s)
    set average-i (get-average t sum-i average-i)
    set average-r (get-average t sum-r average-r)

    set average-r0x (replace-item t average-r0x (compute-reproduction-number t average-i))

    set t (t + 1)
  ]

  let last-index ((length iteration-s) - 1)
  while [ t < length sum-s]
  [
    set sum-s (add-sum t last-index sum-s iteration-s)
    set sum-i (add-sum t last-index sum-i iteration-i)
    set sum-r (add-sum t last-index sum-r iteration-r)

    set average-s (get-average t sum-s average-s)
    set average-i (get-average t sum-i average-i)
    set average-r (get-average t sum-r average-r)

    set average-r0x (replace-item t average-r0x (compute-reproduction-number t average-i))

    set t (t + 1)
  ]
end 

to calc-kmck
  set kmck []
  set kmck lput basic-reproduction-number kmck
  let t 1
  while [ t < length average-s]
  [
    let val (item (t - 1) average-s)
    set val (val + (item t average-s))
    set val 0.5 * basic-reproduction-number * val
    set val (val / population)
    set kmck lput val kmck

    set t (t + 1)
  ]
end 

to extend-history
  if time >= (length iteration-s)
  [
    set iteration-s lput (count-state "SUSCEPTIBLE") iteration-s
    set iteration-i lput (count-state "INFECTED") iteration-i
    set iteration-r lput (count-state "RECOVERED") iteration-r
    set iteration-r0x lput (compute-reproduction-number time iteration-i) iteration-r0x
  ]

  set time (time + 1)
end 

to plot-array [y]
  let t 0
  while [ t < length y]
  [
    if t = 0
    [
      plot-pen-up
    ]
    plotxy t (item t y)
    plot-pen-down
    set t (t + 1)
  ]
end 

to plot-arrays [x y]
  let t 0
  while [ t < length x]
  [
    plotxy (item t x) (item t y)
    set t (t + 1)
  ]
end 

to print-table [s i r r0x]
  print "Time, S, I, R, Reproduction number"
  let t 0
  while [ t < length s]
  [
    type t type ", "
    type (item t s) type ", "
    type (item t i) type ", "
    type (item t r) type ", "
    print (item t r0x)
    set t (t + 1)
  ]
end 

to print-report
  type "Population " print population
  type "Proportion of population with prior immunity " print prior-immunity-proportion
  type "Basic reproduction number " print basic-reproduction-number
  type "Iterations " print iteration
  print ""
  print "Current"
  print-table iteration-s iteration-i iteration-r iteration-r0x
  print ""
  print "Ensemble average"
  print-table average-s average-i average-r average-r0x
  print ""
end 

to dont-clear-all
  ;;clear-globals

  clear-ticks
  clear-turtles
  clear-patches
  clear-drawing
  clear-all-plots
  clear-output
end 

to setup
  dont-clear-all
  clear-history (sum-s = 0)
  ;; set up the model

  make-turtles
  set reproduction-number basic-reproduction-number
  set new-infected 1
  extend-history
  recolor
  reset-ticks
end 

;; create all the turtles, place them, and associate forks with individuals


to make-turtles
  set-default-shape individuals "person torso"
  ;; create-ordered- equally spaces the headings of the turtles,

  ;; in who number order

  create-ordered-individuals population [
    set size 0.1
    jump 0.35
    set state "SUSCEPTIBLE"
    set marked? false
  ]
  ask individual 0 [set state "INFECTED"]
  if prior-immunity-proportion > 0 [
    let index 1
    repeat prior-immunity-proportion * population [
      ask individual index [set state "RECOVERED"]
      set index (index + 1)
      if index >= population [ stop ]
    ]
  ]
end 

to go
  if new-infected > 0
  [
    a-go
  ]
end 

to a-go
  set old-infected new-infected
  clear-links
  ask individuals [ infect ]
  set new-infected 0
  ask individuals [ update ]
  ifelse old-infected = 0
  [
    set reproduction-number 0
  ]
  [
    set reproduction-number (new-infected / old-infected)
  ]
  recolor
  extend-history
  if new-infected = 0
  [
    extend-sums
    add-sums
    calc-kmck
  ]
  tick
end 

;; everybody gets a new color.


to recolor
  ask individuals [
    ;; look up the color in the colors list indexed by our current state

    ifelse state = "SUSCEPTIBLE"
      [ set color blue ]
      [ ifelse state = "INFECTED"
        [ set color red ]
        [ set color green ] ]
  ]
end 

;;


to infect  ;; individual procedure

  if state = "INFECTED" [
    ;; create an array of random numbers

    let values array:from-list shuffle range (population - 1)
    let index 0
    repeat basic-reproduction-number [
      let i2 array:item values index
      let i3 ((i2 + 1 + who) mod population)
      ask individual i3 [set marked? true]
      create-link-with individual i3
      set index (index + 1)
    ]
  ]
end 

;;


to update  ;; individual procedure

  if state = "INFECTED" [
    set state "RECOVERED"
  ]
  if marked? and state = "SUSCEPTIBLE"
  [
    set state "INFECTED"
    set new-infected (new-infected + 1)
  ]
  set marked? false
end 


; Copyright 2025 Zebedee Mason.

; See Info tab for full copyright and license.

There are 2 versions of this model.

Uploaded by When Description Download
Zebedee Mason 4 days ago Track average behaviour Download this version
Zebedee Mason 13 days ago Initial upload Download this version

Attached files

File Type Description Last updated
Minimal SIR.png preview New screenshot 4 days ago, by Zebedee Mason Download

This model does not have any ancestors.

This model does not have any descendants.