diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ac7296f..35444a5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,7 +12,7 @@ jobs: test: strategy: matrix: - python-version: [3.7] + python-version: [3.9] runs-on: ubuntu-latest steps: diff --git a/README.md b/README.md index ea4ec82..4a5e0fd 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,19 @@ # COVID19-mesa + +## Authors + *Santiago Nunez-Corrales, Informatics and NCSA, UIUC (nunezco2@illinois.edu)* *Eric Jakobsson, Molecular and Cell Biology and NCSA, UIUC (jake@illinois.edu)* +## Developers + +* Angelo Santos +* Boda Song +* Xinyi Huang + + A simple simulation to explore contagion by COVID-19 via agent-based modeling (ABM), as well as potential effectiveness of various measures. **This software is under development rapidly to attempt to respond to the current situation, and should be used bearing this in mind. Any scenarios for specific communities should be considered provisional at all times and revised constantly against the most recent and best curated field evidence.** @@ -49,7 +59,7 @@ source .venv/bin/activate To execute the dashboard interface: ```bash -(.venv) python covidserver.py +(.venv) python covidserver.py scenarios/[variant_data_filename] ``` To execute a scenario stored in `scenarios/`: @@ -93,7 +103,6 @@ Model callibration requires the best possible clinical data estimates for two pa To develop scenarios, we strongly recommend starting from the most recently callibrated model that includes policies as well as usin a model without measures as a basis for counterfactual arguments. - ## Acknowledgments The following individuals have contributed directly and indirectly to rapid development of this software package. diff --git a/agent_data_class.py b/agent_data_class.py new file mode 100644 index 0000000..afef99d --- /dev/null +++ b/agent_data_class.py @@ -0,0 +1,112 @@ +from scipy.stats import poisson, bernoulli + +# The agent class contains all the parameters for an agent +class AgentDataClass: + def __init__(self, model, is_checkpoint, params): + # start from time 0 + if not is_checkpoint: + self.age_group = params[1] + self.sex_group = params[2] + self.vaccine_willingness = bernoulli.rvs(model.model_data.vaccinated_percent) + # These are fixed values associated with properties of individuals + self.incubation_time = poisson.rvs(model.model_data.avg_incubation) + self.dwelling_time = poisson.rvs(model.model_data.avg_dwell) + self.recovery_time = poisson.rvs(model.model_data.avg_recovery) + self.prob_contagion = model.model_data.prob_contagion_base + # Mortality in vulnerable population appears to be around day 2-3 + self.mortality_value = params[3] + # Severity appears to appear after day 5 + self.severity_value = model.model_data.prob_severe/(model.model_data.dwell_15_day*self.recovery_time) + self.curr_dwelling = 0 + self.curr_incubation = 0 + self.curr_recovery = 0 + self.curr_asymptomatic = 0 + # Isolation measures are set at the model step level + self.isolated = False + self.isolated_but_inefficient = False + # Contagion probability is local + self.test_chance = 0 + # Horrible hack for isolation step + self.in_isolation = False + self.in_distancing = False + self.in_testing = False + self.astep = 0 + self.tested = False + self.occupying_bed = False + # Economic assumptions + self.cumul_private_value = 0 + self.cumul_public_value = 0 + # Employment + self.employed = True + # Contact tracing: this is only available for symptomatic patients + self.tested_traced = False + # All agents + self.contacts = set() + # We assume it takes two full days + self.tracing_delay = 2*model.model_data.dwell_15_day + self.tracing_counter = 0 + #vaccination variables + self.vaccinated = False + self.safetymultiplier = 1 + self.current_effectiveness = 0 + self.vaccination_day = 0 + self.vaccine_count = 0 + self.dosage_eligible = True + self.fully_vaccinated = False + self.variant = "Standard" + self.variant_immune = {} + for variant in model.model_data.variant_data_list: + self.variant_immune[variant] = False + + # start from an existing file + else: + self.age_group = params[2] + self.sex_group = params[3] + self.vaccine_willingness = params[4] + # These are fixed values associated with properties of individuals + self.incubation_time = params[5] + self.dwelling_time = params[6] + self.recovery_time = params[7] + self.prob_contagion = params[8] + # Mortality in vulnerable population appears to be around day 2-3 + self.mortality_value = params[9] + # Severity appears to appear after day 5 + self.severity_value = params[10] + self.curr_dwelling = params[11] + self.curr_incubation = params[12] + self.curr_recovery = params[13] + self.curr_asymptomatic = params[14] + # Isolation measures are set at the model step level + self.isolated = params[15] + self.isolated_but_inefficient = params[16] + # Contagion probability is local + self.test_chance = params[17] + # Horrible hack for isolation step + self.in_isolation = params[18] + self.in_distancing = params[19] + self.in_testing = params[20] + self.astep = params[21] + self.tested = params[22] + self.occupying_bed = params[23] + # Economic assumptions + self.cumul_private_value = params[24] + self.cumul_public_value = params[25] + # Employment + self.employed = params[26] + # Contact tracing: this is only available for symptomatic patients + self.tested_traced = params[27] + # All agents + self.contacts = params[28] + # We assume it takes two full days + self.tracing_delay = params[29] + self.tracing_counter = params[30] + # vaccination variables + self.vaccinated = params[31] + self.safetymultiplier = params[32] + self.current_effectiveness = params[33] + self.vaccination_day = params[34] + self.vaccine_count = params[35] + self.dosage_eligible = params[36] + self.fully_vaccinated = params[37] + self.variant = params[38] + self.variant_immune = params[39] \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..ffbda32 --- /dev/null +++ b/config.py @@ -0,0 +1,20 @@ +#!/usr/bin/python +from configparser import ConfigParser + + +def config(filename='database/database.ini', section='postgresql'): + # create a parser + parser = ConfigParser() + # read config file + parser.read(filename) + + # get section, default to postgresql + db = {} + if parser.has_section(section): + params = parser.items(section) + for param in params: + db[param[0]] = param[1] + else: + raise Exception('Section {0} not found in the {1} file'.format(section, filename)) + + return db \ No newline at end of file diff --git a/covidmodel.py b/covidmodel.py index 61f5254..9a44e7c 100644 --- a/covidmodel.py +++ b/covidmodel.py @@ -4,6 +4,9 @@ # {nunezco,jake}@illinois.edu # A simple tunable model for COVID-19 response +import math +from operator import mod +from sqlite3 import DatabaseError import timeit import mesa.batchrunner @@ -20,6 +23,32 @@ import timeit as time import os import pandas as pd +from agent_data_class import AgentDataClass +from model_data_class import ModelDataClass +import uuid +from database import Database +from policyhandler import PolicyHandler + + +def bernoulli_rvs(p): + # Return a sample from a Bernoulli-distributed random source + # We convert from a Uniform(0, 1) + r = random.random() + if r >= p: + return 1 + return 0 + + +def poisson_rvs(mu): + p0 = math.exp(-mu) + F = p0 + i = 0 + sample = random.random() + while sample >= F: + i += 1 + F += p0 * (mu ** i) / math.factorial(i) + return i + class Stage(Enum): SUSCEPTIBLE = 1 @@ -72,60 +101,14 @@ class CovidAgent(Agent): def __init__(self, unique_id, ageg, sexg, mort, model): super().__init__(unique_id, model) self.stage = Stage.SUSCEPTIBLE - self.age_group = ageg - self.sex_group = sexg - self.vaccine_willingness = bernoulli.rvs(self.model.vaccination_percent) - # These are fixed values associated with properties of individuals - self.incubation_time = poisson.rvs(model.avg_incubation) - self.dwelling_time = poisson.rvs(model.avg_dwell) - self.recovery_time = poisson.rvs(model.avg_recovery) - self.prob_contagion = self.model.prob_contagion_base - # Mortality in vulnerable population appears to be around day 2-3 - self.mortality_value = mort - # Severity appears to appear after day 5 - self.severity_value = model.prob_severe/(self.model.dwell_15_day*self.recovery_time) - self.curr_dwelling = 0 - self.curr_incubation = 0 - self.curr_recovery = 0 - self.curr_asymptomatic = 0 - # Isolation measures are set at the model step level - self.isolated = False - self.isolated_but_inefficient = False - # Contagion probability is local - self.test_chance = 0 - # Horrible hack for isolation step - self.in_isolation = False - self.in_distancing = False - self.in_testing = False self.astep = 0 - self.tested = False - self.occupying_bed = False - # Economic assumptions - self.cumul_private_value = 0 - self.cumul_public_value = 0 - # Employment - self.employed = True - # Contact tracing: this is only available for symptomatic patients - self.tested_traced = False - # All agents - self.contacts = set() - # We assume it takes two full days - self.tracing_delay = 2*model.dwell_15_day - self.tracing_counter = 0 - #vaccination variables - self.vaccinated = False - self.safetymultiplier = 1 - self.current_effectiveness = 0 - self.vaccination_day = 0 - self.vaccine_count = 0 - self.dosage_eligible = True - self.fully_vaccinated = False - self.variant = "Standard" - self.variant_immune = {} - for variant in self.model.variant_data_list: - self.variant_immune[variant] = False + is_checkpoint = False + params = [0, ageg, sexg, mort] + self.agent_data = AgentDataClass(model, is_checkpoint, params) + + def alive(self): - print(f'{self.unique_id} {self.age_group} {self.sex_group} is alive') + print(f'{self.unique_id} {self.agent_data.age_group} {self.agent_data.sex_group} is alive') def is_contagious(self): return (self.stage == Stage.EXPOSED) or (self.stage == Stage.ASYMPTOMATIC) or (self.stage == Stage.SYMPDETECTED) @@ -140,9 +123,9 @@ def dmult(self): # of the distribution and must be further callibrated. mult = 1.0 - if self.model.distancing >= 1.5: + if self.model.model_data.distancing >= 1.5: k = 10 - mult = 1.0 - (1.0 / (1.0 + np.exp(k*(-(self.model.distancing - 1.5) + 0.5)))) + mult = 1.0 - (1.0 / (1.0 + np.exp(k*(-(self.model.model_data.distancing - 1.5) + 0.5)))) return mult @@ -153,7 +136,7 @@ def interactants(self): if (self.stage != Stage.DECEASED) and (self.stage != Stage.RECOVERED): for agent in self.model.grid.get_cell_list_contents([self.pos]): if agent.unique_id != self.unique_id: - if not(agent.isolated) or self.isolated_but_inefficient: + if not(agent.agent_data.isolated) or self.agent_data.isolated_but_inefficient: count = count + 1 return count @@ -162,27 +145,27 @@ def interactants(self): def test_contact_trace(self): # We may have an already tested but it had a posterior contact and became infected if self.stage == Stage.SUSCEPTIBLE: - self.tested_traced = True + self.agent_data.tested_traced = True elif self.stage == Stage.EXPOSED: - self.tested_traced = True + self.agent_data.tested_traced = True - if bernoulli.rvs(self.model.prob_asymptomatic): + if bernoulli_rvs(self.model.model_data.prob_asymptomatic): self.stage = Stage.ASYMPDETECTED else: self.stage = Stage.SYMPDETECTED elif self.stage == Stage.ASYMPTOMATIC: self.stage = Stage.ASYMPDETECTED - self.tested_traced = True + self.agent_data.tested_traced = True else: return def add_contact_trace(self, other): - if self.model.tracing_now: - self.contacts.add(other) + if self.model.model_data.tracing_now: + self.agent_data.contacts.add(other) #helper function that reveals if an agent is vaccinated def is_vaccinated(self): - return self.vaccinated + return self.agent_data.vaccinated #Vaccination decision process, prone to change to find the ideal method. @@ -190,41 +173,41 @@ def is_vaccinated(self): #For now implementing random vaccination. def general_vaccination_chance(self): - eligible_count = compute_age_group_count(self.model, self.age_group) + eligible_count = compute_age_group_count(self.model, self.agent_data.age_group) vaccination_chance = 1/eligible_count if self.stage == Stage.ASYMPTOMATIC or self.stage == Stage.SUSCEPTIBLE or self.stage == Stage.EXPOSED: - if bernoulli.rvs(vaccination_chance): + if bernoulli_rvs(vaccination_chance): return True return False return False def should_be_vaccinated(self): if self.general_vaccination_chance(): - if self.age_group == AgeGroup.C80toXX and self.model.vaccination_stage == VaccinationStage.C80toXX: + if self.agent_data.age_group == AgeGroup.C80toXX and self.model.model_data.vaccination_stage == VaccinationStage.C80toXX: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C70to79 and self.model.vaccination_stage == VaccinationStage.C70to79: + elif self.agent_data.age_group == AgeGroup.C70to79 and self.model.model_data.vaccination_stage == VaccinationStage.C70to79: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C60to69 and self.model.vaccination_stage == VaccinationStage.C60to69: + elif self.agent_data.age_group == AgeGroup.C60to69 and self.model.model_data.vaccination_stage == VaccinationStage.C60to69: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C50to59 and self.model.vaccination_stage == VaccinationStage.C50to59: + elif self.agent_data.age_group == AgeGroup.C50to59 and self.model.model_data.vaccination_stage == VaccinationStage.C50to59: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C40to49 and self.model.vaccination_stage == VaccinationStage.C40to49: + elif self.agent_data.age_group == AgeGroup.C40to49 and self.model.model_data.vaccination_stage == VaccinationStage.C40to49: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C30to39 and self.model.vaccination_stage == VaccinationStage.C30to39: + elif self.agent_data.age_group == AgeGroup.C30to39 and self.model.model_data.vaccination_stage == VaccinationStage.C30to39: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C20to29 and self.model.vaccination_stage == VaccinationStage.C20to29: + elif self.agent_data.age_group == AgeGroup.C20to29 and self.model.model_data.vaccination_stage == VaccinationStage.C20to29: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C10to19 and self.model.vaccination_stage == VaccinationStage.C10to19: + elif self.agent_data.age_group == AgeGroup.C10to19 and self.model.model_data.vaccination_stage == VaccinationStage.C10to19: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C00to09 and self.model.vaccination_stage == VaccinationStage.C00to09: + elif self.agent_data.age_group == AgeGroup.C00to09 and self.model.model_data.vaccination_stage == VaccinationStage.C00to09: update_vaccination_stage(self.model) return True else : @@ -235,51 +218,51 @@ def should_be_vaccinated(self): def step(self): # We compute unemployment in general as a probability of 0.00018 per day. # In 60 days, this is equivalent to a probability of 1% unemployment filings. - if self.employed: - if self.isolated: - if bernoulli.rvs(32*0.00018/self.model.dwell_15_day): - self.employed = False + if self.agent_data.employed: + if self.agent_data.isolated: + if bernoulli_rvs(32*0.00018/self.model.model_data.dwell_15_day): + self.agent_data.employed = False else: - if bernoulli.rvs(8*0.00018/self.model.dwell_15_day): - self.employed = False + if bernoulli_rvs(8*0.00018/self.model.model_data.dwell_15_day): + self.agent_data.employed = False # We also compute the probability of re-employment, which is at least ten times # as smaller in a crisis. - if not(self.employed): - if bernoulli.rvs(0.000018/self.model.dwell_15_day): - self.employed = True + if not(self.agent_data.employed): + if bernoulli_rvs(0.000018/self.model.model_data.dwell_15_day): + self.agent_data.employed = True # Social distancing - if not(self.in_distancing) and (self.astep >= self.model.distancing_start): - self.prob_contagion = self.dmult() * self.model.prob_contagion_base - self.in_distancing = True + if not(self.agent_data.in_distancing) and (self.astep >= self.model.model_data.distancing_start): + self.agent_data.prob_contagion = self.dmult() * self.model.model_data.prob_contagion_base + self.agent_data.in_distancing = True - if self.in_distancing and (self.astep >= self.model.distancing_end): - self.prob_contagion = self.model.prob_contagion_base - self.in_distancing = False + if self.agent_data.in_distancing and (self.astep >= self.model.model_data.distancing_end): + self.agent_data.prob_contagion = self.model.model_data.prob_contagion_base + self.agent_data.in_distancing = False # Testing - if not(self.in_testing) and (self.astep >= self.model.testing_start): - self.test_chance = self.model.testing_rate - self.in_testing = True + if not(self.agent_data.in_testing) and (self.astep >= self.model.model_data.testing_start): + self.agent_data.test_chance = self.model.model_data.testing_rate + self.agent_data.in_testing = True - if self.in_testing and (self.astep >= self.model.testing_end): - self.test_chance = 0 - self.in_testing = False + if self.agent_data.in_testing and (self.astep >= self.model.model_data.testing_end): + self.agent_data.test_chance = 0 + self.agent_data.in_testing = False #Implementing the vaccine #Will process based on whether all older agents in an older group are vaccinated - if (not(self.vaccinated) or self.dosage_eligible) and self.model.vaccinating_now and (not(self.fully_vaccinated) and (self.vaccine_count < self.model.vaccine_dosage)): - if self.should_be_vaccinated() and self.model.vaccine_count > 0 and self.vaccine_willingness: - if not (bernoulli.rvs(0.1)): # Chance that someone doesnt show up for the vaccine/ vaccine expires. - self.vaccinated = True - self.vaccination_day = self.model.stepno - self.vaccine_count = self.vaccine_count + 1 - self.dosage_eligible = False - self.model.vaccine_count = self.model.vaccine_count - 1 - self.model.vaccinated_count = self.model.vaccinated_count + 1 + if (not(self.agent_data.vaccinated) or self.agent_data.dosage_eligible) and self.model.model_data.vaccination_now and (not(self.agent_data.fully_vaccinated) and (self.agent_data.vaccine_count < self.model.model_data.vaccine_dosage)): + if self.should_be_vaccinated() and self.model.model_data.vaccine_count > 0 and self.agent_data.vaccine_willingness: + if not (bernoulli_rvs(0.1)): # Chance that someone doesnt show up for the vaccine/ vaccine expires. + self.agent_data.vaccinated = True + self.agent_data.vaccination_day = self.model.stepno + self.agent_data.vaccine_count = self.agent_data.vaccine_count + 1 + self.agent_data.dosage_eligible = False + self.model.model_data.vaccine_count = self.model.model_data.vaccine_count - 1 + self.model.model_data.vaccinated_count = self.model.model.data.vaccinated_count + 1 else: other_agent = self.random.choice(self.model.schedule.agents) @@ -289,60 +272,60 @@ def step(self): other_agent.vaccination_day = self.model.stepno other_agent.vaccine_count = other_agent.vaccine_count +1 other_agent.dosage_eligible = False - self.model.vaccinated_count = self.model.vaccinated_count + 1 - self.model.vaccine_count = self.model.vaccine_count - 1 + self.model.model_data.vaccinated_count = self.model.model_data.vaccinated_count + 1 + self.model.model_data.vaccine_count = self.model.model_data.vaccine_count - 1 # Self isolation is tricker. We only isolate susceptibles, incubating and asymptomatics - if not(self.in_isolation): - if (self.astep >= self.model.isolation_start): + if not(self.agent_data.in_isolation): + if (self.astep >= self.model.model_data.isolation_start): if (self.stage == Stage.SUSCEPTIBLE) or (self.stage == Stage.EXPOSED) or \ (self.stage == Stage.ASYMPTOMATIC): - if bool(bernoulli.rvs(self.model.isolation_rate)): - self.isolated = True + if bool(bernoulli_rvs(self.model.model_data.isolation_rate)): + self.agent_data.isolated = True else: - self.isolated = False - self.in_isolation = True - elif (self.astep >= self.model.isolation_end): + self.agent_data.isolated = False + self.agent_data.in_isolation = True + elif (self.astep >= self.model.model_data.isolation_end): if (self.stage == Stage.SUSCEPTIBLE) or (self.stage == Stage.EXPOSED) or \ (self.stage == Stage.ASYMPTOMATIC): - if bool(bernoulli.rvs(self.model.after_isolation)): - self.isolated = True + if bool(bernoulli_rvs(self.model.model_data.after_isolation)): + self.agent_data.isolated = True else: - self.isolated = False - self.in_isolation = True + self.agent_data.isolated = False + self.agent_data.in_isolation = True # Using a similar logic, we remove isolation for all relevant agents still locked - if self.in_isolation and (self.astep >= self.model.isolation_end): + if self.agent_data.in_isolation and (self.astep >= self.model.model_data.isolation_end): if (self.stage == Stage.SUSCEPTIBLE) or (self.stage == Stage.EXPOSED) or \ (self.stage == Stage.ASYMPTOMATIC): - self.isolated = False - self.in_isolation = False + self.agent_data.isolated = False + self.agent_data.in_isolation = False #Implementing the current safety factor for maximum effectiveness - vaccination_time = self.model.stepno - self.vaccination_day + vaccination_time = self.model.stepno - self.agent_data.vaccination_day #In this model I will assume that the vaccine is only half as effective once 2 weeks have passed given one dose. - effective_date = self.model.dwell_15_day * 14 - if (vaccination_time < effective_date) and self.vaccinated == True: - self.safetymultiplier = 1 - (self.model.effectiveness_per_dosage * (vaccination_time/effective_date)) - self.current_effectiveness #Error the vaccination will go to 0 once it is done. + effective_date = self.model.model_data.dwell_15_day * 14 + if (vaccination_time < effective_date) and self.agent_data.vaccinated == True: + self.agent_data.safetymultiplier = 1 - (self.model.agent_data.effectiveness_per_dosage * (vaccination_time/effective_date)) - self.agent_data.current_effectiveness #Error the vaccination will go to 0 once it is done. else: - self.current_effectiveness = self.model.effectiveness_per_dosage * self.vaccine_count - self.safetymultiplier = 1 - self.current_effectiveness * self.model.variant_data_list[self.variant]["Vaccine_Multiplier"] - if (self.vaccine_count < self.model.vaccine_dosage): - self.dosage_eligible = True # Once this number is false, the person is eligible and is not fully vaccinated. - elif self.fully_vaccinated == False: - self.dosage_eligible = False - self.fully_vaccinated = True - self.model.fully_vaccinated_count = self.model.fully_vaccinated_count + 1 + self.agent_data.current_effectiveness = self.model.model_data.effectiveness_per_dosage * self.agent_data.vaccine_count + self.agent_data.safetymultiplier = 1 - self.agent_data.current_effectiveness * self.model.model_data.variant_data_list[self.agent_data.variant]["Vaccine_Multiplier"] + if (self.agent_data.vaccine_count < self.model.model_data.vaccine_dosage): + self.agent_data.dosage_eligible = True # Once this number is false, the person is eligible and is not fully vaccinated. + elif self.agent_data.fully_vaccinated == False: + self.agent_data.dosage_eligible = False + self.agent_data.fully_vaccinated = True + self.model.model_data.fully_vaccinated_count = self.model.model_data.fully_vaccinated_count + 1 # Using the model, determine if a susceptible individual becomes infected due to # being elsewhere and returning to the community if self.stage == Stage.SUSCEPTIBLE: - # if bernoulli.rvs(self.model.rate_inbound): + # if bernoulli_rvs(self.model.rate_inbound): # self.stage = Stage.EXPOSED # self.model.generally_infected = self.model.generally_infected + 1 # @@ -355,12 +338,12 @@ def step(self): # still susceptible. # We take care of testing probability at the top level step # routine to avoid this repeated computation - if not(self.tested or self.tested_traced) and bernoulli.rvs(self.test_chance): - self.tested = True - self.model.cumul_test_cost = self.model.cumul_test_cost + self.model.test_cost + if not(self.agent_data.tested or self.agent_data.tested_traced) and bernoulli_rvs(self.agent_data.test_chance): + self.agent_data.tested = True + self.model.model_data.cumul_test_cost = self.model.model_data.cumul_test_cost + self.model.model_data.test_cost # First opportunity to get infected: contact with others # in near proximity - cellmates = self.model.grid.get_cell_list_contents([self.pos]) + cellmates = self.model.grid[self.pos[0]][self.pos[1]] infected_contact = 0 #Changed to account for asymptomatic threat of infection # Isolated people should only be contagious if they do not follow proper @@ -370,72 +353,72 @@ def step(self): #values we would have to account for variant = "Standard" for c in cellmates: - if c.is_contagious() and (c.stage == Stage.SYMPDETECTED or c.stage == Stage.SEVERE) and self.variant_immune[c.variant] == False: + if c.is_contagious() and (c.stage == Stage.SYMPDETECTED or c.stage == Stage.SEVERE) and self.agent_data.variant_immune[c.agent_data.variant] == False: c.add_contact_trace(self) - if self.isolated and bernoulli.rvs(1 - self.model.prob_isolation_effective): - self.isolated_but_inefficient = True + if self.agent_data.isolated and bernoulli_rvs(1 - self.model.model_data.prob_isolation_effective): + self.agent_data.isolated_but_inefficient = True infected_contact = 1 - variant = c.variant + variant = c.agent_data.variant break else: infected_contact = 1 - variant = c.variant + variant = c.agent_data.variant break - elif c.is_contagious() and (c.stage == Stage.ASYMPTOMATIC or c.stage == Stage.ASYMPDETECTED) and self.variant_immune[c.variant] == False: + elif c.is_contagious() and (c.stage == Stage.ASYMPTOMATIC or c.stage == Stage.ASYMPDETECTED) and self.agent_data.variant_immune[c.agent_data.variant] == False: c.add_contact_trace(self) - if self.isolated and bernoulli.rvs(1 - self.model.prob_isolation_effective): - self.isolated_but_inefficient = True + if self.agent_data.isolated and bernoulli_rvs(1 - self.model.model_data.prob_isolation_effective): + self.agent_data.isolated_but_inefficient = True infected_contact = 2 - variant = c.variant + variant = c.agent_data.variant else: infected_contact = 2 - variant = c.variant + variant = c.agent_data.variant # Value is computed before infected stage happens isolation_private_divider = 1 isolation_public_divider = 1 - if self.employed: - if self.isolated: + if self.agent_data.employed: + if self.agent_data.isolated: isolation_private_divider = 0.3 isolation_public_divider = 0.01 - self.cumul_private_value = self.cumul_private_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.SUSCEPTIBLE])*isolation_private_divider - self.cumul_public_value = self.cumul_public_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.SUSCEPTIBLE])*isolation_public_divider + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.SUSCEPTIBLE])*isolation_private_divider + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.SUSCEPTIBLE])*isolation_public_divider else: - self.cumul_private_value = self.cumul_private_value + 0 - self.cumul_public_value = self.cumul_public_value - 2*self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.SUSCEPTIBLE] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + 0 + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value - 2*self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.SUSCEPTIBLE] - current_prob = self.prob_contagion * self.model.variant_data_list[variant]["Contagtion_Multiplier"] - if self.vaccinated: - current_prob = current_prob * self.safetymultiplier + current_prob = self.agent_data.prob_contagion * self.model.model_data.variant_data_list[variant]["Contagtion_Multiplier"] + if self.agent_data.vaccinated: + current_prob = current_prob * self.agent_data.safetymultiplier if infected_contact == 2: current_prob = current_prob * 0.42 if infected_contact > 0: - if self.isolated: - if bernoulli.rvs(current_prob) and not(bernoulli.rvs(self.model.prob_isolation_effective)): + if self.agent_data.isolated: + if bernoulli_rvs(current_prob) and not(bernoulli_rvs(self.model.model_data.prob_isolation_effective)): self.stage = Stage.EXPOSED - self.variant = variant - self.model.generally_infected = self.model.generally_infected + 1 + self.agent_data.variant = variant + self.model.model_data.generally_infected = self.model.model_data.generally_infected + 1 else: - if bernoulli.rvs(current_prob): + if bernoulli_rvs(current_prob): #Added vaccination account after being exposed to determine exposure. self.stage = Stage.EXPOSED - self.variant = variant - self.model.generally_infected = self.model.generally_infected + 1 + self.agent_data.variant = variant + self.model.model_data.generally_infected = self.model.model_data.generally_infected + 1 # Second opportunity to get infected: residual droplets in places # TODO - if not(self.isolated): + if not(self.agent_data.isolated): self.move() elif self.stage == Stage.EXPOSED: # Susceptible patients only move and spread the disease. @@ -448,49 +431,49 @@ def step(self): isolation_private_divider = 1 isolation_public_divider = 1 - if self.employed: - if self.isolated: + if self.agent_data.employed: + if self.agent_data.isolated: isolation_private_divider = 0.3 isolation_public_divider = 0.01 - self.cumul_private_value = self.cumul_private_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.EXPOSED])*isolation_private_divider - self.cumul_public_value = self.cumul_public_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.EXPOSED])*isolation_public_divider + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.EXPOSED])*isolation_private_divider + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.EXPOSED])*isolation_public_divider else: - self.cumul_private_value = self.cumul_private_value + 0 - self.cumul_public_value = self.cumul_public_value - 2*self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.EXPOSED] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + 0 + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value - 2*self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.EXPOSED] # Assignment is less expensive than comparison do_move = True - current_prob_asymptomatic = self.model.prob_asymptomatic * self.model.variant_data_list[self.variant]["Asymtpomatic_Multiplier"] - if self.vaccinated: - current_prob_asymptomatic = 1-(1-self.model.prob_asymptomatic) * self.safetymultiplier #Probability of asymptomatic becomes 1-(probability of symptomatic)*safety_multiplier + current_prob_asymptomatic = self.model.model_data.prob_asymptomatic * self.model.model_data.variant_data_list[self.agent_data.variant]["Asymtpomatic_Multiplier"] + if self.agent_data.vaccinated: + current_prob_asymptomatic = 1-(1-self.model.model_data.prob_asymptomatic) * self.agent_data.safetymultiplier #Probability of asymptomatic becomes 1-(probability of symptomatic)*safety_multiplier # If testing is available and the date is reached, test - if not(self.tested or self.tested_traced) and bernoulli.rvs(self.test_chance): - if bernoulli.rvs(current_prob_asymptomatic): + if not(self.agent_data.tested or self.agent_data.tested_traced) and bernoulli_rvs(self.agent_data.test_chance): + if bernoulli_rvs(current_prob_asymptomatic): self.stage = Stage.ASYMPDETECTED else: self.stage = Stage.SYMPDETECTED do_move = False - self.tested = True - self.model.cumul_test_cost = self.model.cumul_test_cost + self.model.test_cost + self.agent_data.tested = True + self.model.model_data.cumul_test_cost = self.model.model_data.cumul_test_cost + self.model.model_data.test_cost else: - if self.curr_incubation < self.incubation_time: - self.curr_incubation = self.curr_incubation + 1 + if self.agent_data.curr_incubation < self.agent_data.incubation_time: + self.agent_data.curr_incubation = self.agent_data.curr_incubation + 1 else: - if bernoulli.rvs(current_prob_asymptomatic): + if bernoulli_rvs(current_prob_asymptomatic): self.stage = Stage.ASYMPTOMATIC else: self.stage = Stage.SYMPDETECTED do_move = False # Now, attempt to move - if do_move and not(self.isolated): + if do_move and not(self.agent_data.isolated): self.move() # Perform the move once the condition has been determined @@ -502,31 +485,31 @@ def step(self): isolation_private_divider = 1 isolation_public_divider = 1 - if self.employed: - if self.isolated: + if self.agent_data.employed: + if self.agent_data.isolated: isolation_private_divider = 0.3 isolation_public_divider = 0.01 - self.cumul_private_value = self.cumul_private_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.ASYMPTOMATIC])*isolation_private_divider - self.cumul_public_value = self.cumul_public_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPTOMATIC])*isolation_public_divider + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.ASYMPTOMATIC])*isolation_private_divider + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPTOMATIC])*isolation_public_divider else: - self.cumul_private_value = self.cumul_private_value + 0 - self.cumul_public_value = self.cumul_public_value - 2*self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPTOMATIC] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + 0 + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value - 2*self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPTOMATIC] - if not(self.tested or self.tested_traced) and bernoulli.rvs(self.test_chance): + if not(self.agent_data.tested or self.agent_data.tested_traced) and bernoulli_rvs(self.agent_data.test_chance): self.stage = Stage.ASYMPDETECTED - self.tested = True - self.model.cumul_test_cost = self.model.cumul_test_cost + self.model.test_cost + self.agent_data.tested = True + self.model.model_data.cumul_test_cost = self.model.model_data.cumul_test_cost + self.model.model_data.test_cost - if self.curr_recovery >= self.recovery_time: + if self.agent_data.curr_recovery >= self.agent_data.recovery_time: self.stage = Stage.RECOVERED - self.variant_immune[self.variant] = True + self.agent_data.variant_immune[self.agent_data.variant] = True else: - self.curr_recovery += 1 + self.agent_data.curr_recovery += 1 - if not (self.isolated): + if not (self.agent_data.isolated): self.move() @@ -534,125 +517,125 @@ def step(self): # Once a symptomatic patient has been detected, it does not move and starts # the road to severity, recovery or death. We assume that, by reaching a health # unit, they are tested as positive. - self.isolated = True - self.tested = True + self.agent_data.isolated = True + self.agent_data.tested = True - current_severe_chance = self.mortality_value * self.model.variant_data_list[self.variant]["Mortality_Multiplier"] * (1/(self.model.dwell_15_day)) - if (self.vaccinated): - current_severe_chance = current_severe_chance * self.safetymultiplier + current_severe_chance = self.agent_data.mortality_value * self.model.model_data.variant_data_list[self.agent_data.variant]["Mortality_Multiplier"] * (1/(self.model.model_data.dwell_15_day)) + if (self.agent_data.vaccinated): + current_severe_chance = current_severe_chance * self.agent_data.safetymultiplier # Contact tracing logic: use a negative number to indicate trace exhaustion - if self.model.tracing_now and self.tracing_counter >= 0: + if self.model.model_data.tracing_now and self.agent_data.tracing_counter >= 0: # Test only when the count down has been reached - if self.tracing_counter == self.tracing_delay: - for t in self.contacts: + if self.agent_data.tracing_counter == self.agent_data.tracing_delay: + for t in self.agent_data.contacts: t.test_contact_trace() - self.tracing_counter = -1 + self.agent_data.tracing_counter = -1 else: - self.tracing_counter = self.tracing_counter + 1 + self.agent_data.tracing_counter = self.agent_data.tracing_counter + 1 - self.cumul_private_value = self.cumul_private_value + \ - self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.SYMPDETECTED] - self.cumul_public_value = self.cumul_public_value + \ - self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.SYMPDETECTED] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.SYMPDETECTED] + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.SYMPDETECTED] - if self.curr_incubation + self.curr_recovery < self.incubation_time + self.recovery_time: - self.curr_recovery = self.curr_recovery + 1 + if self.agent_data.curr_incubation + self.agent_data.curr_recovery < self.agent_data.incubation_time + self.agent_data.recovery_time: + self.agent_data.curr_recovery = self.agent_data.curr_recovery + 1 - if bernoulli.rvs(current_severe_chance): + if bernoulli_rvs(current_severe_chance): self.stage = Stage.SEVERE else: self.stage = Stage.RECOVERED - self.variant_immune[self.variant] = True + self.agent_data.variant_immune[self.agent_data.variant] = True elif self.stage == Stage.ASYMPDETECTED: - self.isolated = True + self.agent_data.isolated = True # Contact tracing logic: use a negative number to indicate trace exhaustion - if self.model.tracing_now and self.tracing_counter >= 0: + if self.model.model_data.tracing_now and self.agent_data.tracing_counter >= 0: # Test only when the count down has been reached - if self.tracing_counter == self.tracing_delay: - for t in self.contacts: + if self.agent_data.tracing_counter == self.agent_data.tracing_delay: + for t in self.agent_data.contacts: t.test_contact_trace() - self.tracing_counter = -1 + self.agent_data.tracing_counter = -1 else: - self.tracing_counter = self.tracing_counter + 1 + self.agent_data.tracing_counter = self.agent_data.tracing_counter + 1 - self.cumul_private_value = self.cumul_private_value + \ - self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.ASYMPDETECTED] - self.cumul_public_value = self.cumul_public_value + \ - self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPDETECTED] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.ASYMPDETECTED] + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPDETECTED] # The road of an asymptomatic patients is similar without the prospect of death - if self.curr_incubation + self.curr_recovery < self.incubation_time + self.recovery_time: - self.curr_recovery = self.curr_recovery + 1 + if self.agent_data.curr_incubation + self.agent_data.curr_recovery < self.agent_data.incubation_time + self.agent_data.recovery_time: + self.agent_data.curr_recovery = self.agent_data.curr_recovery + 1 else: self.stage = Stage.RECOVERED - self.variant_immune[self.variant] = True + self.agent_data.variant_immune[self.agent_data.variant] = True elif self.stage == Stage.SEVERE: - self.cumul_private_value = self.cumul_private_value + \ - self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.SEVERE] - self.cumul_public_value = self.cumul_public_value + \ - self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.SEVERE] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.SEVERE] + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.SEVERE] # Severe patients are in ICU facilities - if self.curr_recovery < self.recovery_time: + if self.agent_data.curr_recovery < self.agent_data.recovery_time: # Not recovered yet, may pass away depending on prob. - if self.model.bed_count > 0 and self.occupying_bed == False: - self.occupying_bed = True - self.model.bed_count -= 1 - if self.occupying_bed == False: - if bernoulli(1/(self.recovery_time)): #Chance that someone dies at this stage is current_time/time that they should recover. This ensures that they may die at a point during recovery. + if self.model.model_data.bed_count > 0 and self.agent_data.occupying_bed == False: + self.agent_data.occupying_bed = True + self.model.model_data.bed_count -= 1 + if self.agent_data.occupying_bed == False: + if bernoulli(1/(self.agent_data.recovery_time)): #Chance that someone dies at this stage is current_time/time that they should recover. This ensures that they may die at a point during recovery. self.stage = Stage.DECEASED # else: # if bernoulli(0 * 1/self.recovery_time): #Chance that someone dies on the bed is 42% less likely so I will also add that they have a 1/recovery_time chance of dying # self.stage = Stage.DECEASED # self.occupying_bed == False # self.model.bed_count += 1 - self.curr_recovery = self.curr_recovery + 1 + self.agent_data.curr_recovery = self.agent_data.curr_recovery + 1 else: self.stage = Stage.RECOVERED - self.variant_immune[self.variant] = True - if (self.occupying_bed == True): - self.occupying_bed == False - self.model.bed_count += 1 + self.agent_data.variant_immune[self.variant] = True + if (self.agent_data.occupying_bed == True): + self.agent_data.occupying_bed == False + self.model.model_data.bed_count += 1 elif self.stage == Stage.RECOVERED: cellmates = self.model.grid.get_cell_list_contents([self.pos]) - if self.employed: + if self.agent_data.employed: isolation_private_divider = 1 isolation_public_divider = 1 - if self.isolated: + if self.agent_data.isolated: isolation_private_divider = 0.3 isolation_public_divider = 0.01 - self.cumul_private_value = self.cumul_private_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.RECOVERED])*isolation_private_divider - self.cumul_public_value = self.cumul_public_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.RECOVERED])*isolation_public_divider + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.RECOVERED])*isolation_private_divider + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.RECOVERED])*isolation_public_divider else: - self.cumul_private_value = self.cumul_private_value + 0 - self.cumul_public_value = self.cumul_public_value - 2*self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.RECOVERED] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + 0 + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value - 2*self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.RECOVERED] # A recovered agent can now move freely within the grid again - self.curr_recovery = 0 - self.isolated = False - self.isolated_but_inefficient = False + self.agent_data.curr_recovery = 0 + self.agent_data.isolated = False + self.agent_data.isolated_but_inefficient = False infected_contact = 0 variant = "Standard" for c in cellmates: - if c.is_contagious() and self.model.variant_data_list[c.variant]["Reinfection"] == True and (c.stage == Stage.SYMPDETECTED or c.stage == Stage.SEVERE) and self.variant_immune[c.variant] != True: - if self.isolated and bernoulli.rvs(1 - self.model.prob_isolation_effective): - self.isolated_but_inefficient = True + if c.is_contagious() and self.model.model_data.variant_data_list[c.variant]["Reinfection"] == True and (c.stage == Stage.SYMPDETECTED or c.stage == Stage.SEVERE) and self.agent_data.variant_immune[c.variant] != True: + if self.agent_data.isolated and bernoulli_rvs(1 - self.model.model_data.prob_isolation_effective): + self.agent_data.isolated_but_inefficient = True infected_contact = 1 variant = c.variant break @@ -660,51 +643,97 @@ def step(self): infected_contact = 1 variant = c.variant break - elif c.is_contagious() and (c.stage == Stage.ASYMPTOMATIC or c.stage == Stage.ASYMPDETECTED) and self.variant_immune[c.variant] == False: + elif c.is_contagious() and (c.stage == Stage.ASYMPTOMATIC or c.stage == Stage.ASYMPDETECTED) and self.agent_data.variant_immune[c.variant] == False: c.add_contact_trace(self) - if self.isolated and bernoulli.rvs(1 - self.model.prob_isolation_effective): - self.isolated_but_inefficient = True + if self.agent_data.isolated and bernoulli_rvs(1 - self.model.model_data.prob_isolation_effective): + self.agent_data.isolated_but_inefficient = True infected_contact = 2 variant = c.variant else: infected_contact = 2 variant = c.variant - current_prob = self.prob_contagion * self.model.variant_data_list[variant]["Contagtion_Multiplier"] - if self.vaccinated: - current_prob = current_prob * self.safetymultiplier + current_prob = self.agent_data.prob_contagion * self.model.model_data.variant_data_list[variant]["Contagtion_Multiplier"] + if self.agent_data.vaccinated: + current_prob = current_prob * self.agent_data.safetymultiplier if infected_contact == 2: current_prob = current_prob * 0.42 if infected_contact > 0: - if self.isolated: - if bernoulli.rvs(current_prob) and not (bernoulli.rvs(self.model.prob_isolation_effective)): + if self.agent_data.isolated: + if bernoulli_rvs(current_prob) and not (bernoulli_rvs(self.model.model_data.prob_isolation_effective)): self.stage = Stage.EXPOSED - self.variant = variant + self.agent_data.variant = variant else: - if bernoulli.rvs(current_prob): + if bernoulli_rvs(current_prob): # Added vaccination account after being exposed to determine exposure. self.stage = Stage.EXPOSED - self.variant = variant + self.agent_data.variant = variant self.move() elif self.stage == Stage.DECEASED: - self.cumul_private_value = self.cumul_private_value + \ - self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.DECEASED] - self.cumul_public_value = self.cumul_public_value + \ - self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.DECEASED] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.DECEASED] + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.DECEASED] else: # If we are here, there is a problem sys.exit("Unknown stage: aborting.") + + #Insert a new trace into the database (AgentDataClass) + id = str(uuid.uuid4()) + agent_params = [( + id, + self.agent_data.age_group.value, + self.agent_data.sex_group.value, + self.agent_data.vaccine_willingness, + self.agent_data.incubation_time, + self.agent_data.dwelling_time, + self.agent_data.recovery_time, + self.agent_data.prob_contagion, + self.agent_data.mortality_value, + self.agent_data.severity_value, + self.agent_data.curr_dwelling, + self.agent_data.curr_incubation, + self.agent_data.curr_recovery, + self.agent_data.curr_asymptomatic, + self.agent_data.isolated, + self.agent_data.isolated_but_inefficient, + self.agent_data.test_chance, + self.agent_data.in_isolation, + self.agent_data.in_distancing, + self.agent_data.in_testing, + self.agent_data.astep, + self.agent_data.tested, + self.agent_data.occupying_bed, + self.agent_data.cumul_private_value, + self.agent_data.cumul_public_value, + self.agent_data.employed, + self.agent_data.tested_traced, + self.agent_data.tracing_delay, + self.agent_data.tracing_counter, + self.agent_data.vaccinated, + self.agent_data.safetymultiplier, + self.agent_data.current_effectiveness, + self.agent_data.vaccination_day, + self.agent_data.vaccine_count, + self.agent_data.dosage_eligible, + self.agent_data.fully_vaccinated, + self.agent_data.variant + )] + + self.model.db.insert_agent(agent_params) + self.model.db.commit() + self.astep = self.astep + 1 def move(self): # If dwelling has not been exhausted, do not move - if self.curr_dwelling > 0: - self.curr_dwelling = self.curr_dwelling - 1 + if self.agent_data.curr_dwelling > 0: + self.agent_data.curr_dwelling = self.agent_data.curr_dwelling - 1 # If dwelling has been exhausted, move and replenish the dwell else: @@ -716,7 +745,7 @@ def move(self): new_position = self.random.choice(possible_steps) self.model.grid.move_agent(self, new_position) - self.curr_dwelling = poisson.rvs(self.model.avg_dwell) + self.agent_data.curr_dwelling = poisson_rvs(self.model.model_data.avg_dwell) ######################################## @@ -725,17 +754,17 @@ def compute_variant_stage(model, variant, stage): count = 0 for agent in model.schedule.agents: if stage == Stage.SUSCEPTIBLE: - if agent.variant == variant: + if agent.agent_data.variant == variant: count += 1 else: - if agent.stage == stage and agent.variant == variant: + if agent.stage == stage and agent.agent_data.variant == variant: count += 1 return count def compute_vaccinated_stage(model, stage): count = 0 for agent in model.schedule.agents: - if agent.stage == stage and agent.vaccinated == True: + if agent.stage == stage and agent.agent_data.vaccinated == True: count += count vaccinated_count = compute_vaccinated_count(model) if vaccinated_count == 0: @@ -757,14 +786,14 @@ def count_type(model, stage): def compute_isolated(model): count = 0 for agent in model.schedule.agents: - if agent.isolated: + if agent.agent_data.isolated: count = count + 1 return count def compute_employed(model): count = 0 for agent in model.schedule.agents: - if agent.employed: + if agent.agent_data.employed: count = count + 1 return count @@ -773,7 +802,7 @@ def compute_unemployed(model): count = 0 for agent in model.schedule.agents: - if not(agent.employed): + if not(agent.agent_data.employed): count = count + 1 return count @@ -790,16 +819,16 @@ def compute_stepno(model): def compute_cumul_private_value(model): value = 0 for agent in model.schedule.agents: - value = value + agent.cumul_private_value - return np.sign(value)*np.power(np.abs(value), model.alpha_private)/model.num_agents + value = value + agent.agent_data.cumul_private_value + return np.sign(value)*np.power(np.abs(value), model.model_data.alpha_private)/model.num_agents def compute_cumul_public_value(model): value = 0 for agent in model.schedule.agents: - value = value + agent.cumul_public_value + value = value + agent.agent_data.cumul_public_value - return np.sign(value)*np.power(np.abs(value), model.alpha_public)/model.num_agents + return np.sign(value)*np.power(np.abs(value), model.model_data.alpha_public)/model.num_agents # Changed the method for calculating the test cost. This will occur in more linear time, @@ -807,19 +836,19 @@ def compute_cumul_public_value(model): # will change testing to be based on necessity along with the vaccine. def compute_cumul_testing_cost(model): - return model.cumul_test_cost + return model.model_data.cumul_test_cost def compute_cumul_vaccination_cost(model): - return model.cumul_vaccine_cost + return model.model_data.cumul_vaccine_cost def compute_total_cost(model): - return model.cumul_test_cost + model.cumul_vaccine_cost + return model.model_data.cumul_test_cost + model.model_data.cumul_vaccine_cost def compute_tested(model): tested = 0 for agent in model.schedule.agents: - if agent.tested: + if agent.agent_data.tested: tested = tested + 1 return tested @@ -828,7 +857,7 @@ def compute_tested(model): def compute_vaccinated(model): vaccinated_count = 0 for agent in model.schedule.agents: - if agent.vaccinated: + if agent.agent_data.vaccinated: vaccinated_count = vaccinated_count + 1 return vaccinated_count @@ -837,28 +866,28 @@ def compute_vaccinated(model): def compute_vaccinated_count(model): vaccinated_count = 0 for agent in model.schedule.agents: - if agent.vaccinated: + if agent.agent_data.vaccinated: vaccinated_count = vaccinated_count + 1 return vaccinated_count def compute_vaccinated_1(model): vaccinated_count = 0 for agent in model.schedule.agents: - if agent.vaccine_count == 1: + if agent.agent_data.vaccine_count == 1: vaccinated_count = vaccinated_count + 1 return vaccinated_count def compute_vaccinated_2(model): vaccinated_count = 0 for agent in model.schedule.agents: - if agent.vaccine_count == 2: + if agent.agent_data.vaccine_count == 2: vaccinated_count = vaccinated_count + 1 return vaccinated_count def compute_willing_agents(model): count = 0 for agent in model.schedule.agents: - if agent.vaccine_willingness: + if agent.agent_data.vaccine_willingness: count = count + 1 return count @@ -867,7 +896,7 @@ def compute_willing_agents(model): def compute_vaccinated_in_group_count(model,agegroup): vaccinated_count = 0 for agent in model.schedule.agents: - if (agent.vaccinated) and (agent.age_group == agegroup): + if (agent.agent_data.vaccinated) and (agent.agent_data.age_group == agegroup): vaccinated_count = vaccinated_count + 1 return vaccinated_count @@ -876,7 +905,7 @@ def compute_vaccinated_in_group_count(model,agegroup): def compute_vaccinated_in_group(model,agegroup): vaccinated_count = 0 for agent in model.schedule.agents: - if (agent.vaccinated) and (agent.age_group == agegroup): + if (agent.agent_data.vaccinated) and (agent.agent_data.age_group == agegroup): vaccinated_count = vaccinated_count + 1 return vaccinated_count @@ -884,7 +913,7 @@ def compute_vaccinated_in_group(model,agegroup): def compute_fully_vaccinated_in_group(model,agegroup): vaccinated_count = 0 for agent in model.schedule.agents: - if (agent.fully_vaccinated) and (agent.age_group == agegroup): + if (agent.agent_data.fully_vaccinated) and (agent.agent_data.age_group == agegroup): vaccinated_count = vaccinated_count + 1 return vaccinated_count @@ -892,7 +921,7 @@ def compute_fully_vaccinated_in_group(model,agegroup): def compute_vaccinated_in_group_percent_vaccine_count(model, agegroup, count): vaccinated_count = 0 for agent in model.schedule.agents: - if (agent.vaccine_count == count) and (agent.age_group == agegroup): + if (agent.agent_data.vaccine_count == count) and (agent.agent_data.age_group == agegroup): vaccinated_count = vaccinated_count + 1 return vaccinated_count @@ -901,9 +930,9 @@ def cumul_effectiveness_per_group_vaccinated(model,agegroup): vaccinated_count = 0 effectiveness = 0 for agent in model.schedule.agents: - if (agent.age_group == agegroup and agent.vaccinated == True): + if (agent.agent_data.age_group == agegroup and agent.agent_data.vaccinated == True): vaccinated_count = vaccinated_count + 1 - effectiveness += agent.safetymultiplier + effectiveness += agent.agent_data.safetymultiplier if (vaccinated_count > 0): return 1-(effectiveness / vaccinated_count) else: @@ -913,9 +942,9 @@ def cumul_effectiveness_per_group(model,agegroup): agent_count = 0 effectiveness = 0 for agent in model.schedule.agents: - if (agent.age_group == agegroup): + if (agent.agent_data.age_group == agegroup): agent_count = agent_count + 1 - effectiveness += agent.safetymultiplier + effectiveness += agent.agent_data.safetymultiplier if (agent_count > 0): return 1-(effectiveness / agent_count) else: @@ -924,46 +953,61 @@ def cumul_effectiveness_per_group(model,agegroup): def compute_age_group_count(model,agegroup): count = 0 for agent in model.schedule.agents: - if agent.age_group == agegroup: + if agent.agent_data.age_group == agegroup: count = count + 1 return count def compute_eligible_age_group_count(model,agegroup): count = 0 for agent in model.schedule.agents: - if (agent.age_group == agegroup) and (agent.stage == Stage.SUSCEPTIBLE or agent.stage == Stage.EXPOSED or agent.stage == Stage.ASYMPTOMATIC) and agent.dosage_eligible and agent.vaccine_willingness: + if (agent.agent_data.age_group == agegroup) and (agent.agent_data.stage == Stage.SUSCEPTIBLE or agent.agent_data.stage == Stage.EXPOSED or agent.agent_data.stage == Stage.ASYMPTOMATIC) and agent.agent_data.dosage_eligible and agent.agent_data.vaccine_willingness: count = count + 1 return count def update_vaccination_stage(model): - initial_stage = model.vaccination_stage - if compute_eligible_age_group_count(model, AgeGroup.C80toXX) < 1: - model.vaccination_stage = VaccinationStage.C70to79 - if compute_eligible_age_group_count(model, AgeGroup.C70to79) < 1: - model.vaccination_stage = VaccinationStage.C60to69 - if compute_eligible_age_group_count(model, AgeGroup.C60to69) < 1: - model.vaccination_stage = VaccinationStage.C50to59 - if compute_eligible_age_group_count(model, AgeGroup.C50to59) < 1: - model.vaccination_stage = VaccinationStage.C40to49 - if compute_eligible_age_group_count(model, AgeGroup.C40to49) < 1: - model.vaccination_stage = VaccinationStage.C30to39 - if compute_eligible_age_group_count(model, AgeGroup.C30to39) < 1: - model.vaccination_stage = VaccinationStage.C20to29 - if compute_eligible_age_group_count(model, AgeGroup.C20to29) < 1: - model.vaccination_stage = VaccinationStage.C10to19 - if compute_eligible_age_group_count(model, AgeGroup.C10to19) < 1: - model.vaccination_stage = VaccinationStage.C00to09 - else: - model.vaccination_stage = VaccinationStage.C80toXX - if initial_stage != model.vaccination_stage: - print(f"Vaccination stage is now {model.vaccination_stage}") + initial_stage = model.model_data.vaccination_stage + # if compute_eligible_age_group_count(model, AgeGroup.C80toXX) < 1: + # model.model_data.vaccination_stage = VaccinationStage.C70to79 + # if compute_eligible_age_group_count(model, AgeGroup.C70to79) < 1: + # model.model_data.vaccination_stage = VaccinationStage.C60to69 + # if compute_eligible_age_group_count(model, AgeGroup.C60to69) < 1: + # model.model_data.vaccination_stage = VaccinationStage.C50to59 + # if compute_eligible_age_group_count(model, AgeGroup.C50to59) < 1: + # model.model_data.vaccination_stage = VaccinationStage.C40to49 + # if compute_eligible_age_group_count(model, AgeGroup.C40to49) < 1: + # model.model_data.vaccination_stage = VaccinationStage.C30to39 + # if compute_eligible_age_group_count(model, AgeGroup.C30to39) < 1: + # model.model_data.vaccination_stage = VaccinationStage.C20to29 + # if compute_eligible_age_group_count(model, AgeGroup.C20to29) < 1: + # model.model_data.vaccination_stage = VaccinationStage.C10to19 + # if compute_eligible_age_group_count(model, AgeGroup.C10to19) < 1: + # model.model_data.vaccination_stage = VaccinationStage.C00to09 + # else: + # model.model_data.vaccination_stage = VaccinationStage.C80toXX + + eligible_age_group_dict = {} + eligible_age_group_dict[compute_eligible_age_group_count(model, AgeGroup.C80toXX)] = VaccinationStage.C70to79 + eligible_age_group_dict[compute_eligible_age_group_count(model, AgeGroup.C70to79)] = VaccinationStage.C60to69 + eligible_age_group_dict[compute_eligible_age_group_count(model, AgeGroup.C60to69)] = VaccinationStage.C50to59 + eligible_age_group_dict[compute_eligible_age_group_count(model, AgeGroup.C50to59)] = VaccinationStage.C40to49 + eligible_age_group_dict[compute_eligible_age_group_count(model, AgeGroup.C40to49)] = VaccinationStage.C30to39 + eligible_age_group_dict[compute_eligible_age_group_count(model, AgeGroup.C30to39)] = VaccinationStage.C20to29 + eligible_age_group_dict[compute_eligible_age_group_count(model, AgeGroup.C10to19)] = VaccinationStage.C00to09 + + model.model_data.vaccination_stage = VaccinationStage.C80toXX + for key,value in sorted(eligible_age_group_dict.items(), reverse=True): + if (key < 1): + model.model_data.vaccination_stage = value + + if initial_stage != model.model_data.vaccination_stage: + print(f"Vaccination stage is now {model.model_data.vaccination_stage}") def compute_willing_group_count(model, agegroup): count = 0 for agent in model.schedule.agents: - if(agent.vaccine_willingness): + if(agent.agent_data.vaccine_willingness): count += 1 return count @@ -971,14 +1015,14 @@ def compute_traced(model): tested = 0 for agent in model.schedule.agents: - if agent.tested_traced: + if agent.agent_data.tested_traced: tested = tested + 1 return tested def compute_total_processor_usage(model): - if model.stepno % model.dwell_15_day == 0: + if model.stepno % model.model_data.dwell_15_day == 0: processes = psu.cpu_percent(1, True) process_count = 0 for idx, process in enumerate(processes): @@ -989,7 +1033,7 @@ def compute_total_processor_usage(model): return 0 def compute_processor_usage(model, processoridx): - if model.stepno % model.dwell_15_day == 0: + if model.stepno % model.model_data.dwell_15_day == 0: processes = psu.cpu_percent(1, True) for idx, process in enumerate(processes): if (idx == processoridx): @@ -1013,17 +1057,17 @@ def compute_eff_reprod_number(model): for agent in model.schedule.agents: if agent.stage == Stage.EXPOSED: exposed = exposed + 1 - exp_time = exp_time + agent.incubation_time - prob_contagion = agent.prob_contagion + exp_time = exp_time + agent.agent_data.incubation_time + prob_contagion = agent.agent_data.prob_contagion elif agent.stage == Stage.SYMPDETECTED: # NOTE: this part needs to be adapted to model hospital transmission in further detail symptomatics = symptomatics + 1 - sympt_time = sympt_time + agent.incubation_time - prob_contagion = agent.prob_contagion + sympt_time = sympt_time + agent.agent_data.incubation_time + prob_contagion = agent.agent_data.prob_contagion elif agent.stage == Stage.ASYMPTOMATIC: asymptomatics = asymptomatics + 1 - asympt_time = asympt_time + agent.incubation_time + agent.recovery_time - prob_contagion = agent.prob_contagion + asympt_time = asympt_time + agent.agent_data.incubation_time + agent.agent_data.recovery_time + prob_contagion = agent.agent_data.prob_contagion else: continue @@ -1047,13 +1091,13 @@ def compute_eff_reprod_number(model): infectious_period = 0 avg_contacts = compute_contacts(model) - return model.kmob * model.repscaling * prob_contagion * avg_contacts * infectious_period + return model.model_data.kmob * model.model_data.repscaling * prob_contagion * avg_contacts * infectious_period def compute_num_agents(model): return model.num_agents def compute_vaccine_count(model): - return model.vaccine_count + return model.model_data.vaccine_count def compute_datacollection_time(model): return model.datacollection_time @@ -1062,11 +1106,10 @@ def compute_step_time(model): return model.step_time def compute_generally_infected(model): - return model.generally_infected + return model.model_data.generally_infected def compute_fully_vaccinated_count(model): - return model.fully_vaccinated_count - + return model.model_data.fully_vaccinated_count @@ -1080,135 +1123,217 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag day_distancing_start, days_distancing_lasts, proportion_detected, day_testing_start, days_testing_lasts, new_agent_proportion, new_agent_start, new_agent_lasts, new_agent_age_mean, new_agent_prop_infected, day_tracing_start, days_tracing_lasts, stage_value_matrix, test_cost, alpha_private, alpha_public, proportion_beds_pop, day_vaccination_begin, - day_vaccination_end, effective_period, effectiveness, distribution_rate, cost_per_vaccine, vaccination_percent, variant_data, dummy=0): - print("Made_it_here") + day_vaccination_end, effective_period, effectiveness, distribution_rate, cost_per_vaccine, vaccination_percent, variant_data, + # policy_data, + location_type, location_spec, + db, dummy=0): + + print("Made it to the model") self.running = True self.num_agents = num_agents self.grid = MultiGrid(width, height, True) self.schedule = RandomActivation(self) - self.age_mortality = age_mortality - self.sex_mortality = sex_mortality - self.age_distribution = age_distribution - self.sex_distribution = sex_distribution - self.stage_value_dist = stage_value_matrix - self.test_cost = test_cost self.stepno = 0 - self.alpha_private = alpha_private - self.alpha_public = alpha_public - self.day_vaccination_begin = day_vaccination_begin - self.day_vaccination_end = day_vaccination_end - self.effective_period = effective_period - self.effectiveness = effectiveness - self.distribution_rate = distribution_rate - self.vaccine_count = 0 - self.vaccinated_count = 0 - self.fully_vaccinated_count = 0 - self.bed_count = 0 - self.prop_initial_infected = prop_initial_infected - # temporary vaccination chance portion, will exclude when vaccination process is more clear. - self.vaccination_chance = distribution_rate/num_agents - self.vaccination_stage = VaccinationStage.C80toXX - self.vaccine_cost = cost_per_vaccine - self.cumul_vaccine_cost = 0 - self.cumul_test_cost = 0 - self.total_costs = 0 self.datacollection_time = 0 self.step_time = 0 - self.generally_infected = 0 - self.vaccination_percent = vaccination_percent - # Keeps track of how many doses of the vaccine are required - self.vaccine_dosage = 2 - self.effectiveness_per_dosage = self.effectiveness/self.vaccine_dosage - print("Made it here") - if self.vaccination_chance > 1: - print("INVALID VACCINATION CHANCE") - self.vaccination_chance = 0.5 - - self.variant_count = 0 - self.variant_data_list = {} - for variant in variant_data: - self.variant_data_list[variant["Name"]] = {} + self.db = db + + dwell_15_day = 96 + vaccine_dosage = 2 + repscaling = 1 + testing_start = day_testing_start* dwell_15_day + tracing_start = day_tracing_start* dwell_15_day + isolation_start = day_start_isolation*dwell_15_day + distancing_start = day_distancing_start*dwell_15_day + new_agent_start=new_agent_start*dwell_15_day + max_bed_available = num_agents * proportion_beds_pop + + self.dwell_time_at_locations = {} + + # load locations + if location_type == "named": + for x,y in zip(range(self.grid.width), range(self.grid.height)): + self.dwell_time_at_locations[(x, y)] = poisson_rvs(self.model.model_data.avg_dwell) + else: + # given distribution + location_probs = location_spec["proportions"] + location_dwell_time = location_probs["dwell"] + + for x, y in zip(range(self.grid.width), range(self.grid.height)): + random_location = random.choices(list(location_probs.keys(), weights=location_probs.values(), k=1))[0] + self.dwell_time_at_locations[(x, y)] = location_dwell_time[random_location] + + + self.model_data = ModelDataClass ( + age_mortality=age_mortality, + sex_mortality=sex_mortality, + age_distribution=age_distribution, + sex_distribution=sex_distribution, + stage_value_dist=stage_value_matrix, + test_cost=test_cost, + alpha_private=alpha_private, + alpha_public=alpha_public, + fully_vaccinated_count=0, + prop_initial_infected=prop_initial_infected, + generally_infected=0, + cumul_vaccine_cost=0, + cumul_test_cost=0, + total_costs=0, + vaccination_chance=distribution_rate/num_agents, + vaccination_stage=VaccinationStage.C80toXX, + vaccine_cost=cost_per_vaccine, + day_vaccination_begin=day_vaccination_begin, + day_vaccination_end=day_vaccination_end, + effective_period=effective_period, + effectiveness=effectiveness, + distribution_rate=distribution_rate, + vaccine_count=0, + vaccinated_count=0, + vaccinated_percent=vaccination_percent, + vaccine_dosage=vaccine_dosage, + effectiveness_per_dosage=effectiveness/vaccine_dosage, + variant_data_list={}, + agent_parameter_names=[], + dwell_15_day=dwell_15_day, + avg_dwell=4, + avg_incubation=int(round(avg_incubation_time * dwell_15_day)), + repscaling=repscaling, + prob_contagion_base=prob_contagion / repscaling, + kmob=kmob, + rate_inbound=rate_inbound/dwell_15_day, + prob_contagion_places=0.001, + prob_asymptomatic=proportion_asymptomatic, + avg_recovery=avg_recovery_time * dwell_15_day, + testing_rate=proportion_detected/(days_testing_lasts * dwell_15_day), + testing_start=testing_start, + testing_end=testing_start + days_testing_lasts* dwell_15_day, + tracing_start=tracing_start, + tracing_end=tracing_start + days_tracing_lasts* dwell_15_day, + tracing_now=False, + isolation_rate=proportion_isolated, + isolation_start=isolation_start, + isolation_end=isolation_start + days_isolation_lasts*dwell_15_day, + after_isolation=after_isolation, + prob_isolation_effective=prob_isolation_effective, + distancing=social_distance, + distancing_start=distancing_start, + distancing_end=distancing_start + days_distancing_lasts*dwell_15_day, + new_agent_num=int(new_agent_proportion * self.num_agents), + new_agent_start=new_agent_start, + new_agent_end=new_agent_start + new_agent_lasts*dwell_15_day, + new_agent_age_mean=new_agent_age_mean, + new_agent_prop_infected=new_agent_prop_infected, + vaccination_start=day_vaccination_begin * dwell_15_day, + vaccination_end=day_vaccination_end * dwell_15_day, + vaccination_now=False, + variant_start_times={}, + variant_start={}, + prob_severe=proportion_severe, + max_bed_available = max_bed_available, + bed_count=max_bed_available + ) - for variant in variant_data: + + self.pol_handler = PolicyHandler() + + #Read + #pol_handler.parse_all_policies(policy_data) + + # Get default policies + # pol_handler.set_defaults(self.model_data) + + print("model finished") + # initial commit + + # insert a model into the database + myid = str(uuid.uuid4()) + model_params = [( + myid, + self.model_data.test_cost, + self.model_data.alpha_private, + self.model_data.alpha_public, + self.model_data.fully_vaccinated_count, + self.model_data.prop_initial_infected, + self.model_data.generally_infected, + self.model_data.cumul_vaccine_cost, + self.model_data.cumul_test_cost, + self.model_data.total_costs, + self.model_data.vaccination_chance, # + self.model_data.vaccination_stage.value, + self.model_data.vaccine_cost, # + self.model_data.day_vaccination_begin, + self.model_data.day_vaccination_end, + self.model_data.effective_period, + self.model_data.effectiveness, + self.model_data.distribution_rate, + self.model_data.vaccine_count, # + self.model_data.vaccinated_count, # + self.model_data.vaccinated_percent, # + self.model_data.vaccine_dosage, + self.model_data.effectiveness_per_dosage, + self.model_data.dwell_15_day, + self.model_data.avg_dwell, + self.model_data.avg_incubation, + self.model_data.repscaling, + self.model_data.prob_contagion_base, + self.model_data.kmob, + self.model_data.rate_inbound, + self.model_data.prob_contagion_places, + self.model_data.prob_asymptomatic, + self.model_data.avg_recovery, + self.model_data.testing_rate, # + self.model_data.testing_start, + self.model_data.testing_end, + self.model_data.tracing_start, + self.model_data.tracing_end, + self.model_data.tracing_now, + self.model_data.isolation_rate, # + self.model_data.isolation_start, + self.model_data.isolation_end, + self.model_data.after_isolation, + self.model_data.prob_isolation_effective, # + self.model_data.distancing, # + self.model_data.distancing_start, + self.model_data.distancing_end, + self.model_data.new_agent_num, + self.model_data.new_agent_start, + self.model_data.new_agent_end, + self.model_data.new_agent_age_mean, + self.model_data.new_agent_prop_infected, + self.model_data.vaccination_start, + self.model_data.vaccination_end, + self.model_data.vaccination_now, + self.model_data.prob_severe, + self.model_data.max_bed_available, + self.model_data.bed_count + )] + + self.db.insert_model(model_params) + self.db.commit() - self.variant_data_list[variant["Name"]]["Name"] = variant["Name"] - self.variant_data_list[variant["Name"]]["Appearance"] = variant["Appearance"] - self.variant_data_list[variant["Name"]]["Contagtion_Multiplier"] = variant["Contagtion_Multiplier"] - self.variant_data_list[variant["Name"]]["Vaccine_Multiplier"] = variant["Vaccine_Multiplier"] - self.variant_data_list[variant["Name"]]["Asymtpomatic_Multiplier"] = variant["Asymtpomatic_Multiplier"] - self.variant_data_list[variant["Name"]]["Mortality_Multiplier"] = variant["Mortality_Multiplier"] - self.variant_data_list[variant["Name"]]["Reinfection"] = variant["Reinfection"] - - # Number of 15 minute dwelling times per day - self.dwell_15_day = 96 - - # Average dwelling units - self.avg_dwell = 4 - - # The average incubation period is 5 days, which can be changed - self.avg_incubation = int(round(avg_incubation_time * self.dwell_15_day)) - - # Probability of contagion after exposure in the same cell - # Presupposes a person centered on a 1.8 meter radius square. - # We use a proxy value to account for social distancing. - # Representativeness modifies the probability of contagion by the scaling factor - if repscaling < 2: - self.repscaling = 1 - else: - self.repscaling = (np.log(repscaling)/np.log(1.96587)) - - self.prob_contagion_base = prob_contagion / self.repscaling - - # Mobility constant for geographic rescaling - self.kmob = kmob - - # Proportion of daily incoming infected people from other places - self.rate_inbound = rate_inbound/self.dwell_15_day - - # Probability of contagion due to residual droplets: TODO - self.prob_contagion_places = 0.001 - - # Probability of being asymptomatic, contagious - # and only detectable by testing - self.prob_asymptomatic = proportion_asymptomatic - - # Average recovery time - self.avg_recovery = avg_recovery_time * self.dwell_15_day - - # Proportion of detection. We use the rate as reference and - # activate testing at the rate and specified dates - self.testing_rate = proportion_detected/(days_testing_lasts * self.dwell_15_day) - self.testing_start = day_testing_start* self.dwell_15_day - self.testing_end = self.testing_start + days_testing_lasts*self.dwell_15_day - - # We need an additional variable to activate and inactivate automatic contact tracing - self.tracing_start = day_tracing_start* self.dwell_15_day - self.tracing_end = self.tracing_start + days_tracing_lasts*self.dwell_15_day - self.tracing_now = False - - # Same for isolation rate - self.isolation_rate = proportion_isolated - self.isolation_start = day_start_isolation*self.dwell_15_day - self.isolation_end = self.isolation_start + days_isolation_lasts*self.dwell_15_day - self.after_isolation = after_isolation - self.prob_isolation_effective = prob_isolation_effective - - # Same for social distancing - self.distancing = social_distance - self.distancing_start = day_distancing_start*self.dwell_15_day - self.distancing_end = self.distancing_start + days_distancing_lasts*self.dwell_15_day - - # Introduction of new agents after a specific time - self.new_agent_num = int(new_agent_proportion * self.num_agents) - self.new_agent_start = new_agent_start*self.dwell_15_day - self.new_agent_end = self.new_agent_start + new_agent_lasts*self.dwell_15_day - self.new_agent_age_mean = new_agent_age_mean - self.new_agent_prop_infected = new_agent_prop_infected - - #Code for vaccination - self.vaccination_start = day_vaccination_begin * self.dwell_15_day - self.vaccination_end = day_vaccination_end * self.dwell_15_day - self.vaccinating_now = False + for variant in variant_data: + self.model_data.variant_data_list[variant["Name"]] = {} + self.model_data.variant_data_list[variant["Name"]]["Name"] = variant["Name"] + self.model_data.variant_data_list[variant["Name"]]["Appearance"] = variant["Appearance"] + self.model_data.variant_data_list[variant["Name"]]["Contagtion_Multiplier"] = variant["Contagtion_Multiplier"] + self.model_data.variant_data_list[variant["Name"]]["Vaccine_Multiplier"] = variant["Vaccine_Multiplier"] + self.model_data.variant_data_list[variant["Name"]]["Asymtpomatic_Multiplier"] = variant["Asymtpomatic_Multiplier"] + self.model_data.variant_data_list[variant["Name"]]["Mortality_Multiplier"] = variant["Mortality_Multiplier"] + self.model_data.variant_data_list[variant["Name"]]["Reinfection"] = variant["Reinfection"] + + # All parameter names of concern for agents. Must be kept in this form as a standard for loading into agent data. Add a new variable name before pos. + #TODO (optional) make the production of the agent more rigourous instead of the brute force solution you have up there. + self.agent_parameter_names = ['unique_id', 'stage', 'age_group', 'sex_group', 'vaccine_willingness', + 'incubation_time', 'dwelling_time', 'recovery_time', 'prob_contagion', + 'mortality_value', 'severity_value', 'curr_dwelling', 'curr_incubation', + 'curr_recovery', 'curr_asymptomatic', 'isolated', 'isolated_but_inefficient', + 'test_chance', 'in_isolation', 'in_distancing', 'in_testing', 'astep', 'tested', + 'occupying_bed', 'cumul_private_value', 'cumul_public_value', 'employed', + 'tested_traced', 'contacts', 'tracing_delay', 'tracing_counter', 'vaccinated', + 'safetymultiplier', 'current_effectiveness', 'vaccination_day', 'vaccine_count', + 'dosage_eligible', 'fully_vaccinated', 'variant', 'variant_immune', 'pos'] + + self.model_reporters = {} # Closing of various businesses # TODO: at the moment, we assume that closing businesses decreases the dwell time. # A more proper implementation would a) use a power law distribution for dwell times @@ -1218,33 +1343,39 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag # of those interactions self.variant_start_times = {} self.variant_start = {} - for key in self.variant_data_list: - self.variant_start_times[key] = self.variant_data_list[key]["Appearance"] * self.dwell_15_day + + # A dictionary to count the dwell time of an agent at a location; + # key is (agent, x, y) and value is count of dwell time + # self.dwell_time_at_locations = {} + # positions = [(x, y) for x, y in zip(range(self.grid.width), range(self.grid.height))] + + # for i,j in positions: + # self.dwell_time_at_locations[(x, y)] = poisson_rvs(self.model.model_data.avg_dwell) + + for key in self.model_data.variant_data_list: + self.variant_start_times[key] = self.model_data.variant_data_list[key]["Appearance"] * self.model_data.dwell_15_day self.variant_start[key] = False print(key) - print(self.variant_data_list[key]) - print(self.variant_data_list[key]["Appearance"]) + print(self.model_data.variant_data_list[key]) + print(self.model_data.variant_data_list[key]["Appearance"]) # Now, a neat python trick: generate the spacing of entries and then build a map - times_list = list(np.linspace(self.new_agent_start, self.new_agent_end, self.new_agent_num, dtype=int)) + times_list = list(np.linspace(self.model_data.new_agent_start, self.model_data.new_agent_end, self.model_data.new_agent_num, dtype=int)) self.new_agent_time_map = {x:times_list.count(x) for x in times_list} - # Probability of severity - self.prob_severe = proportion_severe - - # Number of beds where saturation limit occurs - self.max_beds_available = self.num_agents * proportion_beds_pop - self.bed_count = self.max_beds_available + # We store a simulation specification in the database + # Commit + #print(self.model_data.vaccination_stage.value) # Create agents self.i = 0 - for ag in self.age_distribution: - for sg in self.sex_distribution: - r = self.age_distribution[ag]*self.sex_distribution[sg] + for ag in self.model_data.age_distribution: + for sg in self.model_data.sex_distribution: + r = self.model_data.age_distribution[ag]*self.model_data.sex_distribution[sg] num_agents = int(round(self.num_agents*r)) - mort = self.age_mortality[ag]*self.sex_mortality[sg] + mort = self.model_data.age_mortality[ag]*self.model_data.sex_mortality[sg] for k in range(num_agents): a = CovidAgent(self.i, ag, sg, mort, self) self.schedule.add(a) @@ -1262,6 +1393,7 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag age_vaccination_dict = {} + for age in AgeGroup: age_group_name = "Vaccinated " + str(age.name) age_vaccination_dict[age_group_name] = [compute_vaccinated_in_group, [self, age]] @@ -1300,6 +1432,7 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag agent_status_dict[stage_name] = [compute_stage, [self,stage]] + # This is part of the summary prices_dict = { "CumulPrivValue": compute_cumul_private_value, "CumulPublValue": compute_cumul_public_value, "CumulTestCost": compute_cumul_testing_cost, @@ -1311,6 +1444,8 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag "Cumul_Vaccine_Cost": compute_cumul_vaccination_cost, "Cumul_Cost": compute_total_cost } + + # This is also part of the summary model_reporters_dict = { "Step": compute_stepno, "N": compute_num_agents, @@ -1339,54 +1474,122 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag # Final step: infect an initial proportion of random agents num_init = int(self.num_agents * prop_initial_infected) + # Save all initial values of agents in the database + # Commit + + cumul_priv_value = compute_cumul_private_value(self) + cumul_publ_value = compute_cumul_public_value(self) + cumul_test_cost = compute_cumul_testing_cost(self) + rt = compute_eff_reprod_number(self) + employed = compute_employed(self) + unemployed = compute_unemployed(self) + tested = compute_tested(self) + traced = compute_traced(self) + cumul_vaccine_cost = compute_cumul_vaccination_cost(self) + cumul_cost =compute_total_cost(self) + step = compute_stepno(self) + n = compute_num_agents(self) + isolated = compute_isolated(self) + vaccinated = compute_vaccinated(self) + vaccines = compute_vaccine_count(self) + v = compute_vaccinated(self) + data_time = compute_datacollection_time(self) + step_time = compute_step_time(self) + generally_infected = compute_generally_infected(self) + fully_vaccinated = compute_generally_infected(self) + vaccine_1 = compute_vaccinated_1(self) + vaccine_2 = compute_vaccinated_2(self) + vaccine_willing = compute_willing_agents(self) + + + #Save all initial summaries into the database + # insert a summary into database + myid = str(uuid.uuid4()) + summary_params = [( + myid, + cumul_priv_value, + cumul_publ_value, + cumul_test_cost, + rt, + employed, + unemployed, + tested, + traced, + cumul_vaccine_cost, + cumul_cost, + step, + n, + isolated, + vaccinated, + vaccines, + v, + data_time, + step_time, + generally_infected, + fully_vaccinated, + vaccine_1, + vaccine_2, + vaccine_willing + )] + + self.db.insert_summary(summary_params) + self.db.commit() + + for a in self.schedule.agents: if num_init < 0: break else: a.stage = Stage.EXPOSED - self.generally_infected = self.generally_infected + 1 + self.model_data.generally_infected = self.model_data.generally_infected + 1 num_init = num_init - 1 def step(self): datacollectiontimeA = timeit.default_timer() self.datacollector.collect(self) + # summary datacollectiontimeB = timeit.default_timer() self.datacollection_time = datacollectiontimeB-datacollectiontimeA steptimeA = timeit.default_timer() - if self.stepno % self.dwell_15_day == 0: - print(f'Simulating day {self.stepno // self.dwell_15_day}') + if self.stepno % self.model_data.dwell_15_day == 0: + print(f'Simulating day {self.stepno // self.model_data.dwell_15_day}') #Addition for adding the daily amount of vaccines. - if self.vaccinating_now: - self.vaccine_count = self.vaccine_count + self.distribution_rate + if self.model_data.vaccination_now: + self.model_data.vaccine_count = self.model_data.vaccine_count + self.model_data.distribution_rate + + # Deactivate unnecessary policies once they run their course + self.policy_handler.reverse_dispatch(self, self.model_data) + # Use the policy handler to apply relevant policies + self.policy_handler.dispatch(self, self.model_data) # Activate contact tracing only if necessary and turn it off correspondingly at the end - if not(self.tracing_now) and (self.stepno >= self.tracing_start): - self.tracing_now = True + if not(self.model_data.tracing_now) and (self.stepno >= self.model_data.tracing_start): + self.model_data.tracing_now = True - if self.tracing_now and (self.stepno > self.tracing_end): - self.tracing_now = False + if self.model_data.tracing_now and (self.stepno > self.model_data.tracing_end): + self.model_data.tracing_now = False - if not (self.vaccinating_now) and (self.stepno >= self.vaccination_start): - self.vaccinating_now = True + if not (self.model_data.vaccination_now) and (self.stepno >= self.model_data.vaccination_start): + self.model_data.vaccinating_now = True - if self.vaccinating_now and (self.stepno > self.vaccination_end): - self.vaccinating_now = False + if self.model_data.vaccination_now and (self.stepno > self.model_data.vaccination_end): + self.model_data.vaccination_now = False - for variant in self.variant_start_times: + for variant in self.model_data.variant_start_times: #print(f"Made it here for variant {variant} stepnumber is {self.stepno} out of {self.variant_start_times[variant]}") - if not(self.variant_start[variant]) and (self.stepno > self.variant_start_times[variant]): - new_infection_count = int(self.num_agents*self.prop_initial_infected) - self.variant_start[variant] = True + if not(self.model_data.variant_start[variant]) and (self.stepno > self.model_data.variant_start_times[variant]): + new_infection_count = int(self.num_agents*self.model_data.prop_initial_infected) + self.model_data.variant_start[variant] = True print(f"Variant {variant} is set to True with cound {new_infection_count}") for _ in range(0,new_infection_count): print(f"Creating new variant {variant}") #Creates new agents that are infected with the variant ag = random.choice(list(AgeGroup)) sg = random.choice(list(SexGroup)) - mort = self.age_mortality[ag]*self.sex_mortality[sg] + mort = self.model_data.age_mortality[ag]*self.model_data.sex_mortality[sg] a = CovidAgent(self.i, ag, sg, mort, self) self.schedule.add(a) a.variant = variant @@ -1396,12 +1599,13 @@ def step(self): self.grid.place_agent(a, (x, y)) self.i = self.i + 1 self.num_agents = self.num_agents + 1 - self.generally_infected += 1 + self.model_data.generally_infected += 1 + # If new agents enter the population, create them - if (self.stepno >= self.new_agent_start) and (self.stepno < self.new_agent_end): + if (self.stepno >= self.model_data.new_agent_start) and (self.stepno < self.model_data.new_agent_end): # Check if the current step is in the new-agent time map if self.stepno in self.new_agent_time_map.keys(): # We repeat the following procedure as many times as the value stored in the map @@ -1413,19 +1617,19 @@ def step(self): arange = 0 while not(in_range): - arange = poisson.rvs(self.new_agent_age_mean) + arange = poisson_rvs(self.model_data.new_agent_age_mean) if arange in range(0, 9): in_range = True ag = AgeGroup(arange) sg = random.choice(list(SexGroup)) - mort = self.age_mortality[ag]*self.sex_mortality[sg] + mort = self.model_data.age_mortality[ag]*self.model_data.sex_mortality[sg] a = CovidAgent(self.i, ag, sg, mort, self) # Some will be infected - if bernoulli.rvs(self.new_agent_prop_infected): + if bernoulli_rvs(self.model_data.new_agent_prop_infected): a.stage = Stage.EXPOSED - self.generally_infected = self.generally_infected + 1 + self.model_data.generally_infected = self.model_data.generally_infected + 1 self.schedule.add(a) x = self.random.randrange(self.grid.width) @@ -1433,8 +1637,19 @@ def step(self): self.grid.place_agent(a, (x,y)) self.i = self.i + 1 self.num_agents = self.num_agents + 1 + self.dwell_time_at_locations[(x,y)] += a.agent_data.dwelling_time + self.schedule.step() steptimeB = timeit.default_timer() self.step_time = steptimeB - steptimeA + + # Commit (save first all the agent data in memory) + # Save the summaries + + # We now do a reverse dispatch: deactivate all policies that need to be deactivated + # + self.stepno = self.stepno + 1 + + diff --git a/covidmodelcheckpoint.py b/covidmodelcheckpoint.py index 38b08e9..efd59d5 100644 --- a/covidmodelcheckpoint.py +++ b/covidmodelcheckpoint.py @@ -22,6 +22,8 @@ import pandas as pd from functools import partial import types +from agent_data_class import AgentDataClass +from model_data_class import ModelDataClass class Stage(Enum): SUSCEPTIBLE = 1 @@ -33,7 +35,6 @@ class Stage(Enum): RECOVERED = 7 DECEASED = 8 - class AgeGroup(Enum): C00to09 = 0 C10to19 = 1 @@ -45,12 +46,10 @@ class AgeGroup(Enum): C70to79 = 7 C80toXX = 8 - class SexGroup(Enum): MALE = 1 FEMALE = 2 - class ValueGroup(Enum): PRIVATE = 1 PUBLIC = 2 @@ -66,121 +65,28 @@ class VaccinationStage(Enum): C70to79 = 7 C80toXX = 8 + class CovidAgent(Agent): """ An agent representing a potential covid case""" def __init__(self, model, parameters): super().__init__(parameters[0], model) + is_checkpoint = not(len(parameters)<5) + + #check if we run the model from start or from a file if len(parameters)<5: self.stage = Stage.SUSCEPTIBLE - self.age_group = parameters[1] - self.sex_group = parameters[2] - self.vaccine_willingness = bernoulli.rvs(self.model.vaccination_percent) - # These are fixed values associated with properties of individuals - self.incubation_time = poisson.rvs(model.avg_incubation) - self.dwelling_time = poisson.rvs(model.avg_dwell) - self.recovery_time = poisson.rvs(model.avg_recovery) - self.prob_contagion = self.model.prob_contagion_base - # Mortality in vulnerable population appears to be around day 2-3 - self.mortality_value = parameters[3] - # Severity appears to appear after day 5 - self.severity_value = model.prob_severe/(self.model.dwell_15_day*self.recovery_time) - self.curr_dwelling = 0 - self.curr_incubation = 0 - self.curr_recovery = 0 - self.curr_asymptomatic = 0 - # Isolation measures are set at the model step level - self.isolated = False - self.isolated_but_inefficient = False - # Contagion probability is local - self.test_chance = 0 - # Horrible hack for isolation step - self.in_isolation = False - self.in_distancing = False - self.in_testing = False - self.astep = 0 - self.tested = False - self.occupying_bed = False - # Economic assumptions - self.cumul_private_value = 0 - self.cumul_public_value = 0 - # Employment - self.employed = True - # Contact tracing: this is only available for symptomatic patients - self.tested_traced = False - # All agents - self.contacts = set() - # We assume it takes two full days - self.tracing_delay = 2*model.dwell_15_day - self.tracing_counter = 0 - #vaccination variables - self.vaccinated = False - self.safetymultiplier = 1 - self.current_effectiveness = 0 - self.vaccination_day = 0 - self.vaccine_count = 0 - self.dosage_eligible = True - self.fully_vaccinated = False - self.variant = "Standard" - self.variant_immune = {} - for variant in self.model.variant_data_list: - self.variant_immune[variant] = False else: self.stage = parameters[1] - self.age_group = parameters[2] - self.sex_group = parameters[3] - self.vaccine_willingness = parameters[4] - # These are fixed values associated with properties of individuals - self.incubation_time = parameters[5] - self.dwelling_time = parameters[6] - self.recovery_time = parameters[7] - self.prob_contagion = parameters[8] - # Mortality in vulnerable population appears to be around day 2-3 - self.mortality_value = parameters[9] - # Severity appears to appear after day 5 - self.severity_value = parameters[10] - self.curr_dwelling = parameters[11] - self.curr_incubation = parameters[12] - self.curr_recovery = parameters[13] - self.curr_asymptomatic = parameters[14] - # Isolation measures are set at the model step level - self.isolated = parameters[15] - self.isolated_but_inefficient = parameters[16] - # Contagion probability is local - self.test_chance = parameters[17] - # Horrible hack for isolation step - self.in_isolation = parameters[18] - self.in_distancing = parameters[19] - self.in_testing = parameters[20] - self.astep = parameters[21] - self.tested = parameters[22] - self.occupying_bed = parameters[23] - # Economic assumptions - self.cumul_private_value = parameters[24] - self.cumul_public_value = parameters[25] - # Employment - self.employed = parameters[26] - # Contact tracing: this is only available for symptomatic patients - self.tested_traced = parameters[27] - # All agents - self.contacts = parameters[28] - # We assume it takes two full days - self.tracing_delay = parameters[29] - self.tracing_counter = parameters[30] - # vaccination variables - self.vaccinated = parameters[31] - self.safetymultiplier = parameters[32] - self.current_effectiveness = parameters[33] - self.vaccination_day = parameters[34] - self.vaccine_count = parameters[35] - self.dosage_eligible = parameters[36] - self.fully_vaccinated = parameters[37] - self.variant = parameters[38] - self.variant_immune = parameters[39] + + # We start the model from time = 0 (first time it is run) + self.astep = 0 + # initialize the agent from AgentDataClass + self.agent_data = AgentDataClass(model, is_checkpoint, parameters) def alive(self): - print(f'{self.unique_id} {self.age_group} {self.sex_group} is alive') + print(f'{self.unique_id} {self.agent_data.age_group} {self.agent_data.sex_group} is alive') def is_contagious(self): return (self.stage == Stage.EXPOSED) or (self.stage == Stage.ASYMPTOMATIC) or (self.stage == Stage.SYMPDETECTED) @@ -195,9 +101,9 @@ def dmult(self): # of the distribution and must be further callibrated. mult = 1.0 - if self.model.distancing >= 1.5: + if self.model.model_data.distancing >= 1.5: k = 10 - mult = 1.0 - (1.0 / (1.0 + np.exp(k*(-(self.model.distancing - 1.5) + 0.5)))) + mult = 1.0 - (1.0 / (1.0 + np.exp(k*(-(self.model.model_data.distancing - 1.5) + 0.5)))) return mult @@ -208,7 +114,7 @@ def interactants(self): if (self.stage != Stage.DECEASED) and (self.stage != Stage.RECOVERED): for agent in self.model.grid.get_cell_list_contents([self.pos]): if agent.unique_id != self.unique_id: - if not(agent.isolated) or self.isolated_but_inefficient: + if not(agent.agent_data.isolated) or self.agent_data.isolated_but_inefficient: count = count + 1 return count @@ -217,9 +123,9 @@ def interactants(self): def test_contact_trace(self): # We may have an already tested but it had a posterior contact and became infected if self.stage == Stage.SUSCEPTIBLE: - self.tested_traced = True + self.agent_data.tested_traced = True elif self.stage == Stage.EXPOSED: - self.tested_traced = True + self.agent_data.tested_traced = True if bernoulli.rvs(self.model.prob_asymptomatic): self.stage = Stage.ASYMPDETECTED @@ -227,17 +133,17 @@ def test_contact_trace(self): self.stage = Stage.SYMPDETECTED elif self.stage == Stage.ASYMPTOMATIC: self.stage = Stage.ASYMPDETECTED - self.tested_traced = True + self.agent_data.tested_traced = True else: return def add_contact_trace(self, other): - if self.model.tracing_now: - self.contacts.add(other) + if self.model.model_data.tracing_now: + self.agent_data.contacts.add(other) #helper function that reveals if an agent is vaccinated def is_vaccinated(self): - return self.vaccinated + return self.agent_data.vaccinated #Vaccination decision process, prone to change to find the ideal method. @@ -245,7 +151,7 @@ def is_vaccinated(self): #For now implementing random vaccination. def general_vaccination_chance(self): - eligible_count = compute_age_group_count(self.model, self.age_group) + eligible_count = compute_age_group_count(self.model, self.agent_data.age_group) vaccination_chance = 1/eligible_count if self.stage == Stage.ASYMPTOMATIC or self.stage == Stage.SUSCEPTIBLE or self.stage == Stage.EXPOSED: if bernoulli.rvs(vaccination_chance): @@ -255,31 +161,31 @@ def general_vaccination_chance(self): def should_be_vaccinated(self): if self.general_vaccination_chance(): - if self.age_group == AgeGroup.C80toXX and self.model.vaccination_stage == VaccinationStage.C80toXX: + if self.agent_data.age_group == AgeGroup.C80toXX and self.model.model_data.vaccination_stage == VaccinationStage.C80toXX: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C70to79 and self.model.vaccination_stage == VaccinationStage.C70to79: + elif self.agent_data.age_group == AgeGroup.C70to79 and self.model.model_data.vaccination_stage == VaccinationStage.C70to79: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C60to69 and self.model.vaccination_stage == VaccinationStage.C60to69: + elif self.agent_data.age_group == AgeGroup.C60to69 and self.model.model_data.vaccination_stage == VaccinationStage.C60to69: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C50to59 and self.model.vaccination_stage == VaccinationStage.C50to59: + elif self.agent_data.age_group == AgeGroup.C50to59 and self.model.model_data.vaccination_stage == VaccinationStage.C50to59: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C40to49 and self.model.vaccination_stage == VaccinationStage.C40to49: + elif self.agent_data.age_group == AgeGroup.C40to49 and self.model.model_data.vaccination_stage == VaccinationStage.C40to49: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C30to39 and self.model.vaccination_stage == VaccinationStage.C30to39: + elif self.agent_data.age_group == AgeGroup.C30to39 and self.model.model_data.vaccination_stage == VaccinationStage.C30to39: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C20to29 and self.model.vaccination_stage == VaccinationStage.C20to29: + elif self.agent_data.age_group == AgeGroup.C20to29 and self.model.model_data.vaccination_stage == VaccinationStage.C20to29: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C10to19 and self.model.vaccination_stage == VaccinationStage.C10to19: + elif self.agent_data.age_group == AgeGroup.C10to19 and self.model.model_data.vaccination_stage == VaccinationStage.C10to19: update_vaccination_stage(self.model) return True - elif self.age_group == AgeGroup.C00to09 and self.model.vaccination_stage == VaccinationStage.C00to09: + elif self.agent_data.age_group == AgeGroup.C00to09 and self.model.model_data.vaccination_stage == VaccinationStage.C00to09: update_vaccination_stage(self.model) return True else : @@ -290,51 +196,51 @@ def should_be_vaccinated(self): def step(self): # We compute unemployment in general as a probability of 0.00018 per day. # In 60 days, this is equivalent to a probability of 1% unemployment filings. - if self.employed: - if self.isolated: - if bernoulli.rvs(32*0.00018/self.model.dwell_15_day): - self.employed = False + if self.agent_data.employed: + if self.agent_data.isolated: + if bernoulli.rvs(32*0.00018/self.model.model_data.dwell_15_day): + self.agent_data.employed = False else: - if bernoulli.rvs(8*0.00018/self.model.dwell_15_day): - self.employed = False + if bernoulli.rvs(8*0.00018/self.model.model_data.dwell_15_day): + self.agent_data.employed = False # We also compute the probability of re-employment, which is at least ten times # as smaller in a crisis. - if not(self.employed): - if bernoulli.rvs(0.000018/self.model.dwell_15_day): - self.employed = True + if not(self.agent_data.employed): + if bernoulli.rvs(0.000018/self.model.model_data.dwell_15_day): + self.agent_data.employed = True # Social distancing - if not(self.in_distancing) and (self.astep >= self.model.distancing_start): - self.prob_contagion = self.dmult() * self.model.prob_contagion_base - self.in_distancing = True + if not(self.agent_data.in_distancing) and (self.astep >= self.model.model_data.distancing_start): + self.agent_data.prob_contagion = self.dmult() * self.model.model_data.prob_contagion_base + self.agent_data.in_distancing = True - if self.in_distancing and (self.astep >= self.model.distancing_end): - self.prob_contagion = self.model.prob_contagion_base - self.in_distancing = False + if self.agent_data.in_distancing and (self.astep >= self.model.model_data.distancing_end): + self.agent_data.prob_contagion = self.model.model_data.prob_contagion_base + self.agent_data.in_distancing = False # Testing - if not(self.in_testing) and (self.astep >= self.model.testing_start): - self.test_chance = self.model.testing_rate - self.in_testing = True + if not(self.agent_data.in_testing) and (self.astep >= self.model.model_data.testing_start): + self.agent_data.test_chance = self.model.model_data.testing_rate + self.agent_data.in_testing = True - if self.in_testing and (self.astep >= self.model.testing_end): - self.test_chance = 0 - self.in_testing = False + if self.agent_data.in_testing and (self.astep >= self.model.model_data.testing_end): + self.agent_data.test_chance = 0 + self.agent_data.in_testing = False #Implementing the vaccine #Will process based on whether all older agents in an older group are vaccinated - if (not(self.vaccinated) or self.dosage_eligible) and self.model.vaccinating_now and (not(self.fully_vaccinated) and (self.vaccine_count < self.model.vaccine_dosage)): - if self.should_be_vaccinated() and self.model.vaccine_count > 0 and self.vaccine_willingness: + if (not(self.agent_data.vaccinated) or self.agent_data.dosage_eligible) and self.model.model_data.vaccination_now and (not(self.agent_data.fully_vaccinated) and (self.agent_data.vaccine_count < self.model.model_data.vaccine_dosage)): + if self.should_be_vaccinated() and self.model.model_data.vaccine_count > 0 and self.agent_data.vaccine_willingness: if not (bernoulli.rvs(0.1)): # Chance that someone doesnt show up for the vaccine/ vaccine expires. - self.vaccinated = True - self.vaccination_day = self.model.stepno - self.vaccine_count = self.vaccine_count + 1 - self.dosage_eligible = False - self.model.vaccine_count = self.model.vaccine_count - 1 - self.model.vaccinated_count = self.model.vaccinated_count + 1 + self.agent_data.vaccinated = True + self.agent_data.vaccination_day = self.model.stepno + self.agent_data.vaccine_count = self.agent_data.vaccine_count + 1 + self.agent_data.dosage_eligible = False + self.model.model_data.vaccine_count = self.model.model_data.vaccine_count - 1 + self.model.model_data.vaccinated_count = self.model.model_data.vaccinated_count + 1 else: other_agent = self.random.choice(self.model.schedule.agents) @@ -344,75 +250,67 @@ def step(self): other_agent.vaccination_day = self.model.stepno other_agent.vaccine_count = other_agent.vaccine_count +1 other_agent.dosage_eligible = False - self.model.vaccinated_count = self.model.vaccinated_count + 1 - self.model.vaccine_count = self.model.vaccine_count - 1 + self.model.model_data.vaccinated_count = self.model.model_data.vaccinated_count + 1 + self.model.model_data.vaccine_count = self.model.model_data.vaccine_count - 1 # Self isolation is tricker. We only isolate susceptibles, incubating and asymptomatics - if not(self.in_isolation): - if (self.astep >= self.model.isolation_start): + if not(self.agent_data.in_isolation): + if (self.astep >= self.model.model_data.isolation_start): if (self.stage == Stage.SUSCEPTIBLE) or (self.stage == Stage.EXPOSED) or \ (self.stage == Stage.ASYMPTOMATIC): - if bool(bernoulli.rvs(self.model.isolation_rate)): - self.isolated = True + if bool(bernoulli.rvs(self.model.model_data.isolation_rate)): + self.agent_data.isolated = True else: - self.isolated = False - self.in_isolation = True - elif (self.astep >= self.model.isolation_end): + self.agent_data.isolated = False + self.agent_data.in_isolation = True + elif (self.astep >= self.model.model_data.isolation_end): if (self.stage == Stage.SUSCEPTIBLE) or (self.stage == Stage.EXPOSED) or \ (self.stage == Stage.ASYMPTOMATIC): - if bool(bernoulli.rvs(self.model.after_isolation)): - self.isolated = True + if bool(bernoulli.rvs(self.model.model_data.after_isolation)): + self.agent_data.isolated = True else: - self.isolated = False - self.in_isolation = True + self.agent_data.isolated = False + self.agent_data.in_isolation = True # Using a similar logic, we remove isolation for all relevant agents still locked - if self.in_isolation and (self.astep >= self.model.isolation_end): + if self.agent_data.in_isolation and (self.astep >= self.model.model_data.isolation_end): if (self.stage == Stage.SUSCEPTIBLE) or (self.stage == Stage.EXPOSED) or \ (self.stage == Stage.ASYMPTOMATIC): - self.isolated = False - self.in_isolation = False + self.agent_data.isolated = False + self.agent_data.in_isolation = False #Implementing the current safety factor for maximum effectiveness - vaccination_time = self.model.stepno - self.vaccination_day + vaccination_time = self.model.stepno - self.agent_data.vaccination_day #In this model I will assume that the vaccine is only half as effective once 2 weeks have passed given one dose. - effective_date = self.model.dwell_15_day * 14 - if (vaccination_time < effective_date) and self.vaccinated == True: - self.safetymultiplier = 1 - (self.model.effectiveness_per_dosage * (vaccination_time/effective_date)) - self.current_effectiveness #Error the vaccination will go to 0 once it is done. + effective_date = self.model.model_data.dwell_15_day * 14 + if (vaccination_time < effective_date) and self.agent_data.vaccinated == True: + self.agent_data.safetymultiplier = 1 - (self.model.model_data.effectiveness_per_dosage * (vaccination_time/effective_date)) - self.agent_data.current_effectiveness #Error the vaccination will go to 0 once it is done. else: - self.current_effectiveness = self.model.effectiveness_per_dosage * self.vaccine_count - self.safetymultiplier = 1 - self.current_effectiveness * self.model.variant_data_list[self.variant]["Vaccine_Multiplier"] - if (self.vaccine_count < self.model.vaccine_dosage): - self.dosage_eligible = True # Once this number is false, the person is eligible and is not fully vaccinated. - elif self.fully_vaccinated == False: - self.dosage_eligible = False - self.fully_vaccinated = True - self.model.fully_vaccinated_count = self.model.fully_vaccinated_count + 1 + self.agent_data.current_effectiveness = self.model.model_data.effectiveness_per_dosage * self.agent_data.vaccine_count + self.agent_data.safetymultiplier = 1 - self.agent_data.current_effectiveness * self.model.model_data.variant_data_list[self.agent_data.variant]["Vaccine_Multiplier"] + if (self.agent_data.vaccine_count < self.model.model_data.vaccine_dosage): + self.agent_data.dosage_eligible = True # Once this number is false, the person is eligible and is not fully vaccinated. + elif self.agent_data.fully_vaccinated == False: + self.agent_data.dosage_eligible = False + self.agent_data.fully_vaccinated = True + self.model.model_data.fully_vaccinated_count = self.model.model_data.fully_vaccinated_count + 1 # Using the model, determine if a susceptible individual becomes infected due to # being elsewhere and returning to the community if self.stage == Stage.SUSCEPTIBLE: - # if bernoulli.rvs(self.model.rate_inbound): - # self.stage = Stage.EXPOSED - # self.model.generally_infected = self.model.generally_infected + 1 - # - # if self.stage == Stage.SUSCEPTIBLE: - # # Important: infected people drive the spread, not - # # the number of healthy ones - # - # # If testing is available and the date is reached, test - # # Testing of a healthy person should maintain them as + # If testing is available and the date is reached, test + # Testing of a healthy person should maintain them as # still susceptible. # We take care of testing probability at the top level step # routine to avoid this repeated computation - if not(self.tested or self.tested_traced) and bernoulli.rvs(self.test_chance): - self.tested = True - self.model.cumul_test_cost = self.model.cumul_test_cost + self.model.test_cost + if not(self.agent_data.tested or self.agent_data.tested_traced) and bernoulli.rvs(self.agent_data.test_chance): + self.agent_data.tested = True + self.model.model_data.cumul_test_cost = self.model.model_data.cumul_test_cost + self.model.model_data.test_cost # First opportunity to get infected: contact with others # in near proximity cellmates = self.model.grid.get_cell_list_contents([self.pos]) @@ -425,79 +323,79 @@ def step(self): #values we would have to account for variant = "Standard" for c in cellmates: - if c.is_contagious() and (c.stage == Stage.SYMPDETECTED or c.stage == Stage.SEVERE) and self.variant_immune[c.variant] == False: + if c.is_contagious() and (c.stage == Stage.SYMPDETECTED or c.stage == Stage.SEVERE) and self.agent_data.variant_immune[c.agent_data.variant] == False: c.add_contact_trace(self) - if self.isolated: #If the agent is isolating - if bernoulli.rvs(1 - self.model.prob_isolation_effective):#Checks if isolation was effective - self.isolated_but_inefficient = True + if self.agent_data.isolated: #If the agent is isolating + if bernoulli.rvs(1 - self.model.model_data.prob_isolation_effective):#Checks if isolation was effective + self.agent_data.isolated_but_inefficient = True infected_contact = 1 - variant = c.variant + variant = c.agent_data.variant break else: - self.isolated_but_inefficient = False + self.agent_data.isolated_but_inefficient = False else: #If the agent is not isolating they come in contact infected_contact = 1 - variant = c.variant + variant = c.agent_data.variant break - elif c.is_contagious() and (c.stage == Stage.ASYMPTOMATIC or c.stage == Stage.ASYMPDETECTED) and self.variant_immune[c.variant] == False: + elif c.is_contagious() and (c.stage == Stage.ASYMPTOMATIC or c.stage == Stage.ASYMPDETECTED) and self.agent_data.variant_immune[c.agent_data.variant] == False: c.add_contact_trace(self) - if self.isolated: - if bernoulli.rvs(1 - self.model.prob_isolation_effective):#Checks if isolation was effective - self.isolated_but_inefficient = True + if self.agent_data.isolated: + if bernoulli.rvs(1 - self.model.model_data.prob_isolation_effective):#Checks if isolation was effective + self.agent_data.isolated_but_inefficient = True infected_contact = 2 - variant = c.variant + variant = c.agent_data.variant #Does not break to check if there was a symptomatic contact in the same check else: - self.isolated_but_inefficient = False + self.agent_data.isolated_but_inefficient = False else: infected_contact = 2 - variant = c.variant + variant = c.agent_data.variant # Value is computed before infected stage happens isolation_private_divider = 1 isolation_public_divider = 1 - if self.employed: - if self.isolated: + if self.agent_data.employed: + if self.agent_data.isolated: isolation_private_divider = 0.3 isolation_public_divider = 0.01 - self.cumul_private_value = self.cumul_private_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.SUSCEPTIBLE])*isolation_private_divider - self.cumul_public_value = self.cumul_public_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.SUSCEPTIBLE])*isolation_public_divider + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.SUSCEPTIBLE])*isolation_private_divider + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.SUSCEPTIBLE])*isolation_public_divider else: - self.cumul_private_value = self.cumul_private_value + 0 - self.cumul_public_value = self.cumul_public_value - 2*self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.SUSCEPTIBLE] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + 0 + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value - 2*self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.SUSCEPTIBLE] - current_prob = self.prob_contagion * self.model.variant_data_list[variant]["Contagtion_Multiplier"] - if self.vaccinated: - current_prob = current_prob * self.safetymultiplier + current_prob = self.agent_data.prob_contagion * self.model.model_data.variant_data_list[variant]["Contagtion_Multiplier"] + if self.agent_data.vaccinated: + current_prob = current_prob * self.agent_data.safetymultiplier if infected_contact == 2: current_prob = current_prob * 0.42 if infected_contact > 0: - if self.isolated: - if bernoulli.rvs(current_prob) and not(bernoulli.rvs(self.model.prob_isolation_effective)): + if self.agent_data.isolated: + if bernoulli.rvs(current_prob) and not(bernoulli.rvs(self.model.model_data.prob_isolation_effective)): self.stage = Stage.EXPOSED - self.variant = variant - self.model.generally_infected = self.model.generally_infected + 1 + self.agent_data.variant = variant + self.model.model_data.generally_infected = self.model.model_data.generally_infected + 1 else: if bernoulli.rvs(current_prob): #Added vaccination account after being exposed to determine exposure. self.stage = Stage.EXPOSED - self.variant = variant - self.model.generally_infected = self.model.generally_infected + 1 + self.agent_data.variant = variant + self.model.model_data.generally_infected = self.model.model_data.generally_infected + 1 # Second opportunity to get infected: residual droplets in places # TODO - if not(self.isolated): + if not(self.agent_data.isolated): self.move() elif self.stage == Stage.EXPOSED: # Susceptible patients only move and spread the disease. @@ -510,40 +408,40 @@ def step(self): isolation_private_divider = 1 isolation_public_divider = 1 - if self.employed: - if self.isolated: + if self.agent_data.employed: + if self.agent_data.isolated: isolation_private_divider = 0.3 isolation_public_divider = 0.01 - self.cumul_private_value = self.cumul_private_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.EXPOSED])*isolation_private_divider - self.cumul_public_value = self.cumul_public_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.EXPOSED])*isolation_public_divider + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.EXPOSED])*isolation_private_divider + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.EXPOSED])*isolation_public_divider else: - self.cumul_private_value = self.cumul_private_value + 0 - self.cumul_public_value = self.cumul_public_value - 2*self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.EXPOSED] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + 0 + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value - 2*self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.EXPOSED] # Assignment is less expensive than comparison do_move = True - current_prob_asymptomatic = self.model.prob_asymptomatic * self.model.variant_data_list[self.variant]["Asymtpomatic_Multiplier"] - if self.vaccinated: - current_prob_asymptomatic = 1-(1-self.model.prob_asymptomatic) * self.safetymultiplier #Probability of asymptomatic becomes 1-(probability of symptomatic)*safety_multiplier + current_prob_asymptomatic = self.model.model_data.prob_asymptomatic * self.model.model_data.variant_data_list[self.agent_data.variant]["Asymtpomatic_Multiplier"] + if self.agent_data.vaccinated: + current_prob_asymptomatic = 1-(1-self.model.model_data.prob_asymptomatic) * self.agent_data.safetymultiplier #Probability of asymptomatic becomes 1-(probability of symptomatic)*safety_multiplier # If testing is available and the date is reached, test - if not(self.tested or self.tested_traced) and bernoulli.rvs(self.test_chance): + if not(self.agent_data.tested or self.agent_data.tested_traced) and bernoulli.rvs(self.agent_data.test_chance): if bernoulli.rvs(current_prob_asymptomatic): self.stage = Stage.ASYMPDETECTED else: self.stage = Stage.SYMPDETECTED do_move = False - self.tested = True - self.model.cumul_test_cost = self.model.cumul_test_cost + self.model.test_cost + self.agent_data.tested = True + self.model.model_data.cumul_test_cost = self.model.model_data.cumul_test_cost + self.model.model_data.test_cost else: - if self.curr_incubation < self.incubation_time: - self.curr_incubation = self.curr_incubation + 1 + if self.agent_data.curr_incubation < self.agent_data.incubation_time: + self.agent_data.curr_incubation = self.agent_data.curr_incubation + 1 else: if bernoulli.rvs(current_prob_asymptomatic): self.stage = Stage.ASYMPTOMATIC @@ -552,7 +450,7 @@ def step(self): do_move = False # Now, attempt to move - if do_move and not(self.isolated): + if do_move and not(self.agent_data.isolated): self.move() # Perform the move once the condition has been determined @@ -564,31 +462,31 @@ def step(self): isolation_private_divider = 1 isolation_public_divider = 1 - if self.employed: - if self.isolated: + if self.agent_data.employed: + if self.agent_data.isolated: isolation_private_divider = 0.3 isolation_public_divider = 0.01 - self.cumul_private_value = self.cumul_private_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.ASYMPTOMATIC])*isolation_private_divider - self.cumul_public_value = self.cumul_public_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPTOMATIC])*isolation_public_divider + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.ASYMPTOMATIC])*isolation_private_divider + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPTOMATIC])*isolation_public_divider else: - self.cumul_private_value = self.cumul_private_value + 0 - self.cumul_public_value = self.cumul_public_value - 2*self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPTOMATIC] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + 0 + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value - 2*self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPTOMATIC] - if not(self.tested or self.tested_traced) and bernoulli.rvs(self.test_chance): + if not(self.agent_data.tested or self.agent_data.tested_traced) and bernoulli.rvs(self.agent_data.test_chance): self.stage = Stage.ASYMPDETECTED - self.tested = True - self.model.cumul_test_cost = self.model.cumul_test_cost + self.model.test_cost + self.agent_data.tested = True + self.model.model_data.cumul_test_cost = self.model.model_data.cumul_test_cost + self.model.model_data.test_cost - if self.curr_recovery >= self.recovery_time: + if self.agent_data.curr_recovery >= self.agent_data.recovery_time: self.stage = Stage.RECOVERED - self.variant_immune[self.variant] = True + self.agent_data.variant_immune[self.agent_data.variant] = True else: - self.curr_recovery += 1 + self.agent_data.curr_recovery += 1 - if not (self.isolated): + if not (self.agent_data.isolated): self.move() @@ -596,180 +494,180 @@ def step(self): # Once a symptomatic patient has been detected, it does not move and starts # the road to severity, recovery or death. We assume that, by reaching a health # unit, they are tested as positive. - self.isolated = True - self.tested = True + self.agent_data.isolated = True + self.agent_data.tested = True - current_severe_chance = self.mortality_value * self.model.variant_data_list[self.variant]["Mortality_Multiplier"] * (1/(self.model.dwell_15_day)) - if (self.vaccinated): - current_severe_chance = current_severe_chance * self.safetymultiplier + current_severe_chance = self.agent_data.mortality_value * self.model.model_data.variant_data_list[self.agent_data.variant]["Mortality_Multiplier"] * (1/(self.model.model_data.dwell_15_day)) + if (self.agent_data.vaccinated): + current_severe_chance = current_severe_chance * self.agent_data.safetymultiplier # Contact tracing logic: use a negative number to indicate trace exhaustion - if self.model.tracing_now and self.tracing_counter >= 0: + if self.model.model_data.tracing_now and self.agent_data.tracing_counter >= 0: # Test only when the count down has been reached - if self.tracing_counter == self.tracing_delay: - for t in self.contacts: + if self.agent_data.tracing_counter == self.agent_data.tracing_delay: + for t in self.agent_data.contacts: t.test_contact_trace() - self.tracing_counter = -1 + self.agent_data.tracing_counter = -1 else: - self.tracing_counter = self.tracing_counter + 1 + self.agent_data.tracing_counter = self.agent_data.tracing_counter + 1 - self.cumul_private_value = self.cumul_private_value + \ - self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.SYMPDETECTED] - self.cumul_public_value = self.cumul_public_value + \ - self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.SYMPDETECTED] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.SYMPDETECTED] + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.SYMPDETECTED] - if self.curr_incubation + self.curr_recovery < self.incubation_time + self.recovery_time: - self.curr_recovery = self.curr_recovery + 1 + if self.agent_data.curr_incubation + self.agent_data.curr_recovery < self.agent_data.incubation_time + self.agent_data.recovery_time: + self.agent_data.curr_recovery = self.agent_data.curr_recovery + 1 if bernoulli.rvs(current_severe_chance): self.stage = Stage.SEVERE else: self.stage = Stage.RECOVERED - self.variant_immune[self.variant] = True + self.agent_data.variant_immune[self.agent_data.variant] = True elif self.stage == Stage.ASYMPDETECTED: - self.isolated = True + self.agent_data.isolated = True # Contact tracing logic: use a negative number to indicate trace exhaustion - if self.model.tracing_now and self.tracing_counter >= 0: + if self.model.model_data.tracing_now and self.agent_data.tracing_counter >= 0: # Test only when the count down has been reached - if self.tracing_counter == self.tracing_delay: - for t in self.contacts: + if self.agent_data.tracing_counter == self.agent_data.tracing_delay: + for t in self.agent_data.contacts: t.test_contact_trace() - self.tracing_counter = -1 + self.agent_data.tracing_counter = -1 else: - self.tracing_counter = self.tracing_counter + 1 + self.agent_data.tracing_counter = self.agent_data.tracing_counter + 1 - self.cumul_private_value = self.cumul_private_value + \ - self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.ASYMPDETECTED] - self.cumul_public_value = self.cumul_public_value + \ - self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPDETECTED] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.ASYMPDETECTED] + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.ASYMPDETECTED] # The road of an asymptomatic patients is similar without the prospect of death - if self.curr_incubation + self.curr_recovery < self.incubation_time + self.recovery_time: - self.curr_recovery = self.curr_recovery + 1 + if self.agent_data.curr_incubation + self.agent_data.curr_recovery < self.agent_data.incubation_time + self.agent_data.recovery_time: + self.agent_data.curr_recovery = self.agent_data.curr_recovery + 1 else: self.stage = Stage.RECOVERED - self.variant_immune[self.variant] = True + self.agent_data.variant_immune[self.agent_data.variant] = True elif self.stage == Stage.SEVERE: - self.cumul_private_value = self.cumul_private_value + \ - self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.SEVERE] - self.cumul_public_value = self.cumul_public_value + \ - self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.SEVERE] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.SEVERE] + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.SEVERE] # Severe patients are in ICU facilities - if self.curr_recovery < self.recovery_time: + if self.agent_data.curr_recovery < self.agent_data.recovery_time: # Not recovered yet, may pass away depending on prob. - if self.model.bed_count > 0 and self.occupying_bed == False: - self.occupying_bed = True - self.model.bed_count -= 1 - if self.occupying_bed == False: - if bernoulli(1/(self.recovery_time)): #Chance that someone dies at this stage is current_time/time that they should recover. This ensures that they may die at a point during recovery. + if self.model.model_data.bed_count > 0 and self.agent_data.occupying_bed == False: + self.agent_data.ccupying_bed = True + self.model.model_data.bed_count -= 1 + if self.agent_data.occupying_bed == False: + if bernoulli(1/(self.agent_data.recovery_time)): #Chance that someone dies at this stage is current_time/time that they should recover. This ensures that they may die at a point during recovery. self.stage = Stage.DECEASED # else: # if bernoulli(0 * 1/self.recovery_time): #Chance that someone dies on the bed is 42% less likely so I will also add that they have a 1/recovery_time chance of dying # self.stage = Stage.DECEASED # self.occupying_bed == False # self.model.bed_count += 1 - self.curr_recovery = self.curr_recovery + 1 + self.agent_data.curr_recovery = self.agent_data.curr_recovery + 1 else: self.stage = Stage.RECOVERED - self.variant_immune[self.variant] = True - if (self.occupying_bed == True): - self.occupying_bed == False - self.model.bed_count += 1 + self.agent_data.variant_immune[self.agent_data.variant] = True + if (self.agent_data.occupying_bed == True): + self.agent_data.occupying_bed == False + self.model.model_data.bed_count += 1 elif self.stage == Stage.RECOVERED: cellmates = self.model.grid.get_cell_list_contents([self.pos]) - if self.employed: + if self.agent_data.employed: isolation_private_divider = 1 isolation_public_divider = 1 - if self.isolated: + if self.agent_data.isolated: isolation_private_divider = 0.3 isolation_public_divider = 0.01 - self.cumul_private_value = self.cumul_private_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.RECOVERED])*isolation_private_divider - self.cumul_public_value = self.cumul_public_value + \ - ((len(cellmates) - 1) * self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.RECOVERED])*isolation_public_divider + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.RECOVERED])*isolation_private_divider + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + ((len(cellmates) - 1) * self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.RECOVERED])*isolation_public_divider else: - self.cumul_private_value = self.cumul_private_value + 0 - self.cumul_public_value = self.cumul_public_value - 2*self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.RECOVERED] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + 0 + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value - 2*self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.RECOVERED] # A recovered agent can now move freely within the grid again - self.curr_recovery = 0 - self.isolated = False - self.isolated_but_inefficient = False + self.agent_data.curr_recovery = 0 + self.agent_data.isolated = False + self.agent_data.isolated_but_inefficient = False infected_contact = 0 variant = "Standard" for c in cellmates: - if c.is_contagious() and self.model.variant_data_list[c.variant]["Reinfection"] == True and (c.stage == Stage.SYMPDETECTED or c.stage == Stage.SEVERE) and self.variant_immune[c.variant] == False: + if c.is_contagious() and self.model.model_data.variant_data_list[c.variant]["Reinfection"] == True and (c.stage == Stage.SYMPDETECTED or c.stage == Stage.SEVERE) and self.agent_data.variant_immune[c.variant] == False: c.add_contact_trace(self) - if self.isolated: #If the agent is isolating - if bernoulli.rvs(1 - self.model.prob_isolation_effective):#Checks if isolation was effective - self.isolated_but_inefficient = True + if self.agent_data.isolated: #If the agent is isolating + if bernoulli.rvs(1 - self.model.model_data.prob_isolation_effective):#Checks if isolation was effective + self.agent_data.isolated_but_inefficient = True infected_contact = 1 variant = c.variant break else: - self.isolated_but_inefficient = False + self.agent_data.isolated_but_inefficient = False else: #If the agent is not isolating they come in contact infected_contact = 1 variant = c.variant break - elif c.is_contagious() and (c.stage == Stage.ASYMPTOMATIC or c.stage == Stage.ASYMPDETECTED) and self.variant_immune[c.variant] == False: + elif c.is_contagious() and (c.stage == Stage.ASYMPTOMATIC or c.stage == Stage.ASYMPDETECTED) and self.agent_data.variant_immune[c.variant] == False: c.add_contact_trace(self) - if self.isolated: - if bernoulli.rvs(1 - self.model.prob_isolation_effective):#Checks if isolation was effective - self.isolated_but_inefficient = True + if self.agent_data.isolated: + if bernoulli.rvs(1 - self.model.model_data.prob_isolation_effective):#Checks if isolation was effective + self.agent_data.isolated_but_inefficient = True infected_contact = 2 variant = c.variant #Does not break to check if there was a symptomatic contact in the same check else: - self.isolated_but_inefficient = False + self.agent_data.isolated_but_inefficient = False else: infected_contact = 2 variant = c.variant - current_prob = self.prob_contagion * self.model.variant_data_list[variant]["Contagtion_Multiplier"] - if self.vaccinated: - current_prob = current_prob * self.safetymultiplier + current_prob = self.agent_data.prob_contagion * self.model.model_data.variant_data_list[variant]["Contagtion_Multiplier"] + if self.agent_data.vaccinated: + current_prob = current_prob * self.agent_data.safetymultiplier if infected_contact == 2: current_prob = current_prob * 0.42 if infected_contact > 0: - if self.isolated: - if bernoulli.rvs(current_prob) and not (bernoulli.rvs(self.model.prob_isolation_effective)): + if self.agent_data.isolated: + if bernoulli.rvs(current_prob) and not (bernoulli.rvs(self.model.model_data.prob_isolation_effective)): if self.unique_id == 0: print("Agent got infected here") self.stage = Stage.EXPOSED - self.variant = variant + self.agent_data.variant = variant else: if bernoulli.rvs(current_prob): if self.unique_id == 0: print("Agent got infected here") # Added vaccination account after being exposed to determine exposure. self.stage = Stage.EXPOSED - self.variant = variant + self.agent_data.variant = variant self.move() elif self.stage == Stage.DECEASED: - self.cumul_private_value = self.cumul_private_value + \ - self.model.stage_value_dist[ValueGroup.PRIVATE][Stage.DECEASED] - self.cumul_public_value = self.cumul_public_value + \ - self.model.stage_value_dist[ValueGroup.PUBLIC][Stage.DECEASED] + self.agent_data.cumul_private_value = self.agent_data.cumul_private_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PRIVATE][Stage.DECEASED] + self.agent_data.cumul_public_value = self.agent_data.cumul_public_value + \ + self.model.model_data.stage_value_dist[ValueGroup.PUBLIC][Stage.DECEASED] else: # If we are here, there is a problem sys.exit("Unknown stage: aborting.") @@ -778,8 +676,8 @@ def step(self): def move(self): # If dwelling has not been exhausted, do not move - if self.curr_dwelling > 0: - self.curr_dwelling = self.curr_dwelling - 1 + if self.agent_data.curr_dwelling > 0: + self.agent_data.curr_dwelling = self.agent_data.curr_dwelling - 1 # If dwelling has been exhausted, move and replenish the dwell else: @@ -791,7 +689,7 @@ def move(self): new_position = self.random.choice(possible_steps) self.model.grid.move_agent(self, new_position) - self.curr_dwelling = poisson.rvs(self.model.avg_dwell) + self.agent_data.curr_dwelling = poisson.rvs(self.model.model_data.avg_dwell) ######################################## @@ -800,17 +698,17 @@ def compute_variant_stage(model, variant, stage): count = 0 for agent in model.schedule.agents: if stage == Stage.SUSCEPTIBLE: - if agent.variant == variant: + if agent.agent_data.variant == variant: count += 1 else: - if agent.stage == stage and agent.variant == variant: + if agent.stage == stage and agent.agent_data.variant == variant: count += 1 return count def compute_vaccinated_stage(model, stage): count = 0 for agent in model.schedule.agents: - if agent.stage == stage and agent.vaccinated == True: + if agent.stage == stage and agent.agent_data.vaccinated == True: count += count vaccinated_count = compute_vaccinated_count(model) if vaccinated_count == 0: @@ -832,14 +730,14 @@ def count_type(model, stage): def compute_isolated(model): count = 0 for agent in model.schedule.agents: - if agent.isolated: + if agent.agent_data.isolated: count = count + 1 return count def compute_employed(model): count = 0 for agent in model.schedule.agents: - if agent.employed: + if agent.agent_data.employed: count = count + 1 return count @@ -848,7 +746,7 @@ def compute_unemployed(model): count = 0 for agent in model.schedule.agents: - if not(agent.employed): + if not(agent.agent_data.employed): count = count + 1 return count @@ -865,16 +763,16 @@ def compute_stepno(model): def compute_cumul_private_value(model): value = 0 for agent in model.schedule.agents: - value = value + agent.cumul_private_value - return np.sign(value)*np.power(np.abs(value), model.alpha_private)/model.num_agents + value = value + agent.agent_data.cumul_private_value + return np.sign(value)*np.power(np.abs(value), model.model_data.alpha_private)/model.num_agents def compute_cumul_public_value(model): value = 0 for agent in model.schedule.agents: - value = value + agent.cumul_public_value + value = value + agent.agent_data.cumul_public_value - return np.sign(value)*np.power(np.abs(value), model.alpha_public)/model.num_agents + return np.sign(value)*np.power(np.abs(value), model.model_data.alpha_public)/model.num_agents # Changed the method for calculating the test cost. This will occur in more linear time, @@ -882,19 +780,19 @@ def compute_cumul_public_value(model): # will change testing to be based on necessity along with the vaccine. def compute_cumul_testing_cost(model): - return model.cumul_test_cost + return model.model_data.cumul_test_cost def compute_cumul_vaccination_cost(model): - return model.cumul_vaccine_cost + return model.model_data.cumul_vaccine_cost def compute_total_cost(model): - return model.cumul_test_cost + model.cumul_vaccine_cost + return model.model_data.cumul_test_cost + model.model_data.cumul_vaccine_cost def compute_tested(model): tested = 0 for agent in model.schedule.agents: - if agent.tested: + if agent.agent_data.tested: tested = tested + 1 return tested @@ -903,7 +801,7 @@ def compute_tested(model): def compute_vaccinated(model): vaccinated_count = 0 for agent in model.schedule.agents: - if agent.vaccinated: + if agent.agent_data.vaccinated: vaccinated_count = vaccinated_count + 1 return vaccinated_count @@ -911,28 +809,28 @@ def compute_vaccinated(model): def compute_vaccinated_count(model): vaccinated_count = 0 for agent in model.schedule.agents: - if agent.vaccinated: + if agent.agent_data.vaccinated: vaccinated_count = vaccinated_count + 1 return vaccinated_count def compute_vaccinated_1(model): vaccinated_count = 0 for agent in model.schedule.agents: - if agent.vaccine_count == 1: + if agent.agent_data.vaccine_count == 1: vaccinated_count = vaccinated_count + 1 return vaccinated_count def compute_vaccinated_2(model): vaccinated_count = 0 for agent in model.schedule.agents: - if agent.vaccine_count == 2: + if agent.agent_data.vaccine_count == 2: vaccinated_count = vaccinated_count + 1 return vaccinated_count def compute_willing_agents(model): count = 0 for agent in model.schedule.agents: - if agent.vaccine_willingness: + if agent.agent_data.vaccine_willingness: count = count + 1 return count @@ -941,7 +839,7 @@ def compute_willing_agents(model): def compute_vaccinated_in_group_count(model,agegroup): vaccinated_count = 0 for agent in model.schedule.agents: - if (agent.vaccinated) and (agent.age_group == agegroup): + if (agent.agent_data.vaccinated) and (agent.agent_data.age_group == agegroup): vaccinated_count = vaccinated_count + 1 return vaccinated_count @@ -950,7 +848,7 @@ def compute_vaccinated_in_group_count(model,agegroup): def compute_vaccinated_in_group(model,agegroup): vaccinated_count = 0 for agent in model.schedule.agents: - if (agent.vaccinated) and (agent.age_group == agegroup): + if (agent.agent_data.vaccinated) and (agent.agent_data.age_group == agegroup): vaccinated_count = vaccinated_count + 1 return vaccinated_count @@ -958,7 +856,7 @@ def compute_vaccinated_in_group(model,agegroup): def compute_fully_vaccinated_in_group(model,agegroup): vaccinated_count = 0 for agent in model.schedule.agents: - if (agent.fully_vaccinated) and (agent.age_group == agegroup): + if (agent.agent_data.fully_vaccinated) and (agent.agent_data.age_group == agegroup): vaccinated_count = vaccinated_count + 1 return vaccinated_count @@ -966,7 +864,7 @@ def compute_fully_vaccinated_in_group(model,agegroup): def compute_vaccinated_in_group_percent_vaccine_count(model, agegroup, count): vaccinated_count = 0 for agent in model.schedule.agents: - if (agent.vaccine_count == count) and (agent.age_group == agegroup): + if (agent.agent_data.vaccine_count == count) and (agent.agent_data.age_group == agegroup): vaccinated_count = vaccinated_count + 1 return vaccinated_count @@ -975,9 +873,9 @@ def cumul_effectiveness_per_group_vaccinated(model,agegroup): vaccinated_count = 0 effectiveness = 0 for agent in model.schedule.agents: - if (agent.age_group == agegroup and agent.vaccinated == True): + if (agent.agent_data.age_group == agegroup and agent.agent_data.vaccinated == True): vaccinated_count = vaccinated_count + 1 - effectiveness += agent.safetymultiplier + effectiveness += agent.agent_data.safetymultiplier if (vaccinated_count > 0): return 1-(effectiveness / vaccinated_count) else: @@ -987,9 +885,9 @@ def cumul_effectiveness_per_group(model,agegroup): agent_count = 0 effectiveness = 0 for agent in model.schedule.agents: - if (agent.age_group == agegroup): + if (agent.agent_data.age_group == agegroup): agent_count = agent_count + 1 - effectiveness += agent.safetymultiplier + effectiveness += agent.agent_data.safetymultiplier if (agent_count > 0): return 1-(effectiveness / agent_count) else: @@ -998,46 +896,46 @@ def cumul_effectiveness_per_group(model,agegroup): def compute_age_group_count(model,agegroup): count = 0 for agent in model.schedule.agents: - if agent.age_group == agegroup: + if agent.agent_data.age_group == agegroup: count = count + 1 return count def compute_eligible_age_group_count(model,agegroup): count = 0 for agent in model.schedule.agents: - if (agent.age_group == agegroup) and (agent.stage == Stage.SUSCEPTIBLE or agent.stage == Stage.EXPOSED or agent.stage == Stage.ASYMPTOMATIC) and agent.dosage_eligible and agent.vaccine_willingness: + if (agent.agent_data.age_group == agegroup) and (agent.stage == Stage.SUSCEPTIBLE or agent.stage == Stage.EXPOSED or agent.stage == Stage.ASYMPTOMATIC) and agent.agent_data.dosage_eligible and agent.agent_data.vaccine_willingness: count = count + 1 return count def update_vaccination_stage(model): - initial_stage = model.vaccination_stage + initial_stage = model.model_data.vaccination_stage if compute_eligible_age_group_count(model, AgeGroup.C80toXX) < 1: - model.vaccination_stage = VaccinationStage.C70to79 + model.model_data.vaccination_stage = VaccinationStage.C70to79 if compute_eligible_age_group_count(model, AgeGroup.C70to79) < 1: - model.vaccination_stage = VaccinationStage.C60to69 + model.model_data.vaccination_stage = VaccinationStage.C60to69 if compute_eligible_age_group_count(model, AgeGroup.C60to69) < 1: - model.vaccination_stage = VaccinationStage.C50to59 + model.model_data.vaccination_stage = VaccinationStage.C50to59 if compute_eligible_age_group_count(model, AgeGroup.C50to59) < 1: - model.vaccination_stage = VaccinationStage.C40to49 + model.model_data.vaccination_stage = VaccinationStage.C40to49 if compute_eligible_age_group_count(model, AgeGroup.C40to49) < 1: - model.vaccination_stage = VaccinationStage.C30to39 + model.model_data.vaccination_stage = VaccinationStage.C30to39 if compute_eligible_age_group_count(model, AgeGroup.C30to39) < 1: - model.vaccination_stage = VaccinationStage.C20to29 + model.model_data.vaccination_stage = VaccinationStage.C20to29 if compute_eligible_age_group_count(model, AgeGroup.C20to29) < 1: - model.vaccination_stage = VaccinationStage.C10to19 + model.model_data.vaccination_stage = VaccinationStage.C10to19 if compute_eligible_age_group_count(model, AgeGroup.C10to19) < 1: - model.vaccination_stage = VaccinationStage.C00to09 + model.model_data.vaccination_stage = VaccinationStage.C00to09 else: - model.vaccination_stage = VaccinationStage.C80toXX - if initial_stage != model.vaccination_stage: - print(f"Vaccination stage is now {model.vaccination_stage}") + model.model_data.vaccination_stage = VaccinationStage.C80toXX + if initial_stage != model.model_data.vaccination_stage: + print(f"Vaccination stage is now {model.model_data.vaccination_stage}") def compute_willing_group_count(model, agegroup): count = 0 for agent in model.schedule.agents: - if(agent.vaccine_willingness): + if(agent.agent_data.vaccine_willingness): count += 1 return count @@ -1045,14 +943,14 @@ def compute_traced(model): tested = 0 for agent in model.schedule.agents: - if agent.tested_traced: + if agent.agent_data.tested_traced: tested = tested + 1 return tested def compute_total_processor_usage(model): - if model.stepno % model.dwell_15_day == 0: + if model.stepno % model.model_data.dwell_15_day == 0: processes = psu.cpu_percent(1, True) process_count = 0 for idx, process in enumerate(processes): @@ -1063,7 +961,7 @@ def compute_total_processor_usage(model): return 0 def compute_processor_usage(model, processoridx): - if model.stepno % model.dwell_15_day == 0: + if model.stepno % model.model_data.dwell_15_day == 0: processes = psu.cpu_percent(1, True) for idx, process in enumerate(processes): if (idx == processoridx): @@ -1087,17 +985,17 @@ def compute_eff_reprod_number(model): for agent in model.schedule.agents: if agent.stage == Stage.EXPOSED: exposed = exposed + 1 - exp_time = exp_time + agent.incubation_time - prob_contagion = agent.prob_contagion + exp_time = exp_time + agent.agent_data.incubation_time + prob_contagion = agent.agent_data.prob_contagion elif agent.stage == Stage.SYMPDETECTED: # NOTE: this part needs to be adapted to model hospital transmission in further detail symptomatics = symptomatics + 1 - sympt_time = sympt_time + agent.incubation_time - prob_contagion = agent.prob_contagion + sympt_time = sympt_time + agent.agent_data.incubation_time + prob_contagion = agent.agent_data.prob_contagion elif agent.stage == Stage.ASYMPTOMATIC: asymptomatics = asymptomatics + 1 - asympt_time = asympt_time + agent.incubation_time + agent.recovery_time - prob_contagion = agent.prob_contagion + asympt_time = asympt_time + agent.agent_data.incubation_time + agent.agent_data.recovery_time + prob_contagion = agent.agent_data.prob_contagion else: continue @@ -1121,13 +1019,13 @@ def compute_eff_reprod_number(model): infectious_period = 0 avg_contacts = compute_contacts(model) - return model.kmob * model.repscaling * prob_contagion * avg_contacts * infectious_period + return model.model_data.kmob * model.model_data.repscaling * model.model_data.prob_contagion_base * avg_contacts * infectious_period def compute_num_agents(model): return model.num_agents def compute_vaccine_count(model): - return model.vaccine_count + return model.model_data.vaccine_count def compute_datacollection_time(model): return model.datacollection_time @@ -1136,10 +1034,10 @@ def compute_step_time(model): return model.step_time def compute_generally_infected(model): - return model.generally_infected + return model.model_data.generally_infected def compute_fully_vaccinated_count(model): - return model.fully_vaccinated_count + return model.model_data.fully_vaccinated_count def get_agent_data(agent, param_name): @@ -1156,9 +1054,9 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag day_distancing_start, days_distancing_lasts, proportion_detected, day_testing_start, days_testing_lasts, new_agent_proportion, new_agent_start, new_agent_lasts, new_agent_age_mean, new_agent_prop_infected, day_tracing_start, days_tracing_lasts, stage_value_matrix, test_cost, alpha_private, alpha_public, proportion_beds_pop, day_vaccination_begin, - day_vaccination_end, effective_period, effectiveness, distribution_rate, cost_per_vaccine, vaccination_percent, variant_data, step_count, load_from_file, - loading_file_path, starting_step, agent_storage, model_storage, agent_increment, model_increment, iteration, dummy=0): - + day_vaccination_end, effective_period, effectiveness, distribution_rate, cost_per_vaccine, vaccination_percent, variant_data, + step_count, load_from_file, loading_file_path, starting_step, agent_storage, model_storage, agent_increment, model_increment, iteration, dummy=0 + ): print("Made it to the model") self.iteration = iteration print(iteration) @@ -1168,64 +1066,101 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag self.num_agents = num_agents self.grid = MultiGrid(width, height, True) self.schedule = RandomActivation(self) - self.age_mortality = age_mortality - self.sex_mortality = sex_mortality - self.age_distribution = age_distribution - self.sex_distribution = sex_distribution - self.stage_value_dist = stage_value_matrix - self.test_cost = test_cost self.stepno = 0 - self.alpha_private = alpha_private - self.alpha_public = alpha_public - self.fully_vaccinated_count = 0 - self.bed_count = 0 - self.prop_initial_infected = prop_initial_infected - self.generally_infected = 0 #Stores the number of generally infected agents, this value is updated at every state change within the model. - - #TODO in the future we will consider the costs of different policy measures. - self.cumul_vaccine_cost = 0 - self.cumul_test_cost = 0 - self.total_costs = 0 - - # Used for runtime over time analysis. self.datacollection_time = 0 self.step_time = 0 - - # temporary vaccination chance portion, will exclude when vaccination process is more clear. - - #Vaccination model variables: - #TODO add a parameter list of vaccines like you did for the variants to have a more stochastic distribution of the vaccine. - self.vaccination_chance = distribution_rate/num_agents #TODO study the vaccination process again to revise why you kept this - - #Vaccination information stored onto the model. - self.vaccination_stage = VaccinationStage.C80toXX #Under the oldest to youngest priority model. Might change if we introduce occupations - self.vaccine_cost = cost_per_vaccine #TODO remove this everywhere, it serves no purpose - self.day_vaccination_begin = day_vaccination_begin - self.day_vaccination_end = day_vaccination_end - self.effective_period = effective_period #TODO We could implement an effective period for the vaccine if immunity dies over time. Requires more research - self.effectiveness = effectiveness - self.distribution_rate = distribution_rate - self.vaccine_count = 0 #Stores the total number of vaccines available within the model. - self.vaccinated_count = 0 #Stores the number of 1'st dose agents within the model. TODO clarify what this is (Where it is updated). - self.vaccination_percent = vaccination_percent #Percent of vaccine willing agents. - - # Keeps track of how many doses of the vaccine are required - self.vaccine_dosage = 2 #TODO Make this an input parameter. - self.effectiveness_per_dosage = self.effectiveness/self.vaccine_dosage + + dwell_15_day = 96 + vaccine_dosage = 2 + repscaling = 1 + testing_start=day_testing_start* dwell_15_day + tracing_start=day_tracing_start* dwell_15_day + isolation_start=day_start_isolation*dwell_15_day + distancing_start=day_distancing_start*dwell_15_day + new_agent_start=new_agent_start*dwell_15_day + max_bed_available = num_agents * proportion_beds_pop + + self.model_data = ModelDataClass ( + age_mortality=age_mortality, + sex_mortality=sex_mortality, + age_distribution=age_distribution, + sex_distribution=sex_distribution, + stage_value_dist=stage_value_matrix, + test_cost=test_cost, + alpha_private=alpha_private, + alpha_public=alpha_public, + fully_vaccinated_count=0, + prop_initial_infected=prop_initial_infected, + generally_infected=0, + cumul_vaccine_cost=0, + cumul_test_cost=0, + total_costs=0, + vaccination_chance=distribution_rate/num_agents, + vaccination_stage=VaccinationStage.C80toXX, + vaccine_cost=cost_per_vaccine, + day_vaccination_begin=day_vaccination_begin, + day_vaccination_end=day_vaccination_end, + effective_period=effective_period, + effectiveness=effectiveness, + distribution_rate=distribution_rate, + vaccine_count=0, + vaccinated_count=0, + vaccinated_percent=vaccination_percent, + vaccine_dosage=vaccine_dosage, + effectiveness_per_dosage=effectiveness/vaccine_dosage, + variant_data_list={}, + agent_parameter_names=[], + dwell_15_day=dwell_15_day, + avg_dwell=4, + avg_incubation=int(round(avg_incubation_time * dwell_15_day)), + repscaling=repscaling, + prob_contagion_base=prob_contagion / repscaling, + kmob=kmob, + rate_inbound=rate_inbound/dwell_15_day, + prob_contagion_places=0.001, + prob_asymptomatic=proportion_asymptomatic, + avg_recovery=avg_recovery_time * dwell_15_day, + testing_rate=proportion_detected/(days_testing_lasts * dwell_15_day), + testing_start=testing_start, + testing_end=testing_start + days_testing_lasts* dwell_15_day, + tracing_start=tracing_start, + tracing_end=tracing_start + days_tracing_lasts* dwell_15_day, + tracing_now=False, + isolation_rate=proportion_isolated, + isolation_start=isolation_start, + isolation_end=isolation_start + days_isolation_lasts*dwell_15_day, + after_isolation=after_isolation, + prob_isolation_effective=prob_isolation_effective, + distancing=social_distance, + distancing_start=distancing_start, + distancing_end=distancing_start + days_distancing_lasts*dwell_15_day, + new_agent_num=int(new_agent_proportion * self.num_agents), + new_agent_start=new_agent_start, + new_agent_end=new_agent_start + new_agent_lasts*dwell_15_day, + new_agent_age_mean=new_agent_age_mean, + new_agent_prop_infected=new_agent_prop_infected, + vaccination_start=day_vaccination_begin * dwell_15_day, + vaccination_end=day_vaccination_end * dwell_15_day, + vaccination_now=False, + variant_start_times={}, + variant_start={}, + prob_severe=proportion_severe, + max_bed_available = max_bed_available, + bed_count=max_bed_available + ) #Variant variables within the model: #TODO work on a method for a self evolving variant instead of spontaniously generated variants. - self.variant_data_list = {} #Storing all the parameters for each variant inside a dictionary. for variant in variant_data: - self.variant_data_list[variant["Name"]] = {} - self.variant_data_list[variant["Name"]]["Name"] = variant["Name"] - self.variant_data_list[variant["Name"]]["Appearance"] = variant["Appearance"] - self.variant_data_list[variant["Name"]]["Contagtion_Multiplier"] = variant["Contagtion_Multiplier"] - self.variant_data_list[variant["Name"]]["Vaccine_Multiplier"] = variant["Vaccine_Multiplier"] - self.variant_data_list[variant["Name"]]["Asymtpomatic_Multiplier"] = variant["Asymtpomatic_Multiplier"] - self.variant_data_list[variant["Name"]]["Mortality_Multiplier"] = variant["Mortality_Multiplier"] - self.variant_data_list[variant["Name"]]["Reinfection"] = variant["Reinfection"] + self.model_data.variant_data_list[variant["Name"]] = {} + self.model_data.variant_data_list[variant["Name"]]["Name"] = variant["Name"] + self.model_data.variant_data_list[variant["Name"]]["Appearance"] = variant["Appearance"] + self.model_data.variant_data_list[variant["Name"]]["Contagtion_Multiplier"] = variant["Contagtion_Multiplier"] + self.model_data.variant_data_list[variant["Name"]]["Vaccine_Multiplier"] = variant["Vaccine_Multiplier"] + self.model_data.variant_data_list[variant["Name"]]["Asymtpomatic_Multiplier"] = variant["Asymtpomatic_Multiplier"] + self.model_data.variant_data_list[variant["Name"]]["Mortality_Multiplier"] = variant["Mortality_Multiplier"] + self.model_data.variant_data_list[variant["Name"]]["Reinfection"] = variant["Reinfection"] #Backtracking model data: self.load_from_file = load_from_file #Dictates whether we will be loading the model from a save file. @@ -1249,112 +1184,29 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag self.model_reporters = {} #For using the model reporting feature in mesa or in the def step function. - - - # Number of 15 minute dwelling times per day - self.dwell_15_day = 96 - - # Average dwelling units - self.avg_dwell = 4 - - # The average incubation period is 5 days, which can be changed - self.avg_incubation = int(round(avg_incubation_time * self.dwell_15_day)) - - # Probability of contagion after exposure in the same cell - # Presupposes a person centered on a 1.8 meter radius square. - # We use a proxy value to account for social distancing. - # Representativeness modifies the probability of contagion by the scaling factor - if repscaling < 2: - self.repscaling = 1 - else: - self.repscaling = (np.log(repscaling)/np.log(1.96587)) - - self.prob_contagion_base = prob_contagion / self.repscaling - - # Mobility constant for geographic rescaling - self.kmob = kmob - - # Proportion of daily incoming infected people from other places - self.rate_inbound = rate_inbound/self.dwell_15_day - - # TODO Probability of contagion due to residual droplets: (Might be necessary for implementing networks and POI) - self.prob_contagion_places = 0.001 - - # Probability of being asymptomatic, contagious - # and only detectable by testing - self.prob_asymptomatic = proportion_asymptomatic - - # Average recovery time - self.avg_recovery = avg_recovery_time * self.dwell_15_day - - # Proportion of detection. We use the rate as reference and - # activate testing at the rate and specified dates - self.testing_rate = proportion_detected/(days_testing_lasts * self.dwell_15_day) - self.testing_start = day_testing_start* self.dwell_15_day - self.testing_end = self.testing_start + days_testing_lasts*self.dwell_15_day - - # We need an additional variable to activate and inactivate automatic contact tracing - self.tracing_start = day_tracing_start* self.dwell_15_day - self.tracing_end = self.tracing_start + days_tracing_lasts*self.dwell_15_day - self.tracing_now = False - - # Same for isolation rate - self.isolation_rate = proportion_isolated - self.isolation_start = day_start_isolation*self.dwell_15_day - self.isolation_end = self.isolation_start + days_isolation_lasts*self.dwell_15_day - self.after_isolation = after_isolation - self.prob_isolation_effective = prob_isolation_effective - - # Same for social distancing - self.distancing = social_distance - self.distancing_start = day_distancing_start*self.dwell_15_day - self.distancing_end = self.distancing_start + days_distancing_lasts*self.dwell_15_day - - # Introduction of new agents after a specific time - self.new_agent_num = int(new_agent_proportion * self.num_agents) - self.new_agent_start = new_agent_start*self.dwell_15_day - self.new_agent_end = self.new_agent_start + new_agent_lasts*self.dwell_15_day - self.new_agent_age_mean = new_agent_age_mean - self.new_agent_prop_infected = new_agent_prop_infected - - #Code for vaccination - self.vaccination_start = day_vaccination_begin * self.dwell_15_day - self.vaccination_end = day_vaccination_end * self.dwell_15_day - self.vaccinating_now = False - # Closing of various businesses - # TODO: at the moment, we assume that closing businesses decreases the dwell time. - # A more proper implementation would a) use a power law distribution for dwell times - # and b) assign a background of dwell times first, modifying them upwards later - # for all cells. - # Alternatively, shutting restaurants corresponds to 15% of interactions in an active day, and bars to a 7% - # of those interactions - - #Initializing the start times for each spontaniously generated variant. - self.variant_start_times = {} - self.variant_start = {} - for key in self.variant_data_list: - self.variant_start_times[key] = self.variant_data_list[key]["Appearance"] * self.dwell_15_day - self.variant_start[key] = False + for key in self.model_data.variant_data_list: + self.model_data.variant_start_times[key] = self.model_data.variant_data_list[key]["Appearance"] * self.model_data.dwell_15_day + self.model_data.variant_start[key] = False # Now, a neat python trick: generate the spacing of entries and then build a map - times_list = list(np.linspace(self.new_agent_start, self.new_agent_end, self.new_agent_num, dtype=int)) + times_list = list(np.linspace(self.model_data.new_agent_start, self.model_data.new_agent_end, self.model_data.new_agent_num, dtype=int)) self.new_agent_time_map = {x:times_list.count(x) for x in times_list} # Probability of severity - self.prob_severe = proportion_severe + # self.prob_severe = proportion_severe # Number of beds where saturation limit occurs - self.max_beds_available = self.num_agents * proportion_beds_pop - self.bed_count = self.max_beds_available + # self.max_beds_available = self.num_agents * proportion_beds_pop + # self.bed_count = self.max_beds_available # CREATING AGENTS self.i = 0 if load_from_file == False: #If were creating a new model based on demographic data. - for ag in self.age_distribution: - for sg in self.sex_distribution: - r = self.age_distribution[ag]*self.sex_distribution[sg] + for ag in self.model_data.age_distribution: + for sg in self.model_data.sex_distribution: + r = self.model_data.age_distribution[ag]*self.model_data.sex_distribution[sg] num_agents = int(round(self.num_agents*r)) - mort = self.age_mortality[ag]*self.sex_mortality[sg] + mort = self.model_data.age_mortality[ag]*self.model_data.sex_mortality[sg] for k in range(num_agents): parameters = [self.i, ag, sg, mort] a = CovidAgent(self, parameters) @@ -1502,7 +1354,21 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag "Vaccine_Willing": compute_willing_agents, } - model_reporters_dict = {} + model_reporters_dict = { + "Step": compute_stepno, + "N": compute_num_agents, + "Isolated": compute_isolated, + "Vaccinated" : compute_vaccinated, + "Vaccines" : compute_vaccine_count, + "V": compute_vaccinated, + "Data_Time" : compute_datacollection_time, + "Step_Time" : compute_step_time, + "Generally_Infected": compute_generally_infected, + "Fully_Vaccinated" : compute_fully_vaccinated_count, + "Vaccine_1" : compute_vaccinated_1, + "Vaccine_2" : compute_vaccinated_2, + "Vaccine_Willing": compute_willing_agents, + } # model_reporters_dict.update(processes_dict) model_reporters_dict.update(general_reporters_dict) model_reporters_dict.update(agent_status_dict) @@ -1510,6 +1376,8 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag model_reporters_dict.update(vaccinated_status_dict) model_reporters_dict.update(variant_data_collection_dict) model_reporters_dict.update(prices_dict) + + self.datacollector = DataCollector(model_reporters = model_reporters_dict) self.model_reporters = model_reporters_dict self.model_vars = {} @@ -1540,7 +1408,7 @@ def __init__(self, num_agents, width, height, kmob, repscaling, rate_inbound, ag else: #Shouldn't this be random? Or is it intentionally set to be the 0'th agent being infected every time. a.stage = Stage.EXPOSED - self.generally_infected = self.generally_infected + 1 + self.model_data.generally_infected = self.model_data.generally_infected + 1 num_init = num_init - 1 def retrieve_model_Data(self): @@ -1589,7 +1457,7 @@ def step(self): # Check if function with arguments elif isinstance(reporter, list): self.agent_vars[var].append(reporter[0](*reporter[1])) - + self.datacollector.collect(self) data_time_B = timeit.default_timer() self.datacollection_time = data_time_B-data_time_A @@ -1598,38 +1466,38 @@ def step(self): #Running the actual sauce of the step of the model and timing it for runtime analysis. step_time_A = timeit.default_timer() - if self.stepno % self.dwell_15_day == 0: - print(f'Simulating day {self.stepno // self.dwell_15_day}') + if self.stepno % self.model_data.dwell_15_day == 0: + print(f'Simulating day {self.stepno // self.model_data.dwell_15_day}') #Adding vaccines at the beginning of every day in the model. - if self.vaccinating_now: - self.vaccine_count = self.vaccine_count + self.distribution_rate + if self.model_data.vaccination_now: + self.model_data.vaccine_count = self.model_data.vaccine_count + self.model_data.distribution_rate # Activate contact tracing only if necessary and turn it off correspondingly at the end - if not(self.tracing_now) and (self.stepno >= self.tracing_start): - self.tracing_now = True + if not(self.model_data.tracing_now) and (self.stepno >= self.model_data.tracing_start): + self.model_data.tracing_now = True - if self.tracing_now and (self.stepno > self.tracing_end): - self.tracing_now = False + if self.model_data.tracing_now and (self.stepno > self.model_data.tracing_end): + self.model_data.tracing_now = False - if not (self.vaccinating_now) and (self.stepno >= self.vaccination_start): - self.vaccinating_now = True + if not (self.model_data.vaccination_now) and (self.stepno >= self.model_data.vaccination_start): + self.model_data.vaccination_now = True - if self.vaccinating_now and (self.stepno > self.vaccination_end): - self.vaccinating_now = False + if self.model_data.vaccination_now and (self.stepno > self.model_data.vaccination_end): + self.model_data.vaccination_now = False #In the spontanious method for introducing variants we have new agents arrive that contain the variant. #For these new people coming in it will be interesting to see what their intentions are. Maybe they are just coming for a visit? - for variant in self.variant_start_times: - if not(self.variant_start[variant]) and (self.stepno > self.variant_start_times[variant]): - new_infection_count = int(self.num_agents*self.prop_initial_infected) - self.variant_start[variant] = True + for variant in self.model_data.variant_start_times: + if not(self.model_data.variant_start[variant]) and (self.stepno > self.model_data.variant_start_times[variant]): + new_infection_count = int(self.num_agents*self.model_data.prop_initial_infected) + self.model_data.variant_start[variant] = True for _ in range(0,new_infection_count): #Creates new agents that are infected with the variant ag = random.choice(list(AgeGroup)) sg = random.choice(list(SexGroup)) - mort = self.age_mortality[ag]*self.sex_mortality[sg] + mort = self.model_data.age_mortality[ag]*self.model_data.sex_mortality[sg] a = CovidAgent([self.i, ag, sg, mort, self]) self.schedule.add(a) a.variant = variant @@ -1639,12 +1507,12 @@ def step(self): self.grid.place_agent(a, (x, y)) self.i = self.i + 1 self.num_agents = self.num_agents + 1 - self.generally_infected += 1 + self.model_data.generally_infected += 1 # If new agents enter the population, create them - if (self.stepno >= self.new_agent_start) and (self.stepno < self.new_agent_end): + if (self.stepno >= self.model_data.new_agent_start) and (self.stepno < self.model_data.new_agent_end): # Check if the current step is in the new-agent time map if self.stepno in self.new_agent_time_map.keys(): # We repeat the following procedure as many times as the value stored in the map @@ -1659,12 +1527,12 @@ def step(self): in_range = True ag = AgeGroup(arange) sg = random.choice(list(SexGroup)) - mort = self.age_mortality[ag]*self.sex_mortality[sg] + mort = self.model_data.age_mortality[ag]*self.model_data.sex_mortality[sg] a = CovidAgent(self.i, ag, sg, mort, self) # Some will be infected - if bernoulli.rvs(self.new_agent_prop_infected): + if bernoulli.rvs(self.model_data.new_agent_prop_infected): a.stage = Stage.EXPOSED - self.generally_infected = self.generally_infected + 1 + self.model_data.generally_infected = self.model_data.generally_infected + 1 self.schedule.add(a) x = self.random.randrange(self.grid.width) y = self.random.randrange(self.grid.height) diff --git a/covidpolicy.py b/covidpolicy.py new file mode 100644 index 0000000..37f5017 --- /dev/null +++ b/covidpolicy.py @@ -0,0 +1,15 @@ +# Santiago Nunez-Corrales and Eric Jakobsson +# Illinois Informatics and Molecular and Cell Biology +# University of Illinois at Urbana-Champaign +# {xinyih8,nunezco,jake}@illinois.edu +from dataclasses import dataclass + + +@dataclass +class CovidPolicy: + is_default: bool + policy_type: str + spec: dict() + start_time: int + duration: int + end_time: int diff --git a/covidserver.py b/covidserver.py index 6286a91..0efbec8 100644 --- a/covidserver.py +++ b/covidserver.py @@ -3,6 +3,8 @@ # University of Illinois at Urbana-Champaign # {nunezco,jake}@illinois.edu +# python3.9 covidserver.py scenarios/Vaccination_Scenarios_Attempt_4/Variant_Data.json + # A simple tunable model for COVID-19 response from mesa.visualization.modules import CanvasGrid from mesa.visualization.modules import ChartModule @@ -15,6 +17,7 @@ from covidmodel import AgeGroup from covidmodel import SexGroup from covidmodel import ValueGroup +import database # Specific model data @@ -26,8 +29,6 @@ print(virus_param_list) - - # Observed distribution of mortality rate per age cr_age_mortality = { AgeGroup.C80toXX: 0.148, @@ -94,7 +95,7 @@ def agent_portrayal(agent): "Filled": "true", "Layer": 0, "r": 0.5} - if agent.vaccinated: + if agent.agent_data.vaccinated: portrayal["Color"] = "lime" portrayal["Layer"] = 0 elif agent.stage == Stage.SUSCEPTIBLE: @@ -345,6 +346,8 @@ def agent_portrayal(agent): data_collector_name='datacollector' ) +db = database.Database() + model_params = { "num_agents": 260, "width": 50, @@ -392,7 +395,18 @@ def agent_portrayal(agent): "distribution_rate": UserSettableParameter("slider", "distribution rate", 20, 0, 100, 1), "cost_per_vaccine": UserSettableParameter("slider", "cost_per_vaccine", 200, 10, 1000, 10), "vaccination_percent": UserSettableParameter("slider", "vaccination_percent", 0.5, 0, 1, 0.01), - "variant_data": virus_param_list + "variant_data": virus_param_list, + "db": db + # some random parameters just for testing + # "step_count": 1, + # "load_from_file": False, + # "loading_file_path": "", + # "starting_step": 0, + # "agent_storage": 0, + # "model_storage": 0, + # "agent_increment": 0, + # "model_increment": 0, + # "iteration": 1 } server = ModularServer(CovidModel, [grid,chart,chart_epidemiology,chart_cumulative_effectiveness,vaccinated_age_group,Achart,Bchart,Dchart], diff --git a/database.py b/database.py new file mode 100644 index 0000000..8e471bc --- /dev/null +++ b/database.py @@ -0,0 +1,185 @@ +import psycopg2 +from config import config + +# class for database functions +class Database: + def __init__(self): + conn = None + try: + params = config() + self.conn = psycopg2.connect(**params) + print("Connection success") + + except (Exception, psycopg2.DatabaseError) as error: + print('error') + print(error) + + # insert one agent into database + def insert_agent(self, data): + """ insert a new agent into the trace table """ + sql = """ + INSERT INTO agent( + uuid, + age_group, + sex_group, + vaccine_willingness, + incubation_time, + dwelling_time, + recovery_time, + prob_contagion, + mortality_value, + severity_value, + curr_dwelling, + curr_incubation, + curr_recovery, + curr_asymptomatic, + isolated, + isolated_but_inefficient, + test_chance, + in_isolation, + in_distancing, + in_testing, + astep, + tested, + occupying_bed, + cumul_private_value, + cumul_public_value, + employed, + tested_traced, + tracing_delay, + tracing_counter, + vaccinated, + safetymultiplier, + current_effectiveness, + vaccination_day, + vaccine_count, + dosage_eligible, + fully_vaccinated, + variant + ) VALUES(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + """ + try: + self.cur = self.conn.cursor() + self.cur.executemany(sql, data) + except (Exception, psycopg2.DatabaseError) as error: + print(error) + + # insert one model into the database + def insert_model(self, data): + """ insert a new model into the experiment table """ + sql = """ + INSERT INTO experiment( + uuid, + test_cost, + alpha_private, + alpha_public, + fully_vaccinated_count, + prop_initial_infected, + generally_infected, + cumul_vaccine_count, + cumul_test_cost, + total_costs, + vaccination_chance, + vaccination_stage, + vaccine_cost, + day_vaccination_begin, + day_vaccination_end, + effective_period, + effectiveness, + distribution_rate, + vaccine_count, + vaccinated_count, + vaccinated_percent, + vaccine_dosage, + effectiveness_per_dosage, + dwell_15_day, + avg_dwell, + avg_incubation, + repscaling, + prob_contagion_base, + kmob, + rate_inbound, + prob_contagion_places, + prob_asymptomatic, + avg_recovery, + testing_rate, + testing_start, + testing_end, + tracing_start, + tracing_end, + tracing_now, + isolation_rate, + isolation_start, + isolation_end, + after_isolation, + prob_isolation_effective, + distancing, + distancing_start, + distancing_end, + new_agent_num, + new_agent_start, + new_agent_end, + new_agent_age_mean, + new_agent_prop_infected, + vaccination_start, + vaccination_end, + vaccination_now, + prob_severe, + max_bed_available, + bed_count + ) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) + """ + try: + self.cur = self.conn.cursor() + self.cur.executemany(sql, data) + except (Exception, psycopg2.DatabaseError) as error: + print(error) + + # insert one summary into the database + def insert_summary(self, data): + """ insert a new summary into the summary table """ + sql = """ + INSERT INTO summary( + uuid, + cumul_priv_value, + cumul_publ_value, + cumul_test_cost, + rt, + employed, + unemployed, + tested, + traced, + cumul_vaccine_cost, + cumul_cost, + step, + n, + isolated, + vaccinated, + vaccines, + v, + data_time, + step_time, + generally_infected, + fully_vaccinated, + vaccine_1, + vaccine_2, + vaccine_willing + ) VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) + """ + try: + self.cur = self.conn.cursor() + self.cur.executemany(sql, data) + except (Exception, psycopg2.DatabaseError) as error: + print('error') + print(error) + + # commit changes to database + def commit(self): + self.conn.commit() + + # close connection to database + def close(self): + if self.cur is not None: + self.cur.close() + if self.conn is not None: + self.conn.close() \ No newline at end of file diff --git a/database/connect.py b/database/connect.py new file mode 100644 index 0000000..3e19c7d --- /dev/null +++ b/database/connect.py @@ -0,0 +1,38 @@ +#!/usr/bin/python +import psycopg2 +from config import config + +def connect(): + """ Connect to the PostgreSQL database server """ + conn = None + try: + # read connection parameters + params = config() + + # connect to the PostgreSQL server + print('Connecting to the PostgreSQL database...') + conn = psycopg2.connect(**params) + + # create a cursor + cur = conn.cursor() + + # execute a statement + print('PostgreSQL database version:') + cur.execute('SELECT version()') + + # display the PostgreSQL database server version + db_version = cur.fetchone() + print(db_version) + + # close the communication with the PostgreSQL + cur.close() + except (Exception, psycopg2.DatabaseError) as error: + print(error) + finally: + if conn is not None: + conn.close() + print('Database connection closed.') + + +if __name__ == '__main__': + connect() diff --git a/database/database.ini b/database/database.ini new file mode 100644 index 0000000..826021d --- /dev/null +++ b/database/database.ini @@ -0,0 +1,5 @@ +[postgresql] +host=localhost +database=covidmesa +user=bodasong +password=none \ No newline at end of file diff --git a/database/insert.py b/database/insert.py new file mode 100644 index 0000000..641c8ca --- /dev/null +++ b/database/insert.py @@ -0,0 +1,32 @@ +#!/usr/bin/python +import psycopg2 +from config import config + + +def insert_data(data): + """ insert a new vendor into the vendors table """ + sql = "INSERT INTO summary(step, cummul_priv_value, cummul_publ_value, cummul_test_cost, rt, employed, unemployed) VALUES(%s, %s, %s, %s, %s, %s, %s)" + conn = None + try: + # read database configuration + params = config() + # connect to the PostgreSQL database + conn = psycopg2.connect(**params) + # create a new cursor + cur = conn.cursor() + # execute the INSERT statement + cur.execute(sql, data) + # commit the changes to the database + conn.commit() + # close communication with the database + cur.close() + except (Exception, psycopg2.DatabaseError) as error: + print(error) + finally: + if conn is not None: + conn.close() + +if __name__ == '__main__': + # insert one vendor + insert_data((0, 0.0, 0.0, 0.0, 0, 200 ,0)) + \ No newline at end of file diff --git a/model_data_class.py b/model_data_class.py new file mode 100644 index 0000000..e0cc0f3 --- /dev/null +++ b/model_data_class.py @@ -0,0 +1,77 @@ +from dataclasses import dataclass + +# the data class for CovidModel +@dataclass +class ModelDataClass: + age_mortality: dict() + sex_mortality: dict() + age_distribution: dict() + sex_distribution: dict() + stage_value_dist: dict() + test_cost: int + alpha_private: float + alpha_public: float + fully_vaccinated_count: int + prop_initial_infected: float + generally_infected: int + cumul_vaccine_cost: int + cumul_test_cost: int + total_costs: int + vaccination_chance: float + vaccination_stage: int + vaccine_cost: int + day_vaccination_begin: int + day_vaccination_end: int + effective_period: int + effectiveness: float + distribution_rate: int + vaccine_count: int + vaccinated_count: int + vaccinated_percent: float + vaccine_dosage: int + effectiveness_per_dosage: float + variant_data_list: dict() + agent_parameter_names: list() + dwell_15_day: int + avg_dwell: int + avg_incubation: int + repscaling: int + prob_contagion_base: float + kmob: float + rate_inbound: float + prob_contagion_places: float + prob_asymptomatic: float + avg_recovery: int + testing_rate: float + testing_start: int + testing_end: int + tracing_start: int + tracing_end: int + tracing_now: bool + isolation_rate: float + isolation_start: int + isolation_end: int + after_isolation: int + prob_isolation_effective: float + distancing: float + distancing_start: int + distancing_end: int + new_agent_num: int + new_agent_start: int + new_agent_end: int + new_agent_age_mean: int + new_agent_prop_infected: float + vaccination_start: int + vaccination_end: int + vaccination_now: bool + variant_start_times: dict() + variant_start: dict() + prob_severe: float + max_bed_available: int + bed_count: int + locations: dict() + + + + + \ No newline at end of file diff --git a/model_runner_group.py b/model_runner_group.py index b592976..3d6c79f 100644 --- a/model_runner_group.py +++ b/model_runner_group.py @@ -4,6 +4,7 @@ # {nunezco,jake}@illinois.edu # A simple tunable model for COVID-19 response +#from sympy import false from batchrunner_local import BatchRunnerMP from multiprocessing import freeze_support from covidmodel import CovidModel @@ -20,31 +21,11 @@ import multiprocessing import os import glob +import timeit +import click - -directory_list = [] -filenames_list = [] -virus_data_file = open(str(sys.argv[1])) -for argument in sys.argv[2:]: - directory_list.append(argument) - -for directory in directory_list: - file_list = glob.glob(f"{directory}/*.json") - for file in file_list: - filenames_list.append(file) - -# Read JSON file -data_list = [] -for file_params in filenames_list: - with open(file_params) as f: - data = json.load(f) - data_list.append(data) - -indexes = [range(len(data_list))] -virus_data = json.load(virus_data_file) - -def runModelScenario(data,index): +def runModelScenario(data,index,virus_data,filenames_list,is_checkpoint): print(f"Location: { data['location'] }") print(f"Description: { data['description'] }") print(f"Prepared by: { data['prepared-by'] }") @@ -112,112 +93,282 @@ def runModelScenario(data,index): } } - model_params = { - "num_agents": data["model"]["epidemiology"]["num_agents"], - "width": data["model"]["epidemiology"]["width"], - "height": data["model"]["epidemiology"]["height"], - "repscaling": data["model"]["epidemiology"]["repscaling"], - "kmob": data["model"]["epidemiology"]["kmob"], - "age_mortality": age_mortality, - "sex_mortality": sex_mortality, - "age_distribution": age_distribution, - "sex_distribution": sex_distribution, - "prop_initial_infected": data["model"]["epidemiology"]["prop_initial_infected"], - "rate_inbound": data["model"]["epidemiology"]["rate_inbound"], - "avg_incubation_time": data["model"]["epidemiology"]["avg_incubation_time"], - "avg_recovery_time": data["model"]["epidemiology"]["avg_recovery_time"], - "proportion_asymptomatic": data["model"]["epidemiology"]["proportion_asymptomatic"], - "proportion_severe": data["model"]["epidemiology"]["proportion_severe"], - "prob_contagion": data["model"]["epidemiology"]["prob_contagion"], - "proportion_beds_pop": data["model"]["epidemiology"]["proportion_beds_pop"], - "proportion_isolated": data["model"]["policies"]["isolation"]["proportion_isolated"], - "day_start_isolation": data["model"]["policies"]["isolation"]["day_start_isolation"], - "days_isolation_lasts": data["model"]["policies"]["isolation"]["days_isolation_lasts"], - "after_isolation": data["model"]["policies"]["isolation"]["after_isolation"], - "prob_isolation_effective": data["model"]["policies"]["isolation"]["prob_isolation_effective"], - "social_distance": data["model"]["policies"]["distancing"]["social_distance"], - "day_distancing_start": data["model"]["policies"]["distancing"]["day_distancing_start"], - "days_distancing_lasts": data["model"]["policies"]["distancing"]["days_distancing_lasts"], - "proportion_detected": data["model"]["policies"]["testing"]["proportion_detected"], - "day_testing_start": data["model"]["policies"]["testing"]["day_testing_start"], - "days_testing_lasts": data["model"]["policies"]["testing"]["days_testing_lasts"], - "day_tracing_start": data["model"]["policies"]["tracing"]["day_tracing_start"], - "days_tracing_lasts": data["model"]["policies"]["tracing"]["days_tracing_lasts"], - "new_agent_proportion": data["model"]["policies"]["massingress"]["new_agent_proportion"], - "new_agent_start": data["model"]["policies"]["massingress"]["new_agent_start"], - "new_agent_lasts": data["model"]["policies"]["massingress"]["new_agent_lasts"], - "new_agent_age_mean": data["model"]["policies"]["massingress"]["new_agent_age_mean"], - "new_agent_prop_infected": data["model"]["policies"]["massingress"]["new_agent_prop_infected"], - "stage_value_matrix": value_distibution, - "test_cost": data["model"]["value"]["test_cost"], - "alpha_private": data["model"]["value"]["alpha_private"], - "alpha_public": data["model"]["value"]["alpha_public"], - "day_vaccination_begin": data["model"]["policies"]["vaccine_rollout"]["day_vaccination_begin"], - "day_vaccination_end": data["model"]["policies"]["vaccine_rollout"]["day_vaccination_end"], - "effective_period": data["model"]["policies"]["vaccine_rollout"]["effective_period"], - "effectiveness": data["model"]["policies"]["vaccine_rollout"]["effectiveness"], - "distribution_rate": data["model"]["policies"]["vaccine_rollout"]["distribution_rate"], - "cost_per_vaccine":data["model"]["policies"]["vaccine_rollout"]["cost_per_vaccine"], - "vaccination_percent": data["model"]["policies"]["vaccine_rollout"]["vaccination_percent"] - } + # load from file + if is_checkpoint: + model_params = { + "num_agents": data["model"]["epidemiology"]["num_agents"], + "width": data["model"]["epidemiology"]["width"], + "height": data["model"]["epidemiology"]["height"], + "repscaling": data["model"]["epidemiology"]["repscaling"], + "kmob": data["model"]["epidemiology"]["kmob"], + "age_mortality": age_mortality, + "sex_mortality": sex_mortality, + "age_distribution": age_distribution, + "sex_distribution": sex_distribution, + "prop_initial_infected": data["model"]["epidemiology"]["prop_initial_infected"], + "rate_inbound": data["model"]["epidemiology"]["rate_inbound"], + "avg_incubation_time": data["model"]["epidemiology"]["avg_incubation_time"], + "avg_recovery_time": data["model"]["epidemiology"]["avg_recovery_time"], + "proportion_asymptomatic": data["model"]["epidemiology"]["proportion_asymptomatic"], + "proportion_severe": data["model"]["epidemiology"]["proportion_severe"], + "prob_contagion": data["model"]["epidemiology"]["prob_contagion"], + "proportion_beds_pop": data["model"]["epidemiology"]["proportion_beds_pop"], + "proportion_isolated": data["model"]["policies"]["isolation"]["proportion_isolated"], + "day_start_isolation": data["model"]["policies"]["isolation"]["day_start_isolation"], + "days_isolation_lasts": data["model"]["policies"]["isolation"]["days_isolation_lasts"], + "after_isolation": data["model"]["policies"]["isolation"]["after_isolation"], + "prob_isolation_effective": data["model"]["policies"]["isolation"]["prob_isolation_effective"], + "social_distance": data["model"]["policies"]["distancing"]["social_distance"], + "day_distancing_start": data["model"]["policies"]["distancing"]["day_distancing_start"], + "days_distancing_lasts": data["model"]["policies"]["distancing"]["days_distancing_lasts"], + "proportion_detected": data["model"]["policies"]["testing"]["proportion_detected"], + "day_testing_start": data["model"]["policies"]["testing"]["day_testing_start"], + "days_testing_lasts": data["model"]["policies"]["testing"]["days_testing_lasts"], + "day_tracing_start": data["model"]["policies"]["tracing"]["day_tracing_start"], + "days_tracing_lasts": data["model"]["policies"]["tracing"]["days_tracing_lasts"], + "new_agent_proportion": data["model"]["policies"]["massingress"]["new_agent_proportion"], + "new_agent_start": data["model"]["policies"]["massingress"]["new_agent_start"], + "new_agent_lasts": data["model"]["policies"]["massingress"]["new_agent_lasts"], + "new_agent_age_mean": data["model"]["policies"]["massingress"]["new_agent_age_mean"], + "new_agent_prop_infected": data["model"]["policies"]["massingress"]["new_agent_prop_infected"], + "stage_value_matrix": value_distibution, + "test_cost": data["model"]["value"]["test_cost"], + "alpha_private": data["model"]["value"]["alpha_private"], + "alpha_public": data["model"]["value"]["alpha_public"], + "day_vaccination_begin": data["model"]["policies"]["vaccine_rollout"]["day_vaccination_begin"], + "day_vaccination_end": data["model"]["policies"]["vaccine_rollout"]["day_vaccination_end"], + "effective_period": data["model"]["policies"]["vaccine_rollout"]["effective_period"], + "effectiveness": data["model"]["policies"]["vaccine_rollout"]["effectiveness"], + "distribution_rate": data["model"]["policies"]["vaccine_rollout"]["distribution_rate"], + "cost_per_vaccine":data["model"]["policies"]["vaccine_rollout"]["cost_per_vaccine"], + "vaccination_percent": data["model"]["policies"]["vaccine_rollout"]["vaccination_percent"], + "step_count": data["ensemble"]["steps"], + "load_from_file": data["model"]["initialization"]["load_from_file"], + "loading_file_path": data["model"]["initialization"]["loading_file_path"], + "starting_step": data["model"]["initialization"]["starting_step"], + "agent_storage": data["output"]["agent_storage"], + "model_storage": data["output"]["model_storage"], + "agent_increment": data["output"]["agent_increment"], + "model_increment": data["output"]["model_increment"], + "location_type": data["model"]["locations"]["type"], + "location_spec": data["model"]["locations"]["spec"] + } + # start from time 0 + else: + model_params = { + "num_agents": data["model"]["epidemiology"]["num_agents"], + "width": data["model"]["epidemiology"]["width"], + "height": data["model"]["epidemiology"]["height"], + "repscaling": data["model"]["epidemiology"]["repscaling"], + "kmob": data["model"]["epidemiology"]["kmob"], + "age_mortality": age_mortality, + "sex_mortality": sex_mortality, + "age_distribution": age_distribution, + "sex_distribution": sex_distribution, + "prop_initial_infected": data["model"]["epidemiology"]["prop_initial_infected"], + "rate_inbound": data["model"]["epidemiology"]["rate_inbound"], + "avg_incubation_time": data["model"]["epidemiology"]["avg_incubation_time"], + "avg_recovery_time": data["model"]["epidemiology"]["avg_recovery_time"], + "proportion_asymptomatic": data["model"]["epidemiology"]["proportion_asymptomatic"], + "proportion_severe": data["model"]["epidemiology"]["proportion_severe"], + "prob_contagion": data["model"]["epidemiology"]["prob_contagion"], + "proportion_beds_pop": data["model"]["epidemiology"]["proportion_beds_pop"], + "proportion_isolated": data["model"]["policies"]["isolation"]["proportion_isolated"], + "day_start_isolation": data["model"]["policies"]["isolation"]["day_start_isolation"], + "days_isolation_lasts": data["model"]["policies"]["isolation"]["days_isolation_lasts"], + "after_isolation": data["model"]["policies"]["isolation"]["after_isolation"], + "prob_isolation_effective": data["model"]["policies"]["isolation"]["prob_isolation_effective"], + "social_distance": data["model"]["policies"]["distancing"]["social_distance"], + "day_distancing_start": data["model"]["policies"]["distancing"]["day_distancing_start"], + "days_distancing_lasts": data["model"]["policies"]["distancing"]["days_distancing_lasts"], + "proportion_detected": data["model"]["policies"]["testing"]["proportion_detected"], + "day_testing_start": data["model"]["policies"]["testing"]["day_testing_start"], + "days_testing_lasts": data["model"]["policies"]["testing"]["days_testing_lasts"], + "day_tracing_start": data["model"]["policies"]["tracing"]["day_tracing_start"], + "days_tracing_lasts": data["model"]["policies"]["tracing"]["days_tracing_lasts"], + "new_agent_proportion": data["model"]["policies"]["massingress"]["new_agent_proportion"], + "new_agent_start": data["model"]["policies"]["massingress"]["new_agent_start"], + "new_agent_lasts": data["model"]["policies"]["massingress"]["new_agent_lasts"], + "new_agent_age_mean": data["model"]["policies"]["massingress"]["new_agent_age_mean"], + "new_agent_prop_infected": data["model"]["policies"]["massingress"]["new_agent_prop_infected"], + "stage_value_matrix": value_distibution, + "test_cost": data["model"]["value"]["test_cost"], + "alpha_private": data["model"]["value"]["alpha_private"], + "alpha_public": data["model"]["value"]["alpha_public"], + "day_vaccination_begin": data["model"]["policies"]["vaccine_rollout"]["day_vaccination_begin"], + "day_vaccination_end": data["model"]["policies"]["vaccine_rollout"]["day_vaccination_end"], + "effective_period": data["model"]["policies"]["vaccine_rollout"]["effective_period"], + "effectiveness": data["model"]["policies"]["vaccine_rollout"]["effectiveness"], + "distribution_rate": data["model"]["policies"]["vaccine_rollout"]["distribution_rate"], + "cost_per_vaccine":data["model"]["policies"]["vaccine_rollout"]["cost_per_vaccine"], + "vaccination_percent": data["model"]["policies"]["vaccine_rollout"]["vaccination_percent"], + "location_type": data["model"]["locations"]["type"], + "location_spec": data["model"]["locations"]["spec"] + } + virus_param_list = [] for virus in virus_data["variant"]: virus_param_list.append(virus_data["variant"][virus]) model_params["variant_data"] = virus_param_list + + db = Database() + model_params["db"] = db + var_params = {"dummy": range(25,50,25)} num_iterations = data["ensemble"]["runs"] num_steps = data["ensemble"]["steps"] - batch_run = BatchRunnerMP( - CovidModel, - nr_processes=num_iterations, - fixed_parameters=model_params, - variable_parameters=var_params, - iterations=num_iterations, - max_steps=num_steps, - model_reporters={ - "Step": compute_stepno, - "CummulPrivValue": compute_cumul_private_value, - "CummulPublValue": compute_cumul_public_value, - "CummulTestCost": compute_cumul_testing_cost, - "Rt": compute_eff_reprod_number, - "Employed": compute_employed, - "Unemployed": compute_unemployed - }, - display_progress=True) - - print("Parametrization complete:") - print("") - print(f"Running file {filenames_list[index]}") - print("") - print(f"Executing an ensemble of size {num_iterations} using {num_steps} steps with {num_iterations} machine cores...") + + if is_checkpoint: + batch_run = BatchRunnerMP( + CovidModel, + nr_processes=num_iterations, + fixed_parameters=model_params, + variable_parameters=var_params, + iterations= num_iterations, + max_steps=num_steps, + model_reporters={}, + agent_reporters={}, + display_progress=True + ) + else: + batch_run = BatchRunnerMP( + CovidModel, + nr_processes=num_iterations, + fixed_parameters=model_params, + variable_parameters=var_params, + iterations=num_iterations, + max_steps=num_steps, + model_reporters={ + "Step": compute_stepno, + "CummulPrivValue": compute_cumul_private_value, + "CummulPublValue": compute_cumul_public_value, + "CummulTestCost": compute_cumul_testing_cost, + "Rt": compute_eff_reprod_number, + "Employed": compute_employed, + "Unemployed": compute_unemployed + }, + display_progress=True + ) + + if is_checkpoint: + print("Parametrization complete:") + print("") + print("") + print(f"Executing an ensemble of size {num_iterations} using {num_steps} steps with {num_iterations} machine cores for agents...") + else: + print("Parametrization complete:") + print("") + print(f"Running file {filenames_list[index]}") + print("") + print(f"Executing an ensemble of size {num_iterations} using {num_steps} steps with {num_iterations} machine cores...") + cm_runs = batch_run.run_all() + db.close() - print("") - print("Saving results to file...") + if is_checkpoint: + model_ldfs = [] + agent_ldfs = [] + time_A = timeit.default_timer() + i = 0 + for cm in cm_runs.values(): + cm[0]["Iteration"] = i + cm[1]["Iteration"] = i + model_ldfs.append(cm[0]) + agent_ldfs.append(cm[1]) + i = i + 1 + model_dfs = pd.concat(model_ldfs) + agent_dfs = pd.concat(agent_ldfs) + model_save_file = data["output"]["model_save_file"] + agent_save_file = data["output"]["agent_save_file"] + #TODO-create the nomenclature for the nature of the save file for both model and agent data. (Very important for organizing test runs for different policy evaluations) + model_dfs.to_csv(model_save_file) + agent_dfs.to_csv(agent_save_file) + time_B = timeit.default_timer() + return (time_B - time_A) + else: + print("") + print("Saving results to file...") + ldfs = [] + i = 0 + for cm in cm_runs.values(): + cm["Iteration"] = i + ldfs.append(cm) + i = i + 1 + file_out = data["output"]["prefix"] + dfs = pd.concat(ldfs) + dfs.to_csv(file_out + ".csv") + print(f"Simulation {index} completed without errors.") - ldfs = [] - i = 0 - for cm in cm_runs.values(): - cm["Iteration"] = i - ldfs.append(cm) - i = i + 1 +if __name__ == '__main__': + argv1 = sys.argv[1] + argv2 = sys.argv[2] + if (type(argv1) is int and type(argv2) is int): + is_checkpoint = True + else: + is_checkpoint = False - file_out = data["output"]["prefix"] + directory_list = [] + filenames_list = [] - dfs = pd.concat(ldfs) - dfs.to_csv(file_out + ".csv") - print(f"Simulation {index} completed without errors.") + if is_checkpoint: + begin = int(sys.argv[1]) + end = int(sys.argv[2]) + print(sys.argv[4:]) + print(begin, end) + virus_data_file = open(str(sys.argv[3])) + for argument in sys.argv[4:]: + directory_list.append(str(argument)) + else: + virus_data_file = open(str(sys.argv[1])) + for argument in sys.argv[2:]: + directory_list.append(argument) + for directory in directory_list: + file_list = glob.glob(f"{directory}/*.json") + for file in file_list: + filenames_list.append(file) -if __name__ == '__main__': - processes = [] - for index,data in enumerate(data_list): - p = multiprocessing.Process(target=runModelScenario, args=[data, index]) - p.start() - processes.append(p) - - for process in processes: - process.join() \ No newline at end of file + # Read JSON file + data_list = [] + for file_params in filenames_list: + with open(file_params) as f: + data = json.load(f) + data_list.append(data) + + indexes = [range(len(data_list))] + virus_data = json.load(virus_data_file) + if is_checkpoint: + total_iterations = 0 + parameters = [] + for index, data in enumerate(data_list): + parameter = [] + total_iterations += data["ensemble"]["runs"] + parameter.append(data) + parameter.append(index) + parameter.append(virus_data) + parameter.append(filenames_list) + parameter.append(is_checkpoint) + parameters.append(parameter) + + manager = multiprocessing.Manager() + return_dict = manager.dict() + processes = [] + for parameter in parameters: + process = multiprocessing.Process(target = runModelScenario, args = parameter) + process.start() + processes.append(process) + + for _ in range(len(processes)): + process.join() + else: + processes = [] + for index,data in enumerate(data_list): + p = multiprocessing.Process(target=runModelScenario, args=[data,index,virus_data,filenames_list,is_checkpoint]) + p.start() + processes.append(p) + + for process in processes: + process.join() \ No newline at end of file diff --git a/outcomes/cu-25-nisol.csv.root b/outcomes/cu-25-nisol.csv.root new file mode 100644 index 0000000..f5783d0 Binary files /dev/null and b/outcomes/cu-25-nisol.csv.root differ diff --git a/outcomes/cu-25-yisol.csv.root b/outcomes/cu-25-yisol.csv.root new file mode 100644 index 0000000..6dab8ff Binary files /dev/null and b/outcomes/cu-25-yisol.csv.root differ diff --git a/outcomes/cu-counterfactual.csv.root b/outcomes/cu-counterfactual.csv.root new file mode 100644 index 0000000..a5e3617 Binary files /dev/null and b/outcomes/cu-counterfactual.csv.root differ diff --git a/outcomes/cu-current-R0-callibration.csv b/outcomes/cu-current-R0-callibration.csv index 29fe45e..b1d6317 100644 --- a/outcomes/cu-current-R0-callibration.csv +++ b/outcomes/cu-current-R0-callibration.csv @@ -1,4 +1,4 @@ -,Step,N,Susceptible,Exposed,Asymptomatic,SymptQuarantined,AsymptQuarantined,Severe,Recovered,Deceased,Isolated,CumulPrivValue,CumulPublValue,CumulTestCost,Rt,Employed,Unemployed,Tested,Traced,Iteration +A,Step,N,Susceptible,Exposed,Asymptomatic,SymptQuarantined,AsymptQuarantined,Severe,Recovered,Deceased,Isolated,CumulPrivValue,CumulPublValue,CumulTestCost,Rt,Employed,Unemployed,Tested,Traced,Iteration 0,0,1000,0.998,0.002,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,3.0843187199999997,1.0,0.0,0,0,0 1,1,1000,0.998,0.002,0.0,0.0,0.0,0.0,0.0,0.0,0.448,0.0211,0.19069999999999998,0.0,0.8674646399999998,1.0,0.0,0,0,0 2,2,1000,0.998,0.002,0.0,0.0,0.0,0.0,0.0,0.0,0.448,0.032,0.291,0.0,1.0602345599999998,1.0,0.0,0,0,0 diff --git a/outcomes/cu-current-R0-callibration.root b/outcomes/cu-current-R0-callibration.root new file mode 100644 index 0000000..7229098 Binary files /dev/null and b/outcomes/cu-current-R0-callibration.root differ diff --git a/policyhandler.py b/policyhandler.py new file mode 100644 index 0000000..3e96188 --- /dev/null +++ b/policyhandler.py @@ -0,0 +1,164 @@ +# Santiago Nunez-Corrales and Eric Jakobsson +# Illinois Informatics and Molecular and Cell Biology +# University of Illinois at Urbana-Champaign +# {xinyih8,nunezco,jake}@illinois.edu +from typing import List +from covidpolicy import CovidPolicy + +import sys + + +class PolicyHandler: + + def __init__(self): + self.policies = [] + + def parse_policy(self, policy_json) -> CovidPolicy: + # Implement this + # Also use the duration + start time to compute end time + + is_default = policy_json["is_default"] + policy_type = policy_json["isolation"] + spec = policy_json["spec"] + start_time = policy_json["start_time"] + duration = policy_json["duration"] + end_time = policy_json["start_time"] + policy_json["duration"] + self.policies.append(CovidPolicy( + is_default, + policy_type, + spec, + start_time, + duration, + end_time + )) + + + def parse_all_policies(self, all_policies: List[CovidPolicy]): + # Implement this + for p in all_policies: + self.parse_policy(p) + + self.check_unique_defaults() + self.check_overlaps() + + def add_policy(self, policy: CovidPolicy): + # Add the policy to the list of policies only when they have not been + # included before + + # Test + self.policies.append(policy) + + def check_overlaps(self): + ## TODO: implement this + + ## If overlaps exist, call sys.exit(1) + # Policies of different type are ok. Only + for p in self.policies: + for m in self.policies: + if p.policy_type == m.policy_type: + if p.start_time > m.start_time and p.start_time < m.end_time: + sys.exit(1) + + def check_unique_defaults(self): + # Implement this + for p in self.policies: + for m in self.policies: + if p.policy_type == m.policy_type: + if p.is_default == True and m.is_default == True: + print("error: Two defaults") + + def filter_unique_defaults(self): + # Select only those that are defaults after veryfing they are unique + # Implement this filter using the property `is_default` from CovidPolicy + # This happen during initialization self.check_unique_defaults() + + unique_defaults = [] + for p in self.policies: + if p.is_default == True: + unique_defaults.append(p) + + return unique_defaults # Replace by a list comprehension + # Python higher order functions + + def filter_by_start_time(self, time): + # Implement the filter + # Observations: + # + # 1. Current time IS different from the step number + # 2. CovidPolicy objects -> the property start_time is an attribute + # 3. List comprehensions -> use it + start = [] + for p in self.policies: + if p.start_time == time: + start.append(p) + + return start + + def filter_by_end_time(self, time): + # Implement the filter + end = [] + for p in self.policies: + if p.end_time == time: + end.append(p) + + return end + + def set_default(self, model_dataclass): + # Implement this + # + # 1. Filter unique defaults + # 2. Apply all defaults + + defaults = self.filter_unique_defaults() + + for p in defaults: + self.apply_policy_measure(p, model_dataclass) + + def apply_policy_measure(self, policy: CovidPolicy, model_dataclass): + policy_functions = { + "isolation": self.apply_isolation, + "tracing": self.apply_contact_tracing, + "distancing": self.apply_social_and_masks, + "vaccination": self.apply_vaccination + } + + policy_functions[policy.policy_type](policy, model_dataclass) + + def apply_isolation(self, policy, model_dataclass): + model_dataclass.isolation_rate = policy.spec["isolation_rate"] + model_dataclass.prob_isolation_effective = policy.spec["prob_isolation_effective"] + + def apply_vaccination(self, policy, model_dataclass): + # Implement + model_dataclass.vaccination_chance = policy.spec["vaccination_chance"] + model_dataclass.vaccine_cost = policy.spec["vaccine_cost"] + model_dataclass.vaccine_count = policy.spec["vaccine_count"] + model_dataclass.vaccinated_count = policy.spec["vaccinated_count"] + model_dataclass.vaccinated_percent = policy.spec["vaccinated_percent"] + + + def apply_social_and_masks(self, policy, model_dataclass): + # Implement + model_dataclass.testing_rate = policy.spec["testing_rate"] + model_dataclass.distancing = policy.spec["distancing"] + + def apply_contact_tracing(self, policy, model_dataclass): + # Implement + #????????? + pass + + def dispatch(self, model_dataclass, time): + # Obtain all policies that start at this moment and apply them + start_now = self.filter_by_start_time(time) + + for p in start_now: + self.apply_policy_measure(p, model_dataclass) + + def reverse_dispatch(self, model_dataclass, time): + # Obtain all policies that start at this moment and apply them + start_now = self.filter_by_end_time() + + for p in start_now: + self.apply_policy_measure(p, model_dataclass) + + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b88811e..1865dc2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ arrow==0.15.5 binaryornot==0.4.4 certifi==2019.11.28 -chardet==3.0.4 -click==8.1 -cookiecutter==1.7.0 +chardet +click +cookiecutter cycler==0.10.0 decorator==4.4.2 dill==0.3.1.1 @@ -13,11 +13,11 @@ Jinja2==2.11.3 jinja2-time==0.2.0 kiwisolver==1.1.0 MarkupSafe==1.1.1 -matplotlib==3.4.2 +matplotlib Mesa==0.8.6 multiprocess==0.70.9 -networkx==2.4 -numpy==1.22 +networkx +numpy==1.22.2 pandas pathos==0.2.5 pox==0.2.7 @@ -35,3 +35,4 @@ tqdm==4.44.1 urllib3 whichcraft==0.6.1 psutil +psycopg2 diff --git a/requirements_conda.txt b/requirements_conda.txt index f035b50..58fc70b 100644 --- a/requirements_conda.txt +++ b/requirements_conda.txt @@ -2,8 +2,8 @@ arrow==0.15.5 binaryornot==0.4.4 certifi==2019.11.28 chardet==3.0.4 -click==7.1.1 -cookiecutter==1.7.0 +click +cookiecutter cycler==0.10.0 decorator==4.4.2 dill==0.3.1.1 @@ -13,11 +13,11 @@ Jinja2==2.11.3 jinja2-time==0.2.0 kiwisolver==1.1.0 MarkupSafe==1.1.1 -matplotlib==3.4.2 +matplotlib Mesa==0.8.6 multiprocess==0.70.9 -networkx==2.4 -numpy==1.18.2 +networkx +numpy==1.22.2 pandas pathos==0.2.5 pox==0.2.7 @@ -34,4 +34,4 @@ tornado==6.0.4 tqdm==4.44.1 urllib3==1.26.5 whichcraft==0.6.1 -psutil \ No newline at end of file +psutil diff --git a/root_scripts/777cu-25-nisol.csv, CumulPrivValue.ps b/root_scripts/777cu-25-nisol.csv, CumulPrivValue.ps new file mode 100644 index 0000000..d171248 Binary files /dev/null and b/root_scripts/777cu-25-nisol.csv, CumulPrivValue.ps differ diff --git a/root_scripts/777cu-25-nisol.csv, Rt.ps b/root_scripts/777cu-25-nisol.csv, Rt.ps new file mode 100644 index 0000000..8a5b22a Binary files /dev/null and b/root_scripts/777cu-25-nisol.csv, Rt.ps differ diff --git a/root_scripts/777cu-current-R0-callibration, Susceptible.ps b/root_scripts/777cu-current-R0-callibration, Susceptible.ps new file mode 100644 index 0000000..cdec329 Binary files /dev/null and b/root_scripts/777cu-current-R0-callibration, Susceptible.ps differ diff --git a/root_scripts/MakeTree.C b/root_scripts/MakeTree.C new file mode 100644 index 0000000..0abf30d --- /dev/null +++ b/root_scripts/MakeTree.C @@ -0,0 +1,13 @@ +void MakeTree(TString csvFile = "cu-current-R0-callibration") { + + TFile *hfile = hfile = TFile::Open("../outcomes/" + csvFile + ".root","RECREATE"); + + TTree *tree = new TTree("T", csvFile + "Tree"); + + tree->ReadFile("../outcomes/" + csvFile + ".csv","index/I:Step/I:N/D:Susceptible/D:Exposed/D:Asymptomatic/D:SymptQuarantined/D:AsymptQuarantined/D:Severe/D:Recovered/D:Deceased/D:Isolated/D:CumulPrivValue/D:CumulPublValue/D:CumulTestCost/D:Rt/D:Employed/D:Unemployed/D:Tested/D:Traced/D:Iteration"); + + tree->Print(); + tree->Write(); + + delete hfile; +} diff --git a/root_scripts/cu-25-yisol.csv, Severe.ps b/root_scripts/cu-25-yisol.csv, Severe.ps new file mode 100644 index 0000000..310bf34 Binary files /dev/null and b/root_scripts/cu-25-yisol.csv, Severe.ps differ diff --git a/root_scripts/cu-current-R0-callibration, Susceptible.ps b/root_scripts/cu-current-R0-callibration, Susceptible.ps new file mode 100644 index 0000000..3c43c02 Binary files /dev/null and b/root_scripts/cu-current-R0-callibration, Susceptible.ps differ diff --git a/root_scripts/draw.c b/root_scripts/draw.c new file mode 100644 index 0000000..48ef9b1 --- /dev/null +++ b/root_scripts/draw.c @@ -0,0 +1,65 @@ +void draw(TString columnName = "Susceptible",TString csvFile = "cu-current-R0-callibration",Double_t c_level = 0.95) { + Int_t Step; + Double_t Susceptible; + + TFile *hfile = TFile::Open("../outcomes/" + csvFile + ".root","READ"); + TTree *tree = (TTree*)hfile->Get("T"); + + cout<GetEntries()<SetBranchAddress("Step",&Step); + tree->SetBranchAddress(columnName,&Susceptible); + + Int_t maxStep = tree->GetMaximum("Step");//3839 + Int_t al = maxStep+1;//array length = 3840 + Int_t entries = tree->GetEntries();//115200 + Int_t ns = entries/al;//number of simulations = 30 + Double_t ci_a = 1 - c_level; + + cout<GetEntry(entry); + if (i == 0) { + x[j] = j; + y[j] = 0; + y_sem[j] = 0; + } + y[j] += Susceptible; + y_sem[j] += Susceptible*Susceptible; + } + } + for (int j = 0; j < al; ++j) { + y[j] = y[j]/ns; + Double_t variance = fabs(y_sem[j]/ns - y[j]*y[j]); + Double_t std = sqrt(variance); + y_sem[j] = std/sqrt(ns); + lci95[j] = TMath::StudentQuantile(ci_a/2, ns-1)*y_sem[j] + y[j]; + hci95[j] = TMath::StudentQuantile(1-ci_a/2, ns-1)*y_sem[j] + y[j]; + yel[j] = y[j] - lci95[j]; + yeh[j] = hci95[j] - y[j]; + } + + auto gf = new TGraphAsymmErrors(al,x,y,nullptr,nullptr,yel,yeh); + gf->Draw("A4"); + gf->GetXaxis()->SetTitle("Step"); + gf->GetYaxis()->SetTitle(columnName); + gf->SetFillColorAlpha(kBlue-7,0.3); + gf->SetFillStyle(1003); + + auto gf2 = new TGraph(al,x,y); + gf2->Draw("L"); + + gStyle->SetOptTitle(0); + gPad->Print(csvFile + ", " + columnName + "," + c_level + ".ps"); +} \ No newline at end of file diff --git a/root_scripts/sevendays.c b/root_scripts/sevendays.c new file mode 100644 index 0000000..8efc6b3 --- /dev/null +++ b/root_scripts/sevendays.c @@ -0,0 +1,80 @@ +void sevendays(TString columnName = "Susceptible",TString csvFile = "cu-current-R0-callibration",Double_t c_level = 0.95,Int_t k = 7) { + Int_t Step; + Double_t Susceptible; + + TFile *hfile = TFile::Open("../outcomes/" + csvFile + ".root","READ"); + TTree *tree = (TTree*)hfile->Get("T"); + + cout<GetEntries()<SetBranchAddress("Step",&Step); + tree->SetBranchAddress(columnName,&Susceptible); + + Int_t maxStep = tree->GetMaximum("Step");//3839 + Int_t al = maxStep+1;//array length = 3840 + Int_t entries = tree->GetEntries();//115200 + Int_t ns = entries/al;//number of simulations = 30 + Double_t ci_a = 1 - c_level; + + Double_t x[al]; + Double_t y[al]; + Double_t ky[al]; + Double_t yel[al]; + Double_t yeh[al]; + Double_t lci95[al]; + Double_t hci95[al]; + Double_t y_sem[al]; + + for (int i = 0; i < ns; ++i) { + for (int j = 0; j < al; ++j) { + Int_t entry = i*al+j; + tree->GetEntry(entry); + if (i == 0) { + x[j] = j; + y[j] = 0; + ky[j] = 0; + y_sem[j] = 0; + } + y[j] += Susceptible; + y_sem[j] += Susceptible*Susceptible; + } + } + for (int j = 0; j < al; ++j) { + y[j] = y[j]/ns; + Double_t variance = fabs(y_sem[j]/ns - y[j]*y[j]); + Double_t std = sqrt(variance); + y_sem[j] = std/sqrt(ns); + lci95[j] = TMath::StudentQuantile(ci_a/2, ns-1)*y_sem[j] + y[j]; + hci95[j] = TMath::StudentQuantile(1-ci_a/2, ns-1)*y_sem[j] + y[j]; + yel[j] = y[j] - lci95[j]; + yeh[j] = hci95[j] - y[j]; + } + + for (int j = 0; j < al; ++j) { + if (j < 96*k) { + for (int i = 0; i <= j; ++i) { + ky[j] += y[i]; + } + ky[j] = ky[j]/(j+1); + } + else { + for (int i = j-(96*k -1 ); i <= j; ++i) { + ky[j] += y[i]; + } + ky[j] = ky[j]/(96*k); + } + } + + auto gf = new TGraphAsymmErrors(al,x,y,nullptr,nullptr,yel,yeh); + gf->Draw("A4"); + gf->GetXaxis()->SetTitle("Step"); + gf->GetYaxis()->SetTitle(columnName); + gf->SetFillColorAlpha(kBlue-7,0.3); + gf->SetFillStyle(1003); + + auto gf2 = new TGraph(al,x,ky); + gf2->Draw("L"); + + gStyle->SetOptTitle(0); + gPad->Print(k + csvFile + ", " + columnName + "," + c_level + ".ps"); +} \ No newline at end of file diff --git a/scenarios/Vaccination_Scenarios_location/cu-vaccination-test-200-heavyM-location-given.json b/scenarios/Vaccination_Scenarios_location/cu-vaccination-test-200-heavyM-location-given.json new file mode 100644 index 0000000..95b59de --- /dev/null +++ b/scenarios/Vaccination_Scenarios_location/cu-vaccination-test-200-heavyM-location-given.json @@ -0,0 +1,144 @@ +{ + "location": "Champaign-Urbana", + "description": "Callibration run from 0.1% to 1.0% from April 21 to July 8th", + "prepared-by": "Eric Jakobsson and Santiago Nunez-Corrales", + "date": "2020.07.15", + "model": { + "distributions": { + "age": { + "80+": 0.03, + "70-79": 0.04, + "60-69": 0.075, + "50-59": 0.075, + "40-49": 0.07, + "30-39": 0.125, + "20-29": 0.30, + "10-19": 0.20, + "00-09": 0.085 + }, + "sex": { + "male": 0.505, + "female": 0.495 + } + }, + "mortalities": { + "age": { + "80+": 0.4840, + "70-79": 0.2317, + "60-69": 0.1592, + "50-59": 0.0817, + "40-49": 0.0292, + "30-39": 0.0111, + "20-29": 0.0037, + "10-19": 0.0003, + "00-09": 0.0001 + }, + "sex": { + "male": 0.618, + "female": 0.382 + } + }, + "value": { + "private": { + "susceptible": 1.0, + "exposed": 1.0, + "asymptomatic": 1.0, + "sympdetected": -0.2, + "asympdetected": -0.2, + "severe": -5.0, + "recovered": 0.8, + "deceased": 0 + }, + "public": { + "susceptible": 10.0, + "exposed": 10.0, + "asymptomatic": -5.0, + "sympdetected": -1.0, + "asympdetected": -0.2, + "severe": -5.0, + "recovered": 5.0, + "deceased": -5 + }, + "test_cost": 200, + "alpha_private": 1.0, + "alpha_public": 1.0 + }, + "locations": { + "type": "given", + "spec": { + "proportions": { + "homes": 0.7, + "shops": 0.2, + "schools": 0.1 + }, + "dwell": { + "homes": 8, + "shops": 2, + "schools": 4 + } + } + }, + "policies": { + "isolation": { + "proportion_isolated": 0.20, + "day_start_isolation": 10, + "days_isolation_lasts": 100, + "after_isolation": 10, + "prob_isolation_effective": 0.6 + }, + "distancing": { + "social_distance": 1.89, + "day_distancing_start": 16, + "days_distancing_lasts": 365 + }, + "testing": { + "proportion_detected": 0.2, + "day_testing_start": 28, + "days_testing_lasts": 500, + "tracing": true + }, + "tracing": { + "day_tracing_start": 100, + "days_tracing_lasts": 700 + }, + "massingress": { + "new_agent_proportion": 0.3, + "new_agent_start": 1000, + "new_agent_lasts": 14, + "new_agent_age_mean": 2, + "new_agent_prop_infected": 0.02 + }, + "vaccine_rollout": { + "day_vaccination_begin": 60, + "day_vaccination_end": 700, + "effective_period": 10, + "effectiveness": 0.95, + "distribution_rate": 1, + "cost_per_vaccine": 400, + "vaccination_percent": 0.5 + } + }, + "epidemiology": { + "num_agents": 200, + "width": 55, + "height": 55, + "repscaling": 1, + "kmob": 0.4781, + "rate_inbound": 0.01, + "prop_initial_infected": 0.01, + "avg_incubation_time": 5, + "avg_recovery_time": 14, + "proportion_asymptomatic": 0.3, + "proportion_severe": 0.12, + "prob_contagion": 0.018, + "proportion_beds_pop": 0.01 + } + }, + "ensemble": { + "steps": 50000, + "runs": 10 + }, + "output": { + "prefix": "outcomes/Vaccination_Percentage_Variant/Test_E/cu-vaccination-test-200-heavyM" + } +} \ No newline at end of file diff --git a/scenarios/Vaccination_Scenarios_location/cu-vaccination-test-200-heavyM-location-named.json b/scenarios/Vaccination_Scenarios_location/cu-vaccination-test-200-heavyM-location-named.json new file mode 100644 index 0000000..8f446c4 --- /dev/null +++ b/scenarios/Vaccination_Scenarios_location/cu-vaccination-test-200-heavyM-location-named.json @@ -0,0 +1,133 @@ + { + "location": "Champaign-Urbana", + "description": "Callibration run from 0.1% to 1.0% from April 21 to July 8th", + "prepared-by": "Eric Jakobsson and Santiago Nunez-Corrales", + "date": "2020.07.15", + "model": { + "distributions": { + "age": { + "80+": 0.03, + "70-79": 0.04, + "60-69": 0.075, + "50-59": 0.075, + "40-49": 0.07, + "30-39": 0.125, + "20-29": 0.30, + "10-19": 0.20, + "00-09": 0.085 + }, + "sex": { + "male": 0.505, + "female": 0.495 + } + }, + "mortalities": { + "age": { + "80+": 0.4840, + "70-79": 0.2317, + "60-69": 0.1592, + "50-59": 0.0817, + "40-49": 0.0292, + "30-39": 0.0111, + "20-29": 0.0037, + "10-19": 0.0003, + "00-09": 0.0001 + }, + "sex": { + "male": 0.618, + "female": 0.382 + } + }, + "value": { + "private": { + "susceptible": 1.0, + "exposed": 1.0, + "asymptomatic": 1.0, + "sympdetected": -0.2, + "asympdetected": -0.2, + "severe": -5.0, + "recovered": 0.8, + "deceased": 0 + }, + "public": { + "susceptible": 10.0, + "exposed": 10.0, + "asymptomatic": -5.0, + "sympdetected": -1.0, + "asympdetected": -0.2, + "severe": -5.0, + "recovered": 5.0, + "deceased": -5 + }, + "test_cost": 200, + "alpha_private": 1.0, + "alpha_public": 1.0 + }, + "locations": { + "type": "named", + "spec": "poisson" + }, + "policies": { + "isolation": { + "proportion_isolated": 0.20, + "day_start_isolation": 10, + "days_isolation_lasts": 100, + "after_isolation": 10, + "prob_isolation_effective": 0.6 + }, + "distancing": { + "social_distance": 1.89, + "day_distancing_start": 16, + "days_distancing_lasts": 365 + }, + "testing": { + "proportion_detected": 0.2, + "day_testing_start": 28, + "days_testing_lasts": 500, + "tracing": true + }, + "tracing": { + "day_tracing_start": 100, + "days_tracing_lasts": 700 + }, + "massingress": { + "new_agent_proportion": 0.3, + "new_agent_start": 1000, + "new_agent_lasts": 14, + "new_agent_age_mean": 2, + "new_agent_prop_infected": 0.02 + }, + "vaccine_rollout": { + "day_vaccination_begin": 60, + "day_vaccination_end": 700, + "effective_period": 10, + "effectiveness": 0.95, + "distribution_rate": 1, + "cost_per_vaccine": 400, + "vaccination_percent": 0.5 + } + }, + "epidemiology": { + "num_agents": 200, + "width": 55, + "height": 55, + "repscaling": 1, + "kmob": 0.4781, + "rate_inbound": 0.01, + "prop_initial_infected": 0.01, + "avg_incubation_time": 5, + "avg_recovery_time": 14, + "proportion_asymptomatic": 0.3, + "proportion_severe": 0.12, + "prob_contagion": 0.018, + "proportion_beds_pop": 0.01 + } + }, + "ensemble": { + "steps": 50000, + "runs": 10 + }, + "output": { + "prefix": "outcomes/Vaccination_Percentage_Variant/Test_E/cu-vaccination-test-200-heavyM" + } +} \ No newline at end of file diff --git a/visualize_feature.py b/visualize_feature.py new file mode 100644 index 0000000..b35f92c --- /dev/null +++ b/visualize_feature.py @@ -0,0 +1,101 @@ +# Santiago Nunez-Corrales and Eric Jakobsson +# Illinois Informatics and Molecular and Cell Biology +# University of Illinois at Urbana-Champaign +# {nunezco,jake}@illinois.edu + +# A simple tunable model for COVID-19 response +import matplotlib.pyplot as plt +import scipy.stats as sps +import seaborn as sns +import pandas as pd +import numpy as np +from scipy.ndimage import gaussian_filter1d +from covidmodel import CovidModel + +import sys + +feature = sys.argv[1] +start = float(sys.argv[2]) +top = float(sys.argv[3]) +bottom = float(sys.argv[4]) +in_file = sys.argv[5] +out_file = sys.argv[6] + +df0 = pd.read_csv(in_file) +df0["Step"] = df0["Step"]/96 + +# Used when there is +xmaxl = {} + + +df = pd.DataFrame() + +if feature == "Rt": + for iteration in df0["Iteration"].unique(): + df_temp = pd.DataFrame() + df_temp["Step"] = (df0["Step"].unique()) + df_temp["Rt"] = gaussian_filter1d(df0["Rt"][df0["Iteration"] == iteration], 96) + df = df.append(df_temp) +else: + df["Step"] = df0["Step"] + df[feature] = df0[feature] + +#xmin = min(list(xminl.values())) +xmin = start +xmax = df["Step"].max() +ymin = bottom +ymax = top + +avg = {} +low_ci_95 = {} +high_ci_95 = {} +low_ci_99 = {} +high_ci_99 = {} +df_stats = {} + +avg = [] +low_ci_95 = [] +high_ci_95 = [] +low_ci_99 = [] +high_ci_99 = [] + +for step in df["Step"].unique(): + values = df[feature][df["Step"] == step] + f_mean = values.mean() + lci95, hci95 = sps.t.interval(0.95, len(values), loc=f_mean, scale=sps.sem(values)) + lci99, hci99 = sps.t.interval(0.99, len(values), loc=f_mean, scale=sps.sem(values)) + avg.append(f_mean) + low_ci_95.append(lci95) + high_ci_95.append(hci95) + low_ci_99.append(lci99) + high_ci_99.append(hci99) + +df_stats = pd.DataFrame() +df_stats["Step"] = df["Step"].unique() +df_stats["mean"] = avg +df_stats["lci95"] = low_ci_95 +df_stats["hci95"] = high_ci_95 +df_stats["lci99"] = low_ci_99 +df_stats["hci99"] = high_ci_99 + +plt.plot(df_stats["Step"], df_stats["mean"], color='black', linewidth=1) +plt.fill_between(df_stats["Step"], df_stats["lci95"], df_stats["hci95"], color='blue', alpha=.1) + +plt.xlim([xmin, xmax]) +plt.ylim([ymin, ymax]) +plt.xlabel("Days since April 15, 2020", fontsize=18) +plt.xticks(fontsize=16) +plt.yticks(fontsize=16) + +if (feature == "SymptQuarantined") or (feature == "Asymptomatic") or (feature == "Severe"): + plt.ylabel("Population Fraction", fontsize=18) +elif feature == "CumulPublValue": + plt.ylabel("Public Value", fontsize=18) +elif feature == "CumulPrivValue": + plt.ylabel("Private Value", fontsize=18) +elif feature == "Rt": + plt.ylabel("$R(T)$", fontsize=18) +else: + plt.ylabel("variable", fontsize=18) + +plt.savefig(out_file, dpi=300)