fcp2021 / test_infection_simulation / simulate_people.py
simulate_people.py
Raw
from person import Person
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

#haven't implemented real data, this is just a simulation for 

class Simulation():
    
    def __init__(self):
        #SIMULATION PARAMETERS
        self.n=350  #number of individuals
        self.p_infected = 1  #initial percentage of infection (0-100%)
        self.r_contagious=2 #transmission radius (0-100)
        self.p_contagious= 10  #transmission rate (0-100%)
        self.p_isolation =75  #isolation rate (0-100%)
        self.t_infected=100   #recovery time in FRAMES (0-infinity) 
        self.d_rate = 5 #death rate (0-100%)
        self.reinfect_rate = 0.05 #re-infect rate (0-100%)
        self.infected=0
        self.dead=0
        self.recovered=0
        self.people=[]

    def instantiate(self):

        #for instantiating
        

        #Initialise loop. Creating all the individuals in random positions. Infecting some and putting some in quarantine.
        for i in range(self.n):
            p = Person(i,np.random.random()*100, np.random.random()*100,
                        np.random.random() * 100, np.random.random() * 100,
                        (np.random.random()+0.5)*100, self.t_infected, False)
                        #(index, xpos, ypos, xobj, yobj, velocity, infected_time, fixed=false)

            if np.random.random()<self.p_infected/100: #in percentage
                p.infect(0)
                self.infected+=1
            if np.random.random()<self.p_isolation/100: #in percentage
                p.fixed=True

            self.people.append(p)


    def graphics(self):

        self.instantiate()
        
        #this creates all the graphics
        fig = plt.figure(figsize=(18,9))
        ax = fig.add_subplot(1,2,1)
        cx = fig.add_subplot(1,2,2)
        ax.axis('off')
        cx.axis([0,1000,0,self.n]) #(Xmin,Xmax,Ymin,Ymax)
        scatt=ax.scatter([p.posx for p in self.people],
                [p.posy for p in self.people],c='blue',s=8) 
        box = plt.Rectangle((0,0),100,100,fill=False)
        ax.add_patch(box)
        cvst,=cx.plot(self.infected,color="red",label="Currently infected")
        rvst,=cx.plot(self.recovered,color="gray",label="Recovered")
        dvst,=cx.plot(self.dead,color="black", label="Dead")
        cx.legend(handles=[rvst,cvst,dvst])
        cx.set_xlabel("Time in days")
        cx.set_ylabel("People")


        ct=[self.infected]
        rt=[self.recovered]
        t=[0]
        dt=[self.dead]
        
        return scatt,cvst,ct,rvst,rt,dvst,dt,t,fig

    #function excecuted frame by frame
    #frame : time variable
    def update(self,frame,rt,ct,dt,t):
        
        concycle = 0 #susceptible
        recycle = 0  #recovered
        dead = 0  
        colors = []
        sizes = [8 for p in self.people]
        for p in self.people:
            #check how much time the person has been sick, such that if total time passes, it heals them
            p.check_contagious(frame)
            #check if person is dead, if so, fix them in place
            p.check_death()
            #animate the movement of each person
            p.update_pos(0,0)
            if p.retired:
                recycle+=1 #count the amount of recovered
                for per in self.people:
                #check for people around the sick individual and infect the ones within the
                #transmission radius given the probability
                    if per.index==p.index or per.infected or per.die:
                        pass
                    else:
                        d=p.get_dist(per.posx,per.posy)
                        if d< self.r_contagious:
                            if np.random.random() < self.reinfect_rate / 100:
                                per.infect(frame)
                                sizes[per.index]=40
                                p.fix_position()
            if p.die:
                dead+=1 #count the amount of dead people
            if p.infected:
                concycle=concycle+1 #count the amount of infected
                if p.fixed:
                    pass
                elif np.random.random() < self.d_rate/100:
                    p.kill()


                for per in self.people:
                #check for people around the sick individual and infect the ones within the
                #transmission radius given the probability
                    if per.index==p.index or per.infected or per.retired or per.die:
                        pass
                    else:
                        d=p.get_dist(per.posx,per.posy)
                        if d<self.r_contagious:
                            if np.random.random() < self.p_contagious / 100:
                                per.infect(frame)
                                sizes[per.index]=80
            
            colors.append(p.get_color()) #change dot color according to the person's status

        #update the plotting data
        ct.append(concycle)
        rt.append(recycle)
        dt.append(dead)
        t.append(frame)


        #transfer the data to the matplotlib graphics
        offsets=np.array([[p.posx for p in self.people],
                        [p.posy for p in self.people]])
        scatt.set_offsets(np.ndarray.transpose(offsets))
        scatt.set_color(colors)
        scatt.set_sizes(sizes)
        cvst.set_data(t,ct)
        rvst.set_data(t,rt)
        dvst.set_data(t,dt)

        return scatt,cvst,rvst,dvst


#run the animation indefinitely
if __name__ == "__main__":
    sim = Simulation()
    scatt,cvst,ct,rvst,rt,dvst,dt,t,fig = sim.graphics()
    animation = FuncAnimation(fig, sim.update, interval=25, fargs=(rt,ct,dt,t), blit=True)
    plt.show()