#!/usr/bin/perl # model the spread of a pandemic # Andrew Daviel, Nov 2009 # generate 2 output files: "pan.gif", an animated GIF, and # "pan0.dat", a summary file suitable for gnuplot # Initially populate a grid with uninfected people seeded # with a few infected ones # Each loop (one "day") people move around according to their mobility # index. Except the dead ones, or if they stay home # If they come close to an uninfected person, they may infect them $steps = shift or $steps = 50 ; $type = shift or $type = 0 ; $type[0] = 'none' ; $type[1] = 'vulnerable' ; $type[2] = 'mobile' ; $type[3] = 'random' ; $size = shift or $size = 200 ; $ndose = shift or $ndose = 500 ; use GD; $| = 1 ; $spont = 0.05 ; # chance of spontaneous development $morb = 0.003 ; # chance of dying if infected $maxd = 4 ; # max NY distance to catch $trec = 10 ; # recovery time $tinf = 7 ; # till realize sick $pinf = 0.5 ; # probability of infection if meet #$ndose = 500 ; # doses of vaccine available $maxi = 15 ; # max initial infections $maxx = $maxy = $size ; $im = new GD::Image($maxx,$maxy); $white = $im->colorAllocate(255,255,255); # background $black = $im->colorAllocate(0,0,0); $red = $im->colorAllocate(255,0,0); $blue = $im->colorAllocate(0,0,255); $blue2 = $im->colorAllocate(0,0,155); $green = $im->colorAllocate(0,255,0) ; $yellow = $im->colorAllocate(255,255,0) ; # setup $i = 0 ; for ($x=1;$x<$maxx;$x+=5) { for ($y=1;$y<$maxy;$y+=5) { $x[$i] = $x ; $y[$i] = $y ; # initial position $health[$i] = 0 ; $tinf[$i] = 0 ; $mov[$i] = 1 ; $mov[$i] = 2 + 150* rand(1) * rand(1) * rand(1) ; # mobility $morb[$i] = 1 * rand(1) * rand(1) * rand(1) ; # vulnerability if (rand(1) < $spont and $ni<$maxi ) { $health[$i] = 1 ; $ni++ ; } $i++ ; } } open (OUT,">pan0.dat") ; print OUT "# k x y morb health mov (strategy=$type[$type])\n" ; print OUT "# tinf $tinf pinf $pinf trec $trec maxi $maxi\n" ; $N = $i ; $i = 0 ; for ($k=0;$k<$N;$k++) { if ($morb[$k] > 0.15 and $type == 1) { $health[$k] = 4 ; $i++ ; } # vaccinate vulnerable if ($mov[$k] > 20 and $type == 2) { $health[$k] = 4 ; $i++ ; } # vaccinate mobile if (rand(1)<0.3 and $type == 3) { $health[$k] = 4 ; $i++ ; } # vaccinate randomly if ($i>=$ndose) { last ; } # out of vaccine print OUT "$k $x[$k] $y[$k] $morb[$k] $health[$k] $mov[$k] \n" ; } close(OUT) ; $nv = $i ; #$health[15] = $health[139] = 1 ; # 1 infection #$mov[15] = $mov[39] = $mov[79] = $mov[139] = 16 ; #mobile print "# $N people $ni infected, strategy $type ($type[$type])\n" ; print "# T sick immune dead vaccinated\n" ; print "# tinf $tinf pinf $pinf trec $trec maxi $maxi\n" ; my $gifdata = $im->gifanimbegin; $gifdata .= $im->gifanimadd; # first frame for ($t=1;$t<$steps;$t++) { my $frame = GD::Image->new($im->getBounds); $white = $frame->colorAllocate(255,255,255); # background $black = $frame->colorAllocate(0,0,0); $red = $frame->colorAllocate(255,0,0); $blue = $frame->colorAllocate(0,0,255); $blue2 = $frame->colorAllocate(0,0,155); $green = $frame->colorAllocate(0,255,0) ; $yellow = $frame->colorAllocate(255,255,0) ; $nsick = $nimmune = $nhealthy = $ndead = 0 ; # $health 0 healthy 1 sick 2 dead 3 immune 4 = vaccinated $color[0] = $green ; $color[1] = $red ; $color[2] = $black ; $color[3] = $blue ; $color[4] = $blue2 ; for ($i=0;$i<$N;$i++) { if ($health[$i] == 0) { $nhealthy++ ; } if ($health[$i] == 1) { $nsick ++ ; } if ($health[$i] == 2) { $ndead++ ; } if ($health[$i] == 3) { $nimmune++ ; } $frame->filledEllipse($x[$i],$y[$i],2,2,$color[$health[$i]]) ; } $gifdata .= $frame->gifanimadd(0,0,0,5); # add frame, 100ms print "$t $nsick $nimmune $ndead $nv\n" ; if ($nsick == 0) { last ; } undef (@XY) ; # speed up neighbour search by populating array of "geographic" points # so to find possible new infectees, do not need to compute distance # to everyone else but just to close-by people for ($k=0;$k<$N;$k++) { $XY[$x[$k]][$y[$k]] .= "$k," ; } for ($k=0;$k<$N;$k++) { if ($health[$k] == 1 and ($t - $tinf[$k]) > $trec) { if (rand(1) < $morb[$k]) { $health[$k] = 2 ; # die } else { $health[$k] = 3 ; # recover } } for ($nx=$x[$k]-$maxd;$nx<=$x[$k]+$maxd;$nx++) { if ($nx<0 or $nx>$maxx) { next ; } for ($ny=$y[$k]-$maxd;$ny<=$y[$k]+$maxd;$ny++) { if ($ny<0 or $ny>$maxy) { next ; } foreach $k2 (split(',',$XY[$nx][$ny])) { if ($k == $k2) { next ; } if ($k2 eq '') { next ; } $nyd = abs($x[$k]-$x[$k2]) + abs($y[$k]-$y[$k2]) ; if ($nyd > $maxd ) { next ; } if ($health[$k] == 0 and $health[$k2] == 1 and rand(1) < $pinf # stay in bed and ($t - $tinf[$k2]) < $tinf ) { $health[$k] = 1 ; $tinf[$k] = $t ; next ; } } } } # if ($health[$k] == 1 and rand(1) < $morb[$k]) { $health[$k] = 2 ; } unless ($health[$k] == 2 or ( $health[$k] ==1 and ($t-$tinf[$k] > $tinf)) ) { $mov2 = $mov[$k]*2 + 1 ; $x[$k] += int(rand($mov2))-$mov[$k] ; $y[$k] += int(rand($mov2))-$mov[$k] ; if ($x[$k] < 0) { $x[$k] += $maxx ; } if ($y[$k] < 0) { $y[$k] += $maxy ; } if ($x[$k] > $maxx) { $x[$k] -= $maxx ; } if ($y[$k] > $maxy) { $y[$k] -= $maxy ; } } } } $gifdata .= $im->gifanimend; # finish the animated GIF open (OUT,">pan.gif") ; print OUT $gifdata; #print OUT $im->png;