# Dual Epidemic Simulation
# (c) 2020 Kevan Hashemi, Brandeis University.
# Set up the graphical user interface.
wm title . "Epidemic Simulation Version 2.2"
set param(run) "Stop"
set g [frame .f1]
pack $g -side top -fill x
label $g.state -textvariable param(run) -fg blue
pack $g.state -side left -expand yes
foreach a {Construct Start Stop Reset Help} {
set b [string tolower $a]
button $g.$b -text $a -command "epidemic_$b"
pack $g.$b -side left -expand yes
}
set g [frame .f2]
pack $g -side top -fill x
foreach a {uninfected infected_S recovered_S infected_L recovered_L} {
label $g.l$a -text "$a"
entry $g.e$a -textvariable param($a) -width 6
pack $g.l$a $g.e$a -side left -expand yes
}
set g [frame .f3]
pack $g -side top -fill x
foreach a {contact_rate_S contact_rate_L recover_days time} {
label $g.l$a -text "$a"
entry $g.e$a -textvariable param($a) -width 4
pack $g.l$a $g.e$a -side left -expand yes
}
set t [text .text -relief sunken -border 2 -setgrid 1 \
-height 20 -width 60 -wrap word]
$t configure -yscrollcommand ".vsb set"
set vsb [scrollbar .vsb -orient vertical -command "$t yview"]
pack $vsb -side right -fill y
pack $t -expand yes -fill both
bind $t [list $t delete 1.0 end]
bind $t [list $t delete 1.0 end]
bind $t [list $t delete 1.0 end]
$t tag configure title -foreground purple
$t tag configure help -foreground brown
update
# The reset routine goes back to the no-infected state.
proc epidemic_reset {} {
global param
set param(uninfected) "10000"
set param(infected_S) "5"
set param(recovered_S) "0"
set param(infected_L) "5"
set param(recovered_L) "0"
set param(contact_rate_S) "0.3"
set param(contact_rate_L) "0.2"
set param(recover_days) "14"
set param(time) "0"
epidemic_construct
}
# The print routine prints time and the population distribution.
proc epidemic_print {} {
global param t population
set n [llength $population]
$t insert end "$param(time) "
foreach a {uninfected infected_S recovered_S infected_L recovered_L} {
$t insert end "[format %.3f [expr 100.0*$param($a)/$n]] "
$t yview moveto 1
}
$t insert end "\n"
}
# The population list, each entry contains two numbers. The first
# is the number of days infected, which is zero until infected,
# then increases each day to recover_days, then stops, to indicate
# recovery.
set population [list]
# We construct a new array. All infected will be set to the first
# day of infection.
proc epidemic_construct {} {
global param population t
if {$param(run) != "Stop"} {return}
set param(run) "Init"
update
set population [list]
for {set i 0} {$i < $param(uninfected)} {incr i} {
lappend population "0"
}
for {set i 0} {$i < $param(infected_S)} {incr i} {
lappend population "1"
}
for {set i 0} {$i < $param(recovered_S)} {incr i} {
lappend population "$param(recover_days)"
}
for {set i 0} {$i < $param(infected_L)} {incr i} {
lappend population "-1"
}
for {set i 0} {$i < $param(recovered_L)} {incr i} {
lappend population "-$param(recover_days)"
}
set param(time) "0"
set param(run) "Stop"
$t insert end "\nNew Distribution Constructed:\n" title
epidemic_print
}
# We stop the simulation by setting the run flag to Stop, which will bring
# the run procedure to a stop.
proc epidemic_stop {} {
global param
set param(run) "Stop"
}
# The start procedure starts the simulation.
proc epidemic_start {} {
global param
if {$param(run) != "Stop"} {return}
set param(run) "Run"
after 10 epidemic_run
}
# The run procedure goes through all entries in the population list.
# If currently infected with the S strain, increment the entry value,
# and at random select another uninfected member of population for
# infection. If L straing, decrement the entry value and do same
# selection. If recovered, do nothing. At the end of the day,
# update the numbers of various subsets of the population, then posts
# itself to the event queue.
proc epidemic_run {} {
global param population t
if {$param(run) != "Run"} {return}
if {![winfo exists $t]} {return}
set n [llength $population]
for {set i 0} {$i < $n} {incr i} {
set p [lindex $population $i]
# Unifected, recovered, and recovered people don't infect anyone.
if {($p == 0) \
|| ($p == $param(recover_days)) \
|| ($p == -$param(recover_days))} {
continue
}
# Those infected with the S strain can infect another uninfected
# person.
if {$p > 0} {
if {rand() < $param(contact_rate_S)} {
set ii [expr round(rand()*$n-0.5)]
if {$ii>$n} {set ii $n}
if {([lindex $population $ii] == 0)} {
lset population $ii "1"
}
}
lset population $i [expr $p + 1]
} else {
# Those infected with L strain can infect another uninfected
# person.
if {rand() < $param(contact_rate_L)} {
set ii [expr round(rand()*$n-0.5)]
if {$ii>$n} {set ii $n}
if {([lindex $population $ii] == 0)} {
lset population $ii "-1"
}
}
lset population $i [expr $p - 1]
}
}
foreach a {uninfected infected_S recovered_S infected_L recovered_L} {
set $a 0
}
foreach p $population {
if {$p == $param(recover_days)} {
incr recovered_S
} elseif {$p > 0} {
incr infected_S
} elseif {$p == 0} {
incr uninfected
} elseif {$p == -$param(recover_days)} {
incr recovered_L
} else {
incr infected_L
}
}
foreach a {uninfected infected_S recovered_S infected_L recovered_L} {
set param($a) [set $a]
}
incr param(time)
epidemic_print
if {$infected_S + $infected_L == 0} {
$t insert end "Done: no more infected people.\n" title
set param(run) "Stop"
} else {
after 10 epidemic_run
}
}
proc epidemic_help {} {
global t
$t insert end {
Summary: Set population sizes and parameter values, press Construct, press
Start. Simulation runs until epidemic is over, or you press Stop.
The simulation introduces two related viruses, S and L, into a population
through a number of individuals infected with each virus. Immunity or infection
by one virus bestows immunity to infection by the other. Recovery takes the same
amount of time for both viruses. But each virus has its own contact rate, which
is the percentage chance per day that an infected person will spread the disease
to one other person. We press Construct to create the population database, then
Start to run the simulation, Stop to pause it. We get the default values of all
the above numbers with Reset. When there are no more infected people, the
simulation stops.
The simulation proceeds in steps of length one day. Each day, every infected
member of the population proceeds one more day towards recovery, and has a
chance to infect another randomly-selected member of the population. If this
randomly selected person is already infected, or already recovered, the
infection will fail. Once recovered, they remain recovered. They can neither
infect nor can they be infected.
The simulation printes to the screen the day number, and the numbers of:
uninfected, infected with S, recovered from S, infected with L, and recovered
from L. These numbers are intended for cut and past into spreadsheets for
plotting. These same numbers are displayed in their entry boxes at the top of
the window.
The default values we obtain with Reset are supposed to represent the
introduction of a few people with each virus into a population of 10000. The
contact rate of the S strain is 0.3, which gives a doubling time of around 3
days. That of the L strain is 0.2, a doubling time of five days.
Kevan Hashemi, 21-MAR-20
} help
}
epidemic_reset