class Simulation: # TODO: if lockdown or otherwise stopped: destination -1 means no motion def __init__(self, *args, **kwargs): # load default config data self.Config = Configuration() self.frame = 0 # initialize default population self.population_init() self.pop_tracker = Population_trackers() # initalise destinations vector self.destinations = initialize_destination_matrix(self.Config.pop_size, 1) self.fig, self.spec, self.ax1, self.ax2 = build_fig(self.Config) # set_style(self.Config) def population_init(self): """(re-)initializes population""" self.population = initialize_population( self.Config, self.Config.mean_age, self.Config.max_age, self.Config.xbounds, self.Config.ybounds, ) def tstep(self): """ takes a time step in the simulation """ # check destinations if active # define motion vectors if destinations active and not everybody is at destination active_dests = len( self.population[self.population[:, 11] != 0] ) # look op this only once if active_dests > 0 and len(self.population[self.population[:, 12] == 0]) > 0: self.population = set_destination(self.population, self.destinations) self.population = check_at_destination( self.population, self.destinations, wander_factor=self.Config.wander_factor_dest, speed=self.Config.speed, ) if active_dests > 0 and len(self.population[self.population[:, 12] == 1]) > 0: # keep them at destination self.population = keep_at_destination( self.population, self.destinations, self.Config.wander_factor ) # out of bounds # define bounds arrays, excluding those who are marked as having a custom destination if len(self.population[:, 11] == 0) > 0: _xbounds = np.array( [[self.Config.xbounds[0] + 0.02, self.Config.xbounds[1] - 0.02]] * len(self.population[self.population[:, 11] == 0]) ) _ybounds = np.array( [[self.Config.ybounds[0] + 0.02, self.Config.ybounds[1] - 0.02]] * len(self.population[self.population[:, 11] == 0]) ) self.population[self.population[:, 11] == 0] = out_of_bounds( self.population[self.population[:, 11] == 0], _xbounds, _ybounds ) # set randoms if self.Config.lockdown: if len(self.pop_tracker.infectious) == 0: mx = 0 else: mx = np.max(self.pop_tracker.infectious) if len(self.population[self.population[:, 6] == 1]) >= len( self.population ) * self.Config.lockdown_percentage or mx >= ( len(self.population) * self.Config.lockdown_percentage ): # reduce speed of all members of society self.population[:, 5] = np.clip( self.population[:, 5], a_min=None, a_max=0.001 ) # set speeds of complying people to 0 self.population[:, 5][self.Config.lockdown_vector == 0] = 0 else: # update randoms self.population = update_randoms( self.population, self.Config.pop_size, self.Config.speed ) else: # update randoms self.population = update_randoms( self.population, self.Config.pop_size, self.Config.speed ) # for dead ones: set speed and heading to 0 self.population[:, 3:5][self.population[:, 6] == 3] = 0 # update positions self.population = update_positions(self.population) # find new infections self.population, self.destinations = infect( self.population, self.Config, self.frame, send_to_location=self.Config.self_isolate, location_bounds=self.Config.isolation_bounds, destinations=self.destinations, location_no=1, location_odds=self.Config.self_isolate_proportion, ) # recover and die self.population = recover_or_die(self.population, self.frame, self.Config) # send cured back to population if self isolation active # perhaps put in recover or die class # send cured back to population self.population[:, 11][self.population[:, 6] == 2] = 0 # update population statistics self.pop_tracker.update_counts(self.population) # visualise if self.Config.visualise: draw_tstep( self.Config, self.population, self.pop_tracker, self.frame, self.fig, self.spec, self.ax1, self.ax2, ) # report stuff to console sys.stdout.write("\r") sys.stdout.write( "%i: healthy: %i, infected: %i, immune: %i, in treatment: %i, \ dead: %i, of total: %i" % ( self.frame, self.pop_tracker.susceptible[-1], self.pop_tracker.infectious[-1], self.pop_tracker.recovered[-1], len(self.population[self.population[:, 10] == 1]), self.pop_tracker.fatalities[-1], self.Config.pop_size, ) ) # save popdata if required if self.Config.save_pop and (self.frame % self.Config.save_pop_freq) == 0: save_population(self.population, self.frame, self.Config.save_pop_folder) # run callback self.callback() # update frame self.frame += 1 def callback(self): """placeholder function that can be overwritten. By ovewriting this method any custom behaviour can be implemented. The method is called after every simulation timestep. """ if self.frame == 50: print("\ninfecting person") self.population[0][6] = 1 self.population[0][8] = 50 self.population[0][10] = 1 def run(self): """run simulation""" i = 0 while i < self.Config.simulation_steps: try: sim.tstep() except KeyboardInterrupt: print("\nCTRL-C caught, exiting") sys.exit(1) # check whether to end if no infecious persons remain. # check if self.frame is above some threshold to prevent early breaking when simulation # starts initially with no infections. if self.Config.endif_no_infections and self.frame >= 500: if ( len( self.population[ (self.population[:, 6] == 1) | (self.population[:, 6] == 4) ] ) == 0 ): i = self.Config.simulation_steps if self.Config.save_data: save_data(self.population, self.pop_tracker) # report outcomes print("\n-----stopping-----\n") print("total timesteps taken: %i" % self.frame) print("total dead: %i" % len(self.population[self.population[:, 6] == 3])) print("total recovered: %i" % len(self.population[self.population[:, 6] == 2])) print("total infected: %i" % len(self.population[self.population[:, 6] == 1])) print( "total infectious: %i" % len( self.population[ (self.population[:, 6] == 1) | (self.population[:, 6] == 4) ] ) ) print("total unaffected: %i" % len(self.population[self.population[:, 6] == 0]))
class Simulation(): #TODO: if lockdown or otherwise stopped: destination -1 means no motion def __init__(self, *args, **kwargs): #load default config data self.Config = Configuration(*args, **kwargs) self.frame = 0 #initialize default population self.population_init() self.pop_tracker = Population_trackers(self.Config.simulation_steps) #initalise destinations vector self.destinations = initialize_destination_matrix( self.Config.pop_size, 1) self.testing = False self.positive = [] self.last_positive = None self.last_number_of_tests = 0 def reinitialise(self): '''reset the simulation''' self.frame = 0 self.population_init() self.pop_tracker = Population_trackers(self.Config.simulation_steps) self.destinations = initialize_destination_matrix( self.Config.pop_size, 1) def population_init(self): '''(re-)initializes population''' self.population = initialize_population(self.Config, self.Config.xbounds, self.Config.ybounds) def tstep(self): ''' takes a time step in the simulation ''' if self.frame == 0 and self.Config.visualise: #initialize figure self.fig, self.spec, self.ax1, self.ax2 = build_fig(self.Config) #check destinations if active #define motion vectors if destinations active and not everybody is at destination active_dests = len(self.population[ self.population[:, 11] != 0]) # look op this only once if active_dests > 0 and len( self.population[self.population[:, 12] == 0]) > 0: self.population = set_destination(self.population, self.destinations) self.population = check_at_destination( self.population, self.destinations, wander_factor=self.Config.wander_factor_dest, speed=self.Config.speed) if active_dests > 0 and len( self.population[self.population[:, 12] == 1]) > 0: #keep them at destination self.population = keep_at_destination(self.population, self.destinations, self.Config.wander_factor) #out of bounds #define bounds arrays, excluding those who are marked as having a custom destination if len(self.population[:, 11] == 0) > 0: _xbounds = np.array([[ self.Config.xbounds[0] + 0.02, self.Config.xbounds[1] - 0.02 ]] * len(self.population[self.population[:, 11] == 0])) _ybounds = np.array([[ self.Config.ybounds[0] + 0.02, self.Config.ybounds[1] - 0.02 ]] * len(self.population[self.population[:, 11] == 0])) self.population[self.population[:, 11] == 0] = out_of_bounds( self.population[self.population[:, 11] == 0], _xbounds, _ybounds) #set randoms if self.Config.lockdown: if len(self.pop_tracker.infectious) == 0: mx = 0 else: mx = np.max(self.pop_tracker.infectious) if len(self.population[self.population[:,6] == 1]) >= len(self.population) * self.Config.lockdown_percentage or\ mx >= (len(self.population) * self.Config.lockdown_percentage): #reduce speed of all members of society self.population[:, 5] = np.clip(self.population[:, 5], a_min=None, a_max=0.001) #set speeds of complying people to 0 self.population[:, 5][self.Config.lockdown_vector == 0] = 0 else: #update randoms self.population = update_randoms(self.population, self.Config.pop_size, self.Config.speed) else: #update randoms self.population = update_randoms(self.population, self.Config.pop_size, self.Config.speed) #for dead ones: set speed and heading to 0 self.population[:, 3:5][self.population[:, 6] == 3] = 0 #update positions self.population = update_positions(self.population) #find new infections self.population = infect(self.population, self.Config, self.frame) #test if len( self.population[self.population[:, 6] == 1] ) / self.Config.pop_size >= self.Config.test_proportion_to_start: self.testing = True self.population[:, 17] = np.random.uniform(size=( self.Config.pop_size, )) < self.Config.proportion_wearing_masks if self.testing: self.last_positive, self.last_number_of_tests = test_population( self.population, self.Config, self.frame, self.positive, send_to_location=self.Config.self_isolate, location_bounds=self.Config.isolation_bounds, destinations=self.destinations, location_no=1, location_odds=self.Config.self_isolate_proportion) if self.last_positive is not None: self.positive += [self.last_positive] #recover and die self.population = recover_or_die(self.population, self.frame, self.Config) #send cured back to population if self isolation active #perhaps put in recover or die class #send cured back to population self.population[:, 11][self.population[:, 6] == 2] = 0 #update population statistics self.pop_tracker.update_counts(self.population, self.frame, self.last_positive, self.last_number_of_tests) #visualise if self.Config.visualise: draw_tstep(self.Config, self.population, self.pop_tracker, self.frame, self.fig, self.spec, self.ax1, self.ax2) if not self.Config.quiet: #report stuff to console sys.stdout.write('\r') sys.stdout.write( '%i: healthy: %i, infected: %i, immune: %i, in treatment: %i, \ dead: %i, of total: %i' % (self.frame, self.pop_tracker.susceptible[-1], self.pop_tracker.infectious[-1], self.pop_tracker.recovered[-1], len(self.population[self.population[:, 10] == 1]), self.pop_tracker.fatalities[-1], self.Config.pop_size)) #save popdata if required if self.Config.save_pop and (self.frame % self.Config.save_pop_freq) == 0: save_population(self.population, self.frame, self.Config.save_pop_folder) #run callback self.callback() #update frame self.frame += 1 def callback(self): '''placeholder function that can be overwritten. By ovewriting this method any custom behaviour can be implemented. The method is called after every simulation timestep. ''' if self.frame == 5: if not self.Config.quiet: print('\ninfecting patient zero') self.population[0:5, 6] = 1 self.population[0:5, 8] = 5 self.population[0:5, 10] = 1 self.population[0:5, 18] = 2 def run(self): '''run simulation''' i = 0 while i < self.Config.simulation_steps: try: self.tstep() except KeyboardInterrupt: print('\nCTRL-C caught, exiting') sys.exit(1) #check whether to end if no infecious persons remain. #check if self.frame is above some threshold to prevent early breaking when simulation #starts initially with no infections. if self.Config.endif_no_infections and self.frame >= 500: if len(self.population[(self.population[:, 6] == 1) | (self.population[:, 6] == 4)]) == 0: i = self.Config.simulation_steps if self.Config.save_data: save_data(self.population, self.pop_tracker) total_timesteps = self.frame total_dead = len(self.population[self.population[:, 6] == 3]) total_recovered = len(self.population[self.population[:, 6] == 2]) total_infected = len(self.population[self.population[:, 6] == 1]) total_infectious = len(self.population[(self.population[:, 6] == 1) | (self.population[:, 6] == 4)]) total_unaffected = len(self.population[self.population[:, 6] == 0]) if (self.Config.print_summary): #report outcomes print('\n-----stopping-----\n') print('total timesteps taken: %i' % total_timesteps) print('total dead: %i' % total_dead) print('total recovered: %i' % total_recovered) print('total infected: %i' % total_infected) print('total infectious: %i' % total_infectious) print('total unaffected: %i' % total_unaffected) self.pop_tracker.save(self.Config.run_id, 'results') def plot_sir(self, size=(6, 3), include_fatalities=False, title='S-I-R plot of simulation'): plot_sir(self.Config, self.pop_tracker, size, include_fatalities, title)
class Simulation(): #TODO: if lockdown or otherwise stopped: destination -1 means no motion def __init__(self, *args, **kwargs): #load default config data self.Config = Configuration() #set_style(self.Config) def population_init(self): '''(re-)initializes population''' self.population = initialize_population(self.Config, self.Config.mean_age, self.Config.max_age, self.Config.xbounds, self.Config.ybounds) def initialize_simulation(self): #initialize times self.frame = 0 self.time = 0 self.last_step_change = 0 self.above_act_thresh = False self.above_deact_thresh = False self.above_test_thresh = False #initialize default population self.population_init() #initalise destinations vector self.destinations = initialize_destination_matrix( self.Config.pop_size, 1) #initalise grid for tracking population positions self.grid_coords, self.ground_covered = initialize_ground_covered_matrix( self.Config.pop_size, self.Config.n_gridpoints, self.Config.xbounds, self.Config.ybounds) #initalise population tracker self.pop_tracker = Population_trackers(self.Config, self.grid_coords, self.ground_covered) def tstep(self): ''' takes a time step in the simulation ''' start = time.time() # start clock #======================================================================================# #check destinations if active #define motion vectors if destinations active and not everybody is at destination active_dests = len(self.population[ self.population[:, 11] != 0]) # look op this only once if active_dests > 0 and len( self.population[self.population[:, 12] == 0]) > 0: self.population = set_destination(self.population, self.destinations) self.population = check_at_destination( self.population, self.destinations, wander_factor=self.Config.wander_factor_dest) if active_dests > 0 and len( self.population[self.population[:, 12] == 1]) > 0: #keep them at destination self.population = keep_at_destination(self.population, self.Config.isolation_bounds) #======================================================================================# #gravity wells if self.Config.gravity_strength > 0: [self.population, self.last_step_change] = update_gravity_forces( self.population, self.time, self.last_step_change, self.Config.wander_step_size, self.Config.gravity_strength, self.Config.wander_step_duration) #======================================================================================# #activate social distancing above a certain infection threshold if not self.above_act_thresh and self.Config.social_distance_threshold_on > 0: # If not previously above infection threshold activate when threshold reached if self.Config.thresh_type == 'hospitalized': self.above_act_thresh = sum( self.population[:, 11] == 1) >= self.Config.social_distance_threshold_on elif self.Config.thresh_type == 'infected': self.above_act_thresh = sum( self.population[:, 6] == 1) >= self.Config.social_distance_threshold_on elif self.Config.social_distance_threshold_on == 0: self.above_act_thresh = True #deactivate social distancing after infection drops below threshold after using social distancing if self.above_act_thresh and not self.above_deact_thresh and self.Config.social_distance_threshold_off > 0: # If previously went above infection threshold deactivate when threshold reached self.above_deact_thresh = sum(self.population[:,6][self.population[:,11] == 0] == 1) <= \ self.Config.social_distance_threshold_off # activate social distancing at the onset of infection if not self.Config.SD_act_onset: act_social_distancing = self.above_act_thresh and not self.above_deact_thresh and sum( self.population[:, 6] == 1) > 0 # activate social distancing from start of simulation elif self.Config.SD_act_onset: act_social_distancing = self.above_act_thresh and not self.above_deact_thresh #activate social distancing only for compliant individuals if self.Config.social_distance_factor > 0 and act_social_distancing: self.population[(self.population[:,17] == 0) &\ (self.population[:,11] == 0)] = update_repulsive_forces(self.population[(self.population[:,17] == 0) &\ (self.population[:,11] == 0)], self.Config.social_distance_factor) #======================================================================================# #out of bounds #define bounds arrays, excluding those who are marked as having a custom destination if len(self.population[:, 11] == 0) > 0: buffer = 0.0 _xbounds = np.array([[ self.Config.xbounds[0] + buffer, self.Config.xbounds[1] - buffer ]] * len(self.population[self.population[:, 11] == 0])) _ybounds = np.array([[ self.Config.ybounds[0] + buffer, self.Config.ybounds[1] - buffer ]] * len(self.population[self.population[:, 11] == 0])) self.population[self.population[:, 11] == 0] = update_wall_forces( self.population[self.population[:, 11] == 0], _xbounds, _ybounds) #======================================================================================# #update velocities self.population[(self.population[:,11] == 0) |\ (self.population[:,12] == 1)] = update_velocities(self.population[(self.population[:,11] == 0) |\ (self.population[:,12] == 1)], self.Config.max_speed,self.Config.dt) #for dead ones: set velocity and social distancing to 0 for dead ones self.population[:, 3:5][self.population[:, 6] == 3] = 0 self.population[:, 17][self.population[:, 6] == 3] = 1 #update positions self.population = update_positions(self.population, self.Config.dt) #======================================================================================# #find new infections if not self.above_test_thresh and self.Config.testing_threshold_on > 0: # If not previously above infection threshold activate when threshold reached self.above_test_thresh = sum( self.population[:, 6] == 1) >= self.Config.testing_threshold_on # self.above_test_thresh = sum(self.population[:,6] == 1) >= self.Config.social_distance_threshold_on elif self.Config.testing_threshold_on == 0: self.above_test_thresh = True act_testing = self.above_test_thresh and sum( self.population[:, 6] == 1) > 0 self.population, self.destinations = infect( self.population, self.Config, self.frame, send_to_location=self.Config.self_isolate, location_bounds=self.Config.isolation_bounds, destinations=self.destinations, location_no=1, location_odds=self.Config.self_isolate_proportion, test_flag=act_testing) #recover and die self.population = recover_or_die(self.population, self.frame, self.Config) #======================================================================================# #send cured back to population if self isolation active #perhaps put in recover or die class #send cured back to population self.population[:, 11][self.population[:, 6] == 2] = 0 #======================================================================================# #update population statistics self.pop_tracker.update_counts(self.population, self.frame) #======================================================================================# #visualise if self.Config.visualise and ( self.frame % self.Config.visualise_every_n_frame) == 0: draw_tstep(self.Config, self.population, self.pop_tracker, self.frame, self.fig, self.spec, self.ax1, self.ax2, self.tight_bbox) #report stuff to console if (self.Config.verbose) and ((self.frame % self.Config.report_freq) == 0): end = time.time() time_elapsed = end - start # elapsed time sys.stdout.write('\r') sys.stdout.write( '%i: S: %i, I: %i, R: %i, in treatment: %i, F: %i, of total: %i, D: %.5f, GC: %.5f, time: %.5f' % (self.frame, self.pop_tracker.susceptible[-1], self.pop_tracker.infectious[-1], self.pop_tracker.recovered[-1], len(self.population[self.population[:, 10] == 1]), self.pop_tracker.fatalities[-1], self.Config.pop_size, self.pop_tracker.distance_travelled[-1], self.pop_tracker.mean_perentage_covered[-1], time_elapsed)) #save popdata if required if self.Config.save_pop and (self.frame % self.Config.save_pop_freq) == 0: save_population(self.population, self.frame, self.Config.save_pop_folder) #run callback self.callback() #======================================================================================# #update frame self.frame += 1 self.time += self.Config.dt def callback(self): '''placeholder function that can be overwritten. By overwriting this method any custom behavior can be implemented. The method is called after every simulation timestep. ''' if self.frame == 50: print('\ninfecting person (Patient Zero)') if self.Config.patient_Z_loc == 'random': self.population[0][6] = 1 self.population[0][8] = 50 self.population[0][10] = 1 elif self.Config.patient_Z_loc == 'central': center = np.zeros((self.Config.pop_size, 2)) center[:, 0] = (self.Config.xbounds[0] + self.Config.xbounds[1]) / 2 center[:, 1] = (self.Config.ybounds[0] + self.Config.ybounds[1]) / 2 to_center = (center - self.population[:, 1:3]) dist = np.linalg.norm(to_center, axis=1) # infect nearest individual to center self.population[np.argmin(dist)][6] = 1 self.population[np.argmin(dist)][8] = 50 self.population[np.argmin(dist)][10] = 1 def run(self): '''run simulation''' if self.Config.visualise: self.fig, self.spec, self.ax1, self.ax2, self.tight_bbox = build_fig( self.Config) i = 0 while i < self.Config.simulation_steps: try: self.tstep() except KeyboardInterrupt: print('\nCTRL-C caught, exiting') sys.exit(1) #check whether to end if no infectious persons remain. #check if self.frame is above some threshold to prevent early breaking when simulation #starts initially with no infections. if self.Config.endif_no_infections and self.frame >= 300: if len(self.population[(self.population[:, 6] == 1) | (self.population[:, 6] == 4)]) == 0: i = self.Config.simulation_steps else: i += 1 if self.Config.plot_last_tstep: self.fig_sir, self.spec_sir, self.ax1_sir = build_fig_SIRonly( self.Config) draw_SIRonly(self.Config, self.population, self.pop_tracker, self.frame, self.fig_sir, self.spec_sir, self.ax1_sir) if self.Config.save_data: save_data(self.population, self.pop_tracker) #report outcomes if self.Config.verbose: print('\n-----stopping-----\n') print('total timesteps taken: %i' % self.frame) print('total dead: %i' % len(self.population[self.population[:, 6] == 3])) print('total recovered: %i' % len(self.population[self.population[:, 6] == 2])) print('total infected: %i' % len(self.population[self.population[:, 6] == 1])) print('total infectious: %i' % len(self.population[(self.population[:, 6] == 1) | (self.population[:, 6] == 4)])) print('total unaffected: %i' % len(self.population[self.population[:, 6] == 0])) print('mean distance travelled: %f' % np.mean(self.pop_tracker.distance_travelled))
class Simulation(): def __init__(self): self.Config = Configuration() self.frame = 0 self.population = initialize_population(self.Config, self.Config.xbounds, self.Config.ybounds) self.pop_tracker = Population_trackers() self.peak_infections = 0 def tstep(self): if self.frame == 0: self.fig, self.spec, self.ax1, self.ax2 = build_fig(self.Config) xbounds = np.array( [[self.Config.xbounds[0] + 0.02, self.Config.xbounds[1] - 0.02]] * self.Config.pop_size) ybounds = np.array( [[self.Config.ybounds[0] + 0.02, self.Config.ybounds[1] - 0.02]] * self.Config.pop_size) self.population = out_of_bounds(self.population, xbounds, ybounds) if self.Config.is_lockdown and self.Config.lockdown == False: if len(self.population[(self.population[:, 6] == 1)] ) >= self.Config.lockdown_percentage * self.Config.pop_size: self.Config.lockdown = True print("\nLockdown Started") left_range = [ self.Config.xbounds[0] + 0.02, self.Config.xbounds[1] / 3 - 0.02 ] mid_range = [ self.Config.xbounds[1] / 3 + 0.02, 2 * self.Config.xbounds[1] / 3 - 0.02 ] right_range = [ 2 * self.Config.xbounds[1] / 3 + 0.02, self.Config.xbounds[1] - 0.02 ] bottom_range = [ self.Config.ybounds[0] + 0.02, self.Config.ybounds[1] / 2 - 0.02 ] top_range = [ self.Config.ybounds[1] / 2 + 0.02, self.Config.ybounds[1] - 0.02 ] left_condition = (self.population[:, 1] <= self.Config.xbounds[1] / 3) mid_condition = ( self.population[:, 1] > self.Config.xbounds[1] / 3) & ( self.population[:, 1] <= 2 * self.Config.xbounds[1] / 3) right_condition = (self.population[:, 1] > 2 * self.Config.xbounds[1] / 3) bottom_condition = (self.population[:, 2] <= self.Config.ybounds[1] / 2) top_condition = (self.population[:, 2] > self.Config.ybounds[1] / 2) if self.Config.lockdown: x_left_bottom = np.array( [left_range] * len(self.population[left_condition & bottom_condition])) y_left_bottom = np.array( [bottom_range] * len(self.population[left_condition & bottom_condition])) self.population[left_condition & bottom_condition] = out_of_bounds( self.population[left_condition & bottom_condition], x_left_bottom, y_left_bottom) x_left_top = np.array( [left_range] * len(self.population[left_condition & top_condition])) y_left_top = np.array( [top_range] * len(self.population[left_condition & top_condition])) self.population[left_condition & top_condition] = out_of_bounds( self.population[left_condition & top_condition], x_left_top, y_left_top) x_mid_bottom = np.array( [mid_range] * len(self.population[mid_condition & bottom_condition])) y_mid_bottom = np.array( [bottom_range] * len(self.population[mid_condition & bottom_condition])) self.population[mid_condition & bottom_condition] = out_of_bounds( self.population[mid_condition & bottom_condition], x_mid_bottom, y_mid_bottom) x_mid_top = np.array( [mid_range] * len(self.population[mid_condition & top_condition])) y_mid_top = np.array( [top_range] * len(self.population[mid_condition & top_condition])) self.population[mid_condition & top_condition] = out_of_bounds( self.population[mid_condition & top_condition], x_mid_top, y_mid_top) x_right_bottom = np.array( [right_range] * len(self.population[right_condition & bottom_condition])) y_right_bottom = np.array( [bottom_range] * len(self.population[right_condition & bottom_condition])) self.population[right_condition & bottom_condition] = out_of_bounds( self.population[right_condition & bottom_condition], x_right_bottom, y_right_bottom) x_right_top = np.array( [right_range] * len(self.population[right_condition & top_condition])) y_right_top = np.array( [top_range] * len(self.population[right_condition & top_condition])) self.population[right_condition & top_condition] = out_of_bounds( self.population[right_condition & top_condition], x_right_top, y_right_top) self.population = update_randoms(self.population, self.Config.pop_size, self.Config.speed) self.population[:, 5][self.population[:, 6] == 3] = 0 self.population = update_positions(self.population) self.population = infect(self.population, self.Config, self.frame) self.population = recover_or_die(self.population, self.frame, self.Config) self.pop_tracker.update_counts(self.population) draw_tstep(self.Config, self.population, self.pop_tracker, self.frame, self.fig, self.spec, self.ax1, self.ax2) self.peak_infections = max( self.peak_infections, len(self.population[self.population[:, 6] == 1])) if self.frame == 50: print('\ninfecting patient zero') self.population[0][6] = 1 self.frame += 1 def run(self): i = 0 while i < 10000: try: self.tstep() except KeyboardInterrupt: print('\nCTRL-C caught, exiting') sys.exit(1) if self.frame >= 500: if len(self.population[(self.population[:, 6] == 1) | (self.population[:, 6] == 4)]) == 0: break i = i + 1 print('\n-----stopping-----\n') print('total timesteps taken: %i' % self.frame) print('total dead: %i' % len(self.population[self.population[:, 6] == 3])) print('total recovered: %i' % len(self.population[self.population[:, 6] == 2])) print('total unaffected: %i' % len(self.population[self.population[:, 6] == 0])) print('peak infections: %i' % self.peak_infections)