def tick(self): v = get_parameters().get('risk_tolerance_mean') - 0.1 if v < 0.1: v = 0.1 logger().debug(f'Global risk_tolerance change to {v}') change_parameters(risk_tolerance_mean=v) self.model.reroll_human_properties()
def invite_friends_to_restaurant(self): shape = self.properties.risk_tolerance * get_parameters().get('typical_restaurant_event_size') event_size = np.random.gamma(shape, 1) logger().debug(f"Restaurant event size of {self} is {event_size}") accepted = [self] for human in self.tribe[TribeSelector.FRIEND]: if human != self and human.personal_decision(Dilemma.ACCEPT_FRIEND_INVITATION_TO_RESTAURANT): accepted.append(human) if len(accepted) >= event_size: break if len(accepted) == 1: return outdoor = flip_coin(linear_rescale(self.properties.risk_tolerance, 0, 0.5)) if flip_coin(linear_rescale(self.work_info.base_income, 0, 1 / 5)): restaurant_type = RestaurantType.FANCY else: restaurant_type = RestaurantType.FAST_FOOD event = self.work_district.get_available_restaurant(len(accepted), outdoor, restaurant_type) if event is not None and not outdoor: event = self.work_district.get_available_restaurant(len(accepted), True, restaurant_type) if event is None: return event.available -= len(accepted) for human in accepted: human.social_event = (self, event)
def spread_infection(self): if len(self.humans) > 0: logger().info(f"{self} is spreading infection amongst {len(self.humans)} humans") for h1 in self.humans: if h1.is_infected() and not h1.is_hospitalized() and not h1.is_isolated(): for h2 in self.humans: if h1 != h2: self.check_spreading(h1, h2)
def spread_infection(self): if len(self.humans) > 0: logger().info(f"{self} is spreading infection amongst {len(self.humans)} humans") for h1 in self.humans: for h2 in self.humans: if h1 != h2: if h1.social_event == h2.social_event or \ flip_coin(0.25 * self.get_parameter('allowed_restaurant_capacity')): self.check_spreading(h1, h2)
def spread_infection(self): if len(self.humans) > 0: logger().info( f"{self} is spreading infection amongst {len(self.humans)} humans" ) for h1 in self.humans: for h2 in self.humans: if h1 != h2: self.check_spreading(h1, h2)
def disease_evolution(self): # https://media.tghn.org/medialibrary/2020/06/ISARIC_Data_Platform_COVID-19_Report_8JUN20.pdf # https://www.ecdc.europa.eu/en/covid-19/latest-evidence if self.is_infected(): self.infection_days_count += 1 if self.disease_severity == DiseaseSeverity.ASYMPTOMATIC: if self.infection_days_count >= self.infection_incubation: logger().info(f"{self} evolved from ASYMPTOMATIC to LOW") self.disease_severity = DiseaseSeverity.LOW self.covid_model.global_count.asymptomatic_count -= 1 self.covid_model.global_count.symptomatic_count += 1 day = self.covid_model.global_count.day_count if day not in self.covid_model.global_count.new_symptomatic_count: self.covid_model.global_count.new_symptomatic_count[day] = 0 self.covid_model.global_count.new_symptomatic_count[day] += 1 elif self.disease_severity == DiseaseSeverity.LOW: if self.infection_days_count > self.infection_incubation + self.mild_duration: # By the end of this period, either the patient is already with antibodies at # a level sufficient to cure the disease or the symptoms will get worse and he/she # will require hospitalization if self.death_mark or flip_coin(self.moderate_severity_prob): # MODERATE cases requires hospitalization logger().info(f"{self} evolved from LOW to MODERATE") self.disease_severity = DiseaseSeverity.MODERATE self.covid_model.global_count.moderate_severity_count += 1 if not self.covid_model.reached_hospitalization_limit(): self.hospitalize() else: logger().info(f"{self} couldn't be hospitalized (hospitalization limit reached)") else: self.recover() elif self.disease_severity == DiseaseSeverity.MODERATE: if self.infection_days_count >= self.infection_incubation + self.mild_duration + self.hospitalization_duration: if self.death_mark or flip_coin(self.high_severity_prob): logger().info(f"{self} evolved from MODERATE to HIGH") self.disease_severity = DiseaseSeverity.HIGH self.covid_model.global_count.moderate_severity_count -= 1 self.covid_model.global_count.high_severity_count += 1 # If the disease evolves to HIGH and the person could not # be accommodated in a hospital, he/she will die. if not self.hospitalized or self.covid_model.reached_icu_limit(): self.die() else: shape = get_parameters().get('icu_period_duration_shape') scale = get_parameters().get('icu_period_duration_scale') self.icu_duration = np.random.gamma(shape, scale) self.has_been_icu = True logger().debug(f"ICU duration of {self} is {self.icu_duration}") else: self.recover() elif self.disease_severity == DiseaseSeverity.HIGH: if self.infection_days_count >= self.infection_incubation + self.mild_duration +\ self.hospitalization_duration + self.icu_duration: if self.death_mark: self.die() else: self.recover()
def get_available_restaurant(self, people_count, outdoor, restaurant_type): for location in self.locations: if isinstance(location, Restaurant) and \ location.restaurant_type == restaurant_type and \ location.is_outdoor == outdoor and \ ((location.capacity - location.available) + people_count) <= \ location.capacity * self.get_parameter('allowed_restaurant_capacity'): return location logger().info("No restaurant is available") return None
def die(self): logger().info(f"{self} died") self.covid_model.global_count.symptomatic_count -= 1 self.disease_severity = DiseaseSeverity.DEATH self.covid_model.global_count.high_severity_count -= 1 self.covid_model.global_count.infected_count -= 1 self.covid_model.global_count.death_count += 1 if self.hospitalized: self.leave_hospital() self.is_dead = True
def hospitalize(self): self.hospitalized = True self.has_been_hospitalized = True if self.hospital_district is not None: self.hospital = self.hospital_district.get_available_hospital() self.hospital.patients.append(self) self.covid_model.global_count.total_hospitalized += 1 logger().info(f"{self} is now hospitalized") shape = get_parameters().get('hospitalization_period_duration_shape') scale = get_parameters().get('hospitalization_period_duration_scale') self.hospitalization_duration = np.random.gamma(shape, scale) logger().debug(f"Hospital duration of {self} is {self.hospitalization_duration}")
def infect(self): # https://www.acpjournals.org/doi/10.7326/M20-0504 # https://media.tghn.org/medialibrary/2020/06/ISARIC_Data_Platform_COVID-19_Report_8JUN20.pdf # https://www.ecdc.europa.eu/en/covid-19/latest-evidence if not self.immune and not self.is_infected(): # Evolve disease severity based in this human's specific # attributes and update global counts logger().info(f"Infected {self}") self.covid_model.global_count.infected_count += 1 self.covid_model.global_count.non_infected_count -= 1 self.covid_model.global_count.susceptible_count -= 1 self.infection_status = InfectionStatus.INFECTED self.disease_severity = DiseaseSeverity.ASYMPTOMATIC self.covid_model.global_count.asymptomatic_count += 1 shape = get_parameters().get('latency_period_shape') scale = get_parameters().get('latency_period_scale') self.infection_latency = np.random.gamma(shape, scale) - self.early_symptom_detection logger().debug(f"Infection latency of {self} is {self.infection_latency}") if self.infection_latency < 1.0: self.infection_latency = 1.0 shape = get_parameters().get('incubation_period_shape') scale = get_parameters().get('incubation_period_scale') self.infection_incubation = self.infection_latency + np.random.gamma(shape, scale) logger().debug(f"Infection incubation of {self} is {self.infection_incubation}") shape = get_parameters().get('mild_period_duration_shape') scale = get_parameters().get('mild_period_duration_scale') self.mild_duration = np.random.gamma(shape, scale) + self.infection_incubation logger().debug(f"Mild duration of {self} is {self.mild_duration}")
def recover(self): logger().info(f"{self} is recovered after a disease of severity {self.disease_severity}") self.covid_model.global_count.recovered_count += 1 if self.disease_severity == DiseaseSeverity.MODERATE: self.covid_model.global_count.moderate_severity_count -= 1 elif self.disease_severity == DiseaseSeverity.HIGH: self.covid_model.global_count.high_severity_count -= 1 self.covid_model.global_count.infected_count -= 1 if self.hospitalized: self.leave_hospital() self.infection_status = InfectionStatus.RECOVERED self.disease_severity = DiseaseSeverity.ASYMPTOMATIC self.covid_model.global_count.symptomatic_count -= 1 self.covid_model.global_count.asymptomatic_count += 1 self.immune = True
def build_district(name, model, population_size, building_capacity, unit_capacity, occupacy_rate, contagion_probability): logger().info( f"Building district {name} contagion_probability = {contagion_probability}" ) district = District(name, model, '', name) building_count = math.ceil( math.ceil(population_size / unit_capacity) * (1 / occupacy_rate) / building_capacity) for i in range(building_count): building = HomogeneousBuilding(building_capacity, model, name, str(i)) for j in range(building_capacity): unit = BuildingUnit(unit_capacity, model, name, str(i) + '-' + str(j), contagion_probability=contagion_probability) building.locations.append(unit) district.locations.append(building) return district
def get_available_restaurant(self, people_count, outdoor, restaurant_type, favorites): #print ("favorites") #print (favorites) #for location in self.locations: for location in favorites: #print("location.strid") #print (location.strid) #print(isinstance(location, Restaurant)) #print(location.restaurant_type == restaurant_type)) #print(location.is_outdoor == outdoor) #print( ((location.capacity - location.available) + people_count) <= \ #location.capacity * self.get_parameter('allowed_restaurant_capacity') ) if isinstance(location, Restaurant) and \ location.restaurant_type == restaurant_type and \ location.is_outdoor == outdoor and \ ((location.capacity - location.available) + people_count) <= \ location.capacity * self.get_parameter('allowed_restaurant_capacity'): return location logger().info("No restaurant is available") return None
def infect(self, unit=None): # https://www.acpjournals.org/doi/10.7326/M20-0504 # https://media.tghn.org/medialibrary/2020/06/ISARIC_Data_Platform_COVID-19_Report_8JUN20.pdf # https://www.ecdc.europa.eu/en/covid-19/latest-evidence if not self.immune and not self.is_infected(): # Evolve disease severity based in this human's specific # attributes and update global counts logger().info(f"Infected {self}") # Commented because covid_model doesn't have `hrf` #vec = self.covid_model.hrf.feature_vector[self] #blob = self.covid_model.hrf.vector_to_blob[vec] #if blob is not None: # self.covid_model.actual_infections["blob"].append(blob) # self.covid_model.actual_infections["strid"].append(self.strid) # self.covid_model.actual_infections["unit"].append(unit.strid if unit is not None else None) # self.covid_model.actual_infections["day"].append(self.covid_model.global_count.day_count) self.covid_model.global_count.infected_count += 1 self.covid_model.global_count.non_infected_count -= 1 self.covid_model.global_count.susceptible_count -= 1 self.infection_status = InfectionStatus.INFECTED self.disease_severity = DiseaseSeverity.ASYMPTOMATIC self.covid_model.global_count.asymptomatic_count += 1 shape = get_parameters().get('latency_period_shape') scale = get_parameters().get('latency_period_scale') self.infection_latency = np.random.gamma(shape, scale) - self.early_symptom_detection if self.infection_latency < 1.0: self.infection_latency = 1.0 logger().debug(f"Infection latency of {self} is {self.infection_latency}") shape = get_parameters().get('incubation_period_shape') scale = get_parameters().get('incubation_period_scale') self.infection_incubation = np.random.gamma(shape, scale) logger().debug(f"Infection incubation of {self} is {self.infection_incubation}") shape = get_parameters().get('mild_period_duration_shape') scale = get_parameters().get('mild_period_duration_scale') self.mild_duration = np.random.gamma(shape, scale) logger().debug(f"Mild duration of {self} is {self.mild_duration}")
def spread_infection(self): if len(self.patients) > 0: logger().info(f"{self} is spreading infection patients -> workers") print(f"{self} is spreading infection patients -> workers") for worker in humans: for patient in patients: if not flip_coin(r): continue logger().debug( f"Check to see if patient {patient} can infect worker {worker} in {self}" ) print( f"Check to see if patient {patient} can infect worker {worker} in {self}" ) if not worker.is_infected(): logger().debug( f"contagion_probability = {self.get_parameter('contagion_probability')}" ) if flip_coin( self.get_parameter('contagion_probability')): if worker.strid not in self.covid_model.global_count.infection_info: self.covid_model.global_count.infection_info[ worker.strid] = self logger().debug( f"Infection succeeded - {patient} has infected {worker} in {self} with contagion " f"probability {self.get_parameter('contagion_probability')}" ) print( f"Infection succeeded - {patient} has infected {worker} in {self} with contagion " f"probability {self.get_parameter('contagion_probability')}" ) patient.count_infected_humans += 1 worker.infect(patient) else: logger().debug( f"Infection failed - {self} didn't pass contagion_probability check with contagion " f"probability {self.get_parameter('contagion_probability')}" ) print( f"Infection failed - {self} didn't pass contagion_probability check with contagion " f"probability {self.get_parameter('contagion_probability')}" ) else: logger().debug( f"Infection failed - infectee {worker} is already infected" ) print( f"Infection failed - infectee {worker} is already infected" ) super().spread_infection() # amongst workers only
def personal_decision(self, dilemma): if dilemma == Dilemma.GO_TO_WORK_ON_LOCKDOWN: if self.work_info.work_class == WorkClasses.RETAIL: pd = flip_coin(self.properties.risk_tolerance) hd = self.dilemma_history.herding_decision(self, dilemma, TribeSelector.FRIEND, get_parameters().get('min_behaviors_to_copy')) answer = self._standard_decision(pd, hd) logger().debug(f'{self}({self.unique_id}) had risk tolerance of {self.properties.risk_tolerance} in decision to work retail, making a personal decision of {pd} but a herding decision of {hd}') else: answer = False if answer: logger().info(f"{self} decided to get out to work on lockdown") elif dilemma == Dilemma.INVITE_FRIENDS_TO_RESTAURANT: if self.social_event is not None or self.is_symptomatic(): # don't update dilemma_history since it's a compulsory decision return False rt = self.properties.risk_tolerance if SocialPolicy.SOCIAL_DISTANCING in get_parameters().get('social_policies'): rt = rt * rt k = 3 # TODO parameter d = self.covid_model.global_count.infected_count / self.covid_model.global_count.total_population rt = rt * math.exp(-k * d) pd = flip_coin(rt) hd = self.dilemma_history.herding_decision(self,dilemma, TribeSelector.FRIEND, get_parameters().get('min_behaviors_to_copy')) answer = self._standard_decision(pd, hd) logger().debug(f'{self}({self.unique_id}) had risk tolerance of {rt} in decision to invite, making a personal decision of {pd} but a herding decision of {hd} and answer of {answer}') if answer: logger().info(f"{self} decided to invite friends to a restaurant") elif dilemma == Dilemma.ACCEPT_FRIEND_INVITATION_TO_RESTAURANT: if self.social_event is not None or self.is_symptomatic(): # don't update dilemma_history since it's a compulsory decision return False rt = self.properties.risk_tolerance if SocialPolicy.SOCIAL_DISTANCING in get_parameters().get('social_policies'): rt = rt * rt k = 3 # TODO parameter d = self.covid_model.global_count.infected_count / self.covid_model.global_count.total_population rt = rt * math.exp(-k * d) pd = flip_coin(rt) hd = self.dilemma_history.herding_decision(self,dilemma, TribeSelector.FRIEND, get_parameters().get('min_behaviors_to_copy')) answer = self._standard_decision(pd, hd) logger().debug(f'{self}({self.unique_id}) had risk tolerance of {rt} in decision to accept invite, making a personal decision of {pd} but a herding decision of {hd} and answer of {answer}') if answer: logger().info(f"{self} decided to accept an invitation to go to a restaurant") else: assert False for tribe in TribeSelector: self.dilemma_history.history[dilemma][tribe].append(answer) return answer
sc7_model.add_listener( RemovePolicy(sc7_model, SocialPolicy.LOCKDOWN_OFFICE, 90)) sc7_model.add_listener( RemovePolicy(sc7_model, SocialPolicy.LOCKDOWN_ELEMENTARY_SCHOOL, 90)) sc7_model.add_listener( RemovePolicy(sc7_model, SocialPolicy.LOCKDOWN_MIDDLE_SCHOOL, 90)) sc7_model.add_listener( RemovePolicy(sc7_model, SocialPolicy.LOCKDOWN_HIGH_SCHOOL, 120)) scenario[sc]['parameters'] = sc7_parameters scenario[sc]['model'] = sc7_model ################################################################################ # Simulation of all scenarios for sc in scenario: # for sc in [1]: # print("--------------------------------------------------------------------------------") print(f"Running scenario {sc}") set_parameters(scenario[sc]['parameters']) model = scenario[sc]['model'] debug = DebugUtils(model) logger().model = model model.reset_randomizer(seed) statistics = BasicStatistics(model) model.add_listener(statistics) for i in range(simulation_cycles): model.step() statistics.export_chart("scenario" + str(sc) + ".png") statistics.export_csv("scenario" + str(sc) + ".csv") # debug.print_infection_status()
def check_spreading(self, h1, h2): if h1.is_infected(): logger().debug(f"Check to see if {h1} can infect {h2} in {self}") if h1.is_contagious() and not h2.is_infected(): if flip_coin(self.get_parameter('contagion_probability')): me = self.get_parameter('mask_efficacy') if not h1.is_wearing_mask() or (h1.is_wearing_mask() and not flip_coin(me)): if h2.strid not in self.covid_model.global_count.infection_info: self.covid_model.global_count.infection_info[ h2.strid] = self logger().debug( f"Infection succeeded - {h1} has infected {h2} in {self} with contagion " f"probability {self.get_parameter('contagion_probability')}" ) h2.infect() else: if h1.is_wearing_mask(): logger().debug( f"Infection failed - infector {h1} wearing mask" ) if h2.is_wearing_mask(): logger().debug( f"Infection failed - infectee {h2} wearing mask" ) else: logger().debug( f"Infection failed - {self} didn't pass contagion_probability check with contagion " f"probability {self.get_parameter('contagion_probability')}" ) else: if not h1.is_contagious(): logger().debug( f"Infection failed - infector {h1} is not contagious") if h2.is_infected(): logger().debug( f"Infection failed - infectee {h2} is already infected" )