diff --git a/config_files/immunization_history.toml b/config_files/immunization_history.toml new file mode 100644 index 0000000..00574b4 --- /dev/null +++ b/config_files/immunization_history.toml @@ -0,0 +1,19 @@ +[vaccine_max_efficacy] +Pfizer = 0.913 +Moderna = 0.941 +AZ = 0.76 + +[vaccine_immunity_buildup_days] +Pfizer = 14 +Moderna = 14 +AZ = 14 + +[vaccine_efficacy_min_day] +Pfizer = 180 +Moderna = 180 +AZ = 180 + +[long_term_vaccine_eff] +Pfizer = 0.7 +Moderna = 0.7 +AZ = 0.7 diff --git a/config_files/main.toml b/config_files/main.toml index 74140dc..6d3b47f 100644 --- a/config_files/main.toml +++ b/config_files/main.toml @@ -1,5 +1,6 @@ [simulation_data] disease_config_file = "disease.toml" +immunization_history_config_file = "immunization_history.toml" nDays = 50 nPop = 10000 vis_default_virus_type = "alpha" @@ -7,14 +8,22 @@ vis_default_severity = "MILD" student_default_virus_type = "alpha" num_students = 2000 max_num_res_students = 500 -v0 = 0 N_VIS_OPTION = [0, 1, 2, 3] N_VIS_PROB = [0.7, 0.17, 0.08, 0.05] vis_age_upper = 60 vis_age_lower = 16 -inf_students_upper = 20 -inf_students_lower = 2 -num_vaccinations = 0 +inf_students_upper = 2 +inf_students_lower = 0 +num_vaccinations = 100 + [simulation_data.v0_parameters] + v0 = 1000 + v0_interval_start_day = -30 + v0_interval_end_day = 0 + + [simulation_data.vaccine_type] + Pfizer = 0.6 + Moderna = 0.35 + AZ = 0.05 [simulation_data.variants] general = 10 @@ -35,11 +44,6 @@ num_vaccinations = 0 FOOD = 0.1 RES = 1 - [simulation_data.vaccine_type] - Pfizer = 0.6 - Moderna = 0.35 - AZ = 0.05 - [policy_data] initial_mask_mandate = true mask_trigger = 0.3 diff --git a/cv19/immunization_history.py b/cv19/immunization_history.py new file mode 100644 index 0000000..33910e3 --- /dev/null +++ b/cv19/immunization_history.py @@ -0,0 +1,86 @@ +class ImmunizationHistory: + """ + A class designed to keep track of a Person's vaccination history and vaccination parameters. + + Holds the parameters from immunization_history.toml corresponding to the Person's vaccine type, + as well as the attributes listed below. + + Attributes + ---------- + vaccinated: :obj:`bool` + A variable indicating whether or not the person is vaccinated. + list_of_vaccination_dates: :obj:`list` of int + A list of a Person's vaccination dates (corresponding to day in simulation). + """ + + def __init__(self, vaccine_type=None, vaccine_max_efficacy=None, vaccine_immunity_buildup_days=None, long_term_vaccine_eff=None, vaccine_efficacy_min_day=None): + """ + The constructor for the Immunization History class. + """ + + self.vaccinated = False + self.list_of_vaccination_dates = list() + self.vaccine_type = vaccine_type + self.vaccine_max_efficacy = vaccine_max_efficacy + self.vaccine_immunity_buildup_days = vaccine_immunity_buildup_days + self.long_term_vaccine_eff = long_term_vaccine_eff + self.vaccine_efficacy_min_day = vaccine_efficacy_min_day + + def is_vaccinated(self): + """Method to retrieve if a person is vaccinated. Returns True if vaccinated, False if not. + + Returns + ------- + self.vaccinated: :obj:`bool` + """ + return self.vaccinated + + def set_vaccinated(self, day): + """Method to set a person to be vaccinated. + + Parameters + ---------- + day: int + The day in the simulation when a person is vaccinated. + + Returns + ------- + self.vaccinated: :obj:`bool` + """ + + self.vaccinated_day = day + self.list_of_vaccination_dates.append(self.vaccinated_day) + self.vaccinated = True + + def vaccine_efficacy(self, day): + """Method to determine what the efficiency of the vaccine based on the type of vaccine administered, + and other immunization history parameters. + + Parameters + ---------- + day: int + The day in the simulation when a person is vaccinated. + + Returns + ------- + self.current_vaccine_eff: :obj:`float` + """ + + if self.vaccinated: + + days_since_vaccination = day - self.list_of_vaccination_dates[-1] + if days_since_vaccination == 0: + self.current_vaccine_eff = 0 + elif days_since_vaccination <= self.vaccine_immunity_buildup_days: # linear increase of immunity + self.current_vaccine_eff = ((self.vaccine_max_efficacy) / (self.vaccine_immunity_buildup_days)) * days_since_vaccination + elif (days_since_vaccination > self.vaccine_immunity_buildup_days) and (days_since_vaccination < self.vaccine_efficacy_min_day): # calculate linearly decreasing immunity + self.current_vaccine_eff = (-(self.vaccine_max_efficacy - self.long_term_vaccine_eff) / (self.vaccine_efficacy_min_day - self.vaccine_immunity_buildup_days)) # slope calculation + self.current_vaccine_eff *= days_since_vaccination # scale by days since vaccination + self.current_vaccine_eff += self.vaccine_max_efficacy # add initial value (which is the max efficacy) + else: # plateau in immunity after and including min_day + self.current_vaccine_eff = self.long_term_vaccine_eff + + return self.current_vaccine_eff + + else: + return 0 diff --git a/cv19/interaction_sites.py b/cv19/interaction_sites.py index 4fd6b7c..4f99f5d 100644 --- a/cv19/interaction_sites.py +++ b/cv19/interaction_sites.py @@ -466,7 +466,7 @@ def site_interaction(self, will_go_array, day, personal, grade_code): if person_1_infected != person_2_infected: # Have an interaction between those people - did_infect = self.interact(p1_obj, p2_obj) + did_infect = self.interact(p1_obj, p2_obj, day=day) if did_infect: if person_1_infected: new_infections[person_2_index] = True @@ -525,15 +525,17 @@ def calc_interactions(self, site_day_pop): return number_of_interactions - def interact(self, person_1, person_2): - """Method that models the interaction between two people. - + def interact(self, person_1, person_2, day): + """ + Method that models the interaction between two people. Parameters ---------- person_1 : :obj:`cv19.person.Person` First person in the two-way interaction. person_2 : :obj:`cv19.person.Person` Second person in the two-way interaction. + day : int + The day value that this function is being called on in the encompassing simulation class. Used to determine vaccine efficacy of interacting individuals. Returns ------- @@ -566,11 +568,8 @@ def interact(self, person_1, person_2): if p2_mask: spread_prob *= (1 - P2_OUTWARD_EFF) - p1_vaccinated1 = person_1.is_vaccinated() - p2_vaccinated1 = person_2.is_vaccinated() - - p1_vaccine_eff = person_1.vaccine_type_efficiency() if p1_vaccinated1 else 0 - p2_vaccine_eff = person_2.vaccine_type_efficiency() if p2_vaccinated1 else 0 + p1_vaccine_eff = person_1.immunization_history_obj.vaccine_efficacy(day) + p2_vaccine_eff = person_2.immunization_history_obj.vaccine_efficacy(day) spread_prob *= ((1 - p1_vaccine_eff) * (1 - p2_vaccine_eff)) @@ -615,8 +614,7 @@ def house_interact(self, day): virus_name = self.variant_code_map[virus_id] infection_chance = self.base_infection_spread_prob[virus_name] * self.house_infection_spread_factor - person_vaccinated = housemembers[person].is_vaccinated() - person_vaccine_eff = housemembers[person].vaccine_type_efficiency() if person_vaccinated else 0 + person_vaccine_eff = housemembers[person].immunization_history_obj.vaccine_efficacy(day) infection_chance *= (1 - person_vaccine_eff) caught_infection = random() < infection_chance @@ -667,8 +665,7 @@ def student_house_interact(self, day): virus_name = self.variant_code_map[virus_id] infection_chance = self.base_infection_spread_prob[virus_name] * self.house_infection_spread_factor - person_vaccinated = housemembers[person].is_vaccinated() - person_vaccine_eff = housemembers[person].vaccine_type_efficiency() if person_vaccinated else 0 + person_vaccine_eff = housemembers[person].immunization_history_obj.vaccine_efficacy(day) infection_chance *= (1 - person_vaccine_eff) caught_infection = random() < infection_chance if caught_infection: diff --git a/cv19/person.py b/cv19/person.py index b60afc5..8f185b3 100644 --- a/cv19/person.py +++ b/cv19/person.py @@ -1,6 +1,7 @@ from random import random import numpy as np +from .immunization_history import ImmunizationHistory class Person(object): @@ -17,11 +18,11 @@ class Person(object): """ - def __init__(self, index, sim_obj, infected=False, recovered=False, dead=False, hospitalized=False, ICU=False, - quarantined=False, quarantined_day=None, infected_day=None, recovered_day=None, death_day=None, - others_infected=None, cure_days=None, recent_infections=None, vaccinated=False, vaccine_type=None, + def __init__(self, index, sim_obj, infected=False, recovered=False, dead=False, hospitalized=False, ICU=False, quarantined=False, + quarantined_day=None, infected_day=None, recovered_day=None, death_day=None, others_infected=None, + cure_days=None, recent_infections=None, vaccine_info=None, age=None, job=None, house_index=0, isolation_tendencies=None, case_severity=None, mask_type=None, - has_mask=True, virus_type=None, days_until_symptoms=None): + has_mask=True, virus_type=None): """Method to load in attributes from the provided simulation class object. Sets all objects in the "person_data" dictionary key as self attributes of the @@ -45,10 +46,8 @@ def __init__(self, index, sim_obj, infected=False, recovered=False, dead=False, Determines if person is quarentined or not, defaults False. quarantined_day : int The day a person is put into quarantine, defaults None. - vaccinated : bool - Determines if a person is vaccinated or not, defaults to True. - vaccine_type : string - Determines type of vaccine received by person, defaults to None. + vaccine_info : dict + Dictionary containing vaccine parameters, defaults None. infected_day : int The day a person is infected, defaults None. recovered_day : int @@ -90,8 +89,7 @@ def __init__(self, index, sim_obj, infected=False, recovered=False, dead=False, self.others_infected = [] if others_infected is None else others_infected self.cure_days = cure_days self.recent_infections = recent_infections - self.vaccinated = vaccinated - self.vaccine_type = vaccine_type + self.immunization_history_obj = ImmunizationHistory(**vaccine_info) if vaccine_info is not None else ImmunizationHistory() self.index = index self.age = age self.job = job @@ -100,7 +98,7 @@ def __init__(self, index, sim_obj, infected=False, recovered=False, dead=False, self.case_severity = case_severity self.mask_type = mask_type self.show_symptoms = False - self.days_until_symptoms = days_until_symptoms + self.days_until_symptoms = None self.knows_infected = False self.will_get_symptoms = False self.has_mask = has_mask @@ -769,45 +767,3 @@ def update_lockdown_days(self, lockdown_level): elif self.days_in_lockdown != 0: self.days_in_lockdown -= 1 return self.days_in_lockdown - - def is_vaccinated(self): - """Method to retrieve if a person is vaccinated. Returns True if vaccinated, False if not. - - Returns - ------- - self.vaccinated: :obj:`bool` - """ - return self.vaccinated - - def set_vaccinated(self, day): - """Method to set a person to be vaccinated. - - Parameters - ---------- - day: int - The day in the simulation when a person is vaccinated. - - Returns - ------- - self.vaccinated: :obj:`bool` - """ - # Make sure person is not already vaccinated. - if not self.is_vaccinated(): - self.vaccinated_day = day - self.vaccinated = True - - def vaccine_type_efficiency(self): - """Method to determines what the efficiency of the vaccine based on the type of vaccine administered. - - Returns - ------- - self.sim_obj.vaccine_eff[self.vaccine_type]: :obj:`float` - """ - if self.vaccinated: - try: - return self.sim_obj.vaccine_eff[self.vaccine_type] - except KeyError as e: - raise ValueError((f"'{self.vaccine_type}' is not a valid vaccine type " - "and has no associated efficiency.")) from e - else: - return 1 diff --git a/cv19/population.py b/cv19/population.py index 3057056..64e3ffe 100644 --- a/cv19/population.py +++ b/cv19/population.py @@ -37,14 +37,10 @@ def __init__(self, sim_obj): self.set_demographic_parameters() self.nPop = sim_obj.nPop # total population - self.current_num_vis = 0 # initial number of visistors - self.v0 = sim_obj.v0 # initial vaccinated - self.nPop_w_vis = self.nPop + max(sim_obj.N_VIS_OPTION) # max agents in the sim at a time - # Student parameter self.nStudents = sim_obj.num_students # full capacity ~ 24k students - self.population = [NULL_ID] * self.nPop_w_vis # The list to hold all person objects + self.population = [0] * self.nPop # The list of all people self.household = [0] * self.nPop # list of non-student houses (list that contains all lists of the people in the house) self.students = [0] * self.nStudents # The list of only students self.stud_houses = [0] * self.nStudents # list of student houses @@ -101,7 +97,9 @@ def __init__(self, sim_obj): mask_type_arr = np.random.choice(a=self.mask_options, p=self.mask_weights, size=self.nPop) has_mask_arr = np.random.uniform(size=self.nPop) < self.prob_has_mask + # Prepare vaccination date array for population initialization vaccine_type_arr = np.random.choice(a=self.vaccine_options, p=self.vaccine_weights, size=self.nPop) + vaccination_date_arr = self.set_v0_parameters(sim_obj) # Initialize the house index and size for the loop houseIndex = 0 @@ -113,6 +111,14 @@ def __init__(self, sim_obj): houseIndex += 1 houseSize = self.household[houseIndex] + vaccine_type = vaccine_type_arr[i] # vaccine type from vaccine type arr + individual_vaccine_info = {"vaccine_type": vaccine_type, + "vaccine_max_efficacy": self.vaccine_parameters["vaccine_max_efficacy"][vaccine_type], + "vaccine_immunity_buildup_days": self.vaccine_parameters["vaccine_immunity_buildup_days"][vaccine_type], + "long_term_vaccine_eff": self.vaccine_parameters["long_term_vaccine_eff"][vaccine_type], + "vaccine_efficacy_min_day": self.vaccine_parameters["vaccine_efficacy_min_day"][vaccine_type]} + # set up dictionary of vaccination parameters for immunization history object + # MAKE A PERSON newPerson = Person(index=i, sim_obj=sim_obj, @@ -132,8 +138,7 @@ def __init__(self, sim_obj): age=age_arr[i], job=job_arr[i], house_index=houseIndex, - vaccinated=False, - vaccine_type=vaccine_type_arr[i], + vaccine_info=individual_vaccine_info, isolation_tendencies=isolation_tend_arr[i], case_severity=case_severity_arr[i], mask_type=mask_type_arr[i], @@ -178,6 +183,13 @@ def __init__(self, sim_obj): studHouseIndex += 1 studHouseSize = self.stud_houses[studHouseIndex] + vaccine_type = vaccine_type_arr[i] + individual_vaccine_info = {"vaccine_type": vaccine_type, + "vaccine_max_efficacy": self.vaccine_parameters["vaccine_max_efficacy"][vaccine_type], + "vaccine_immunity_buildup_days": self.vaccine_parameters["vaccine_immunity_buildup_days"][vaccine_type], + "long_term_vaccine_eff": self.vaccine_parameters["long_term_vaccine_eff"][vaccine_type], + "vaccine_efficacy_min_day": self.vaccine_parameters["vaccine_efficacy_min_day"][vaccine_type]} # set up immunization history object parameters as dictionary + newStudent = Person(index=i, sim_obj=sim_obj, infected=False, @@ -196,8 +208,7 @@ def __init__(self, sim_obj): age=student_age[i - self.nPop + self.nStudents], # adjust for index inconsistency job='Student', house_index=studHouseIndex, - vaccinated=False, - vaccine_type=vaccine_type_arr[i], + vaccine_info=individual_vaccine_info, isolation_tendencies=isolation_tend_arr[i], case_severity=student_case_severity_arr[i - self.nPop + self.nStudents], # adjust for index inconsistency mask_type=mask_type_arr[i], @@ -233,7 +244,7 @@ def __init__(self, sim_obj): where = np.where(self.house_stud_i[housei] == -1)[0][0] self.house_stud_i[housei][where] = i - # Create person status arrays (visitors not included here) + # Create person status arrays # A non-negative index indicates that they are the property, # NULL_ID (-1) indicates that they are /not/ the property. self.susceptible = np.array(range(self.nPop), dtype=int) # list of all susceptible individuals @@ -268,9 +279,10 @@ def __init__(self, sim_obj): # Vaccinate first v0 people v_indices = sample(range(self.nPop), self.v0) - for i in v_indices: - self.population[i].set_vaccinated(day=0) - self.vaccinated[i] = i + for i, v in enumerate(v_indices): + # set vaccinated date based on vaccination_date_arr + self.population[v].immunization_history_obj.set_vaccinated(day=int(vaccination_date_arr[i])) + self.vaccinated[v] = v def load_attributes_from_sim_obj(self, sim_obj): """Method to load in attributes from the provided simulation class object. @@ -293,9 +305,6 @@ def load_attributes_from_sim_obj(self, sim_obj): self.variant_codes = sim_obj.variant_codes - # get max sickness lengths - self.max_infectious = sim_obj.disease_parameters["max_infectious"] - # case severity from disease params self.severity_options = constants.SEVERITY_OPTIONS @@ -307,9 +316,10 @@ def load_attributes_from_sim_obj(self, sim_obj): self.mask_weights = np.array([self.mask_type[key] for key in constants.MASK_OPTIONS]) self.mask_options = constants.MASK_OPTIONS - # format vaccine weights + # format vaccine parameters self.vaccine_weights = np.array([self.sim_obj.vaccine_type[key] for key in constants.VACCINE_OPTIONS]) self.vaccine_options = constants.VACCINE_OPTIONS + self.vaccine_parameters = sim_obj.immunization_history_parameters def set_demographic_parameters(self): """Method to open disease parameters from the TOML file. @@ -346,8 +356,36 @@ def set_demographic_parameters(self): # Cast this so they can be used as ints self.house_options = [int(x) for x in constants.HOUSE_OPTIONS] + def set_v0_parameters(self, sim_obj): + """Method to set up initially vaccinated population array. + + Parameters + ---------- + sim_obj : :obj:`cv19.simulation.simulation` + The encompassing simulation object hosting the simulation. + + Returns + ------- + vaccination_date_arr: :obj:`np.array` of :obj:`int` + """ + + self.v0 = sim_obj.v0_parameters["v0"] # initial vaccinated + v0_lower = sim_obj.v0_parameters["v0_interval_start_day"] # lower bound on v0 range + v0_upper = sim_obj.v0_parameters["v0_interval_end_day"] # upper bound on v0 range + vaccination_date_arr = np.zeros(self.v0) # initialize v0 dates array + + if v0_lower <= 0 and v0_upper <= 0: # v0 bounds cannot be greater than 0 + + if v0_lower < v0_upper: # v0_lower should be smaller (more negative) than v0_upper + vaccination_date_arr = np.random.uniform(low=v0_lower, high=v0_upper, size=self.v0) + + else: # they are equal to each other: everyone is vaccinated on the same day + vaccination_date_arr = np.ones(self.v0) * v0_lower + + return vaccination_date_arr + def get_population_size(self): - """Method to return population size. Does not include visitors. + """Method to return population size. Returns ------- @@ -356,17 +394,13 @@ def get_population_size(self): return self.nPop def get_population(self): - """Method to retrieve a list of the population, including visitors. Makes sure not to grab - empty visitor bins in the population array. + """Method to retrieve a list of the population. Returns ------- self.has_mask: :obj:`np.array` of :obj:`int` """ - - pop_list = self.population[:self.nPop + self.current_num_vis] - - return pop_list + return self.population def get_student_indices(self): """Method to retrieve a list of the student indices. @@ -386,65 +420,15 @@ def get_student_pop_size(self): """ return self.nStudents - def add_visitors(self, day): - """Method to add visitors to the simulation. - - Parameters - ---------- - day : int - The day value that this function is being called on in the encompassing simulation class. - - """ - - self.current_num_vis = np.random.choice(a=self.sim_obj.N_VIS_OPTION, p=self.sim_obj.N_VIS_PROB) - - visitors_ind = [x for x in range(self.nPop, self.nPop + self.current_num_vis)] - vis_age = np.random.choice(a=self.age_options, p=self.age_weights, size=self.current_num_vis) - vis_iso_tend = np.random.choice(a=self.isolation_options, p=self.isolation_weights, size=self.current_num_vis) - vis_has_mask = np.random.uniform(size=self.current_num_vis) < self.prob_has_mask - vis_mask_type = np.random.choice(a=self.mask_options, p=self.mask_weights, size=self.current_num_vis) - vis_cure_days = np.random.choice(self.max_infectious[self.sim_obj.vis_default_severity], size=self.current_num_vis) - - for i in range(0, self.current_num_vis): - visitor = Person(index=visitors_ind[i], - sim_obj=self.sim_obj, - infected=True, - recovered=False, - dead=False, - hospitalized=False, - ICU=False, - quarantined=False, - quarantined_day=None, - infected_day=day, - recovered_day=None, - death_day=None, - others_infected=None, - cure_days=vis_cure_days[i], - recent_infections=None, - vaccinated=False, - age=vis_age[i], - job="Visitor", - house_index=None, - isolation_tendencies=vis_iso_tend[i], - case_severity=self.sim_obj.vis_default_severity, - has_mask=vis_has_mask[i], - virus_type=self.sim_obj.vis_default_virus_type, - mask_type=vis_mask_type[i], - days_until_symptoms=0) - - self.population[self.nPop + i] = visitor - - def remove_visitors(self): + def remove_visitors(self, indices): """Method to remove visitors from the simulation. """ + for i in sorted(indices, reverse=True): + self.population.pop(i) - for i in range(self.nPop, self.nPop_w_vis): - self.population[i] = NULL_ID - self.current_num_vis = 0 - - if len(self.get_population()) != self.nPop: + if len(self.population) != self.nPop: raise RuntimeError(("Population is not expected length after removing visitors " - f"(expected {self.nPop}, is {len(self.get_population())}).")) + f"(expected {self.nPop}, is {len(self.population)}).")) def get_susceptible(self): """Method to retrieve indicies of people suseptible. @@ -838,7 +822,7 @@ def update_uninfected_symptomatics(self): """Method that causes a random sample of people to develop cold like symptoms. """ - for person in self.get_population(): + for person in self.population: person.update_uninfected_symptomatic() def update_infected_symptomatics(self, day): @@ -951,7 +935,7 @@ def update_vaccinated(self, day): """ non_vaccinated = np.array([index for index in range(self.nPop) - if not self.population[index].is_vaccinated()]) + if not self.population[index].immunization_history_obj.is_vaccinated()]) num_vacc = self.sim_obj.num_vaccinations num_to_vaccinate = num_vacc if len(non_vaccinated) >= num_vacc else len(non_vaccinated) @@ -961,7 +945,7 @@ def update_vaccinated(self, day): for index in self.to_vaccinate: person_to_vaccinate = self.population[index] - person_to_vaccinate.set_vaccinated(day) + person_to_vaccinate.immunization_history_obj.set_vaccinated(day) self.vaccinated[index] = index def change_mask_wearing(self): @@ -971,6 +955,5 @@ def change_mask_wearing(self): ---------- has_mask: bool """ - - for person in self.get_population(): + for person in self.population: person.has_mask = True diff --git a/cv19/simulation.py b/cv19/simulation.py index fa94252..a28cf97 100644 --- a/cv19/simulation.py +++ b/cv19/simulation.py @@ -9,9 +9,11 @@ import matplotlib.pyplot as plt from . import CV19ROOT +from .person import Person from .population import Population from .policy import Policy from .interaction_sites import InteractionSites +from .data import constants class Simulation(): @@ -57,6 +59,7 @@ def __init__(self, config_file, config_dir="", config_override_data=None, verbos self.config_dir = config_dir self.load_general_parameters(config_file) self.load_disease_parameters(self.disease_config_file, config_override_data) + self.load_immunization_parameters(self.immunization_history_config_file) # load immunization history files self.init_classes() # Have to initalize the classes after we have all of the parameters @@ -152,6 +155,43 @@ def load_disease_parameters(self, filename, config_override_data): with open(filepath, 'rb') as file: self.disease_parameters = tomli.load(file) + def load_immunization_parameters(self, config_file_name): + """Method to load in attributes from the immunization history configuration file. + + All parameters in the file are loaded into the object, and parameter names + are taken from dictionary keys. + + Parameters + ---------- + filename : str + Path to the immunization history configuration file. + """ + + # If path is absolute, use it. + if Path(config_file_name).is_absolute(): + with open(config_file_name, 'rb') as file: + self.immunization_history_parameters = tomli.load(file) + + # Assume that the configuration filename is relative to path of main config. + # If not set, assume relative to working directory. + # Last attempt try relative to cv19 project directory. + else: + filepath = Path(self.config_dir, config_file_name) + try: + with open(filepath, 'rb') as file: + self.immunization_history_parameters = tomli.load(file) + + return + + except FileNotFoundError: + warnings.warn((f"Unable to find file: {filepath} " + "assuming directory is relative to main config. " + "Attempting read relative to CV19ROOT directory.")) + + filepath = Path(CV19ROOT, config_file_name) + with open(filepath, 'rb') as file: + self.immunization_history_parameters = tomli.load(file) + def init_classes(self): """ Method that links the policy, population, and interaction sites class objects with the Simulation class (serves as pointer variables). @@ -291,7 +331,6 @@ def run(self, fail_on_rerun=True): old_lockdown_mandate = self.policy.initial_lockdown_mandate old_testing_mandate = self.policy.initial_testing_mandate old_student_mandate = self.policy.initial_student_mandate - # Loop over the number of days for day in range(self.nDays): @@ -331,8 +370,30 @@ def run(self, fail_on_rerun=True): student_default_virus_code = self.variant_codes[self.student_default_virus_type] self.pop.infect_incoming_students(indices=indices, day=day, virus_type=student_default_virus_code) - # ADD DAILY VISITORS - self.pop.add_visitors(day) + # UPDATE VISITORS + + # add a random number of visitors to the population + num_vis = np.random.choice(a=self.N_VIS_OPTION, p=self.N_VIS_PROB) + visitors_ind = [x for x in range(self.nPop, self.nPop + num_vis)] + vis_age = np.random.choice(a=self.pop.age_options, p=self.pop.age_weights, size=num_vis) + vaccine_weights = np.array([self.vaccine_type[key] for key in constants.VACCINE_OPTIONS]) + vis_vaccine_type = np.random.choice(a=constants.VACCINE_OPTIONS, + p=vaccine_weights, size=num_vis) + for i in range(0, num_vis): + visitor = Person(index=visitors_ind[i], sim_obj=self, infected=True, recovered=False, dead=False, + hospitalized=False, ICU=False, quarantined=False, quarantined_day=None, infected_day=None, + recovered_day=None, death_day=None, others_infected=None, + cure_days=None, recent_infections=None, + vaccine_info={"vaccine_type": vis_vaccine_type[i], + "vaccine_max_efficacy": self.immunization_history_parameters["vaccine_max_efficacy"][vis_vaccine_type[i]], + "vaccine_immunity_buildup_days": self.immunization_history_parameters["vaccine_immunity_buildup_days"][vis_vaccine_type[i]], + "long_term_vaccine_eff": + self.immunization_history_parameters["long_term_vaccine_eff"][vis_vaccine_type[i]], + "vaccine_efficacy_min_day": self.immunization_history_parameters["vaccine_efficacy_min_day"][vis_vaccine_type[i]]}, + age=vis_age[i], + job="Visitor", house_index=None, isolation_tendencies=0.2, + case_severity='Mild', has_mask=True, virus_type="alpha") + self.pop.population.append(visitor) # UPDATE INTERACTION SITES self.inter_sites.daily_reset() @@ -384,8 +445,8 @@ def run(self, fail_on_rerun=True): # UPDATE POPULATION - # remove the daily visitors - self.pop.remove_visitors() + # remove the guest visitors + self.pop.remove_visitors(visitors_ind) for index in self.pop.get_infected(): infected_person = self.pop.get_person(index=index) diff --git a/test/testing_config_files/immunization_history_test.toml b/test/testing_config_files/immunization_history_test.toml new file mode 100644 index 0000000..00574b4 --- /dev/null +++ b/test/testing_config_files/immunization_history_test.toml @@ -0,0 +1,19 @@ +[vaccine_max_efficacy] +Pfizer = 0.913 +Moderna = 0.941 +AZ = 0.76 + +[vaccine_immunity_buildup_days] +Pfizer = 14 +Moderna = 14 +AZ = 14 + +[vaccine_efficacy_min_day] +Pfizer = 180 +Moderna = 180 +AZ = 180 + +[long_term_vaccine_eff] +Pfizer = 0.7 +Moderna = 0.7 +AZ = 0.7 diff --git a/test/testing_config_files/main_quarantine_1.toml b/test/testing_config_files/main_quarantine_1.toml index 06ea9a9..b49fa87 100644 --- a/test/testing_config_files/main_quarantine_1.toml +++ b/test/testing_config_files/main_quarantine_1.toml @@ -1,5 +1,6 @@ [simulation_data] disease_config_file = "disease_quarantine_1.toml" +immunization_history_config_file = "immunization_history_test.toml" nDays = 50 ## needs to be at least max infectious time (29 days) + quarantine_time (14 days) nPop = 10000 vis_default_virus_type = "alpha" @@ -7,7 +8,6 @@ vis_default_severity = "MILD" student_default_virus_type = "alpha" num_students = 2000 max_num_res_students = 500 -v0 = 0 N_VIS_OPTION = [0, 1, 2, 3] N_VIS_PROB = [0.7, 0.17, 0.08, 0.05] vis_age_upper = 60 @@ -15,6 +15,10 @@ vis_age_lower = 16 inf_students_upper = 20 inf_students_lower = 2 num_vaccinations = 0 + [simulation_data.v0_parameters] + v0 = 10000 + v0_interval_start_day = -30 + v0_interval_end_day = 0 [simulation_data.variants] general = 1000 diff --git a/test/testing_config_files/main_quarantine_2.toml b/test/testing_config_files/main_quarantine_2.toml index e627a21..1385672 100644 --- a/test/testing_config_files/main_quarantine_2.toml +++ b/test/testing_config_files/main_quarantine_2.toml @@ -1,5 +1,6 @@ [simulation_data] disease_config_file = "disease_quarantine_1.toml" +immunization_history_config_file = "immunization_history_test.toml" nDays = 30 nPop = 10000 vis_default_virus_type = "alpha" @@ -7,7 +8,6 @@ vis_default_severity = "MILD" student_default_virus_type = "alpha" num_students = 2000 max_num_res_students = 500 -v0 = 0 N_VIS_OPTION = [0] N_VIS_PROB = [1] vis_age_upper = 60 @@ -15,6 +15,10 @@ vis_age_lower = 16 inf_students_upper = 20 inf_students_lower = 2 num_vaccinations = 0 + [simulation_data.v0_parameters] + v0 = 10000 + v0_interval_start_day = -30 + v0_interval_end_day = 0 [simulation_data.variants] general = 0