def __init__(self, scenario): if isinstance(scenario, str): self.scenario = EpiScenario(scenario) elif isinstance(scenario, EpiScenario): self.scenario = scenario self.modelname = self.scenario.modelname self.total_days = 0 self.r0 = self.scenario.parameters['initial_r0'] self.beta = None self.population = self.scenario.totalpop self.susceptible = ProbState(period=0, count=self.scenario.init_susceptible, name='susceptible') self.incubating = ProbState(period=self.scenario.incubation_period, count=self.scenario.init_infected, name='incubating') self.infectious = ProbState(period=self.scenario.prediagnosis, count=self.scenario.init_infectious, name='infectious') self.isolated_holding = ProbState(period=90, name='isolated_holding') self.incubating.add_exit_state(self.infectious, 1) self.incubating.normalize_states_over_period() self.infectious.add_exit_state(self.isolated_holding, 1) self.infectious.normalize_states_over_period() self.subgroups = dict() for key, value in self.scenario.subgrouprates.items(): self.subgroups[key] = AgeGroup(value, name=key)
def __init__(self): self.r0 = 2.65 self.total_days = 0 self.population = POP_DENVER self.beta = None self.susceptible = ProbState(period=0, count=self.population - 1, name='susceptible') self.incubating = ProbState(period=3, name='incubating') self.infectious = ProbState(period=3.8, count=1, name='infectious') self.isolated_holding = ProbState(period=90, name='isolated_holding') self.incubating.add_exit_state(self.infectious, 1) self.incubating.normalize_states_over_period() self.infectious.add_exit_state(self.isolated_holding, 1) self.infectious.normalize_states_over_period() self.subgroups = dict() for key, value in AGE_DISTRIBUTION.items(): self.subgroups[key] = AgeGroup(SubgroupRates(ICD[key], value), name=key) self.sum_isolated = None self.sum_noncrit = None self.sum_icu = None self.sum_icu_vent = None self.sum_recovered = None self.sum_deceased = None
class ScenarioDrivenModel: def __init__(self, scenario): if isinstance(scenario, str): self.scenario = EpiScenario(scenario) elif isinstance(scenario, EpiScenario): self.scenario = scenario self.modelname = self.scenario.modelname self.total_days = 0 self.r0 = self.scenario.parameters['initial_r0'] self.beta = None self.population = self.scenario.totalpop self.susceptible = ProbState(period=0, count=self.scenario.init_susceptible, name='susceptible') self.incubating = ProbState(period=self.scenario.incubation_period, count=self.scenario.init_infected, name='incubating') self.infectious = ProbState(period=self.scenario.prediagnosis, count=self.scenario.init_infectious, name='infectious') self.isolated_holding = ProbState(period=90, name='isolated_holding') self.incubating.add_exit_state(self.infectious, 1) self.incubating.normalize_states_over_period() self.infectious.add_exit_state(self.isolated_holding, 1) self.infectious.normalize_states_over_period() self.subgroups = dict() for key, value in self.scenario.subgrouprates.items(): self.subgroups[key] = AgeGroup(value, name=key) # self.fitness = None def run(self): self.run_r0_set(self.scenario.r0_date_offsets, self.scenario.r0_values) def set_r0(self, value): self.r0 = value def run_r0_set(self, date_offsets, r0_values): self.scenario.hospital_door_aggregator = [] day_counter = 0 for itr in range(0, len(date_offsets)): self.set_r0(r0_values[itr]) self.beta = calc_beta(r0_values[itr], self.infectious.period) while day_counter < date_offsets[itr]: self.step_day() day_counter += 1 self.scenario.hospital_door_aggregator.append( self.scenario.hospital_door_aggregator[-1]) def step_day(self): new_infections = calc_infected(self.population, self.beta, self.susceptible.count, self.infectious.count) #print(f"Day {self.total_days} infections: {new_infections} = {self.beta} * {self.susceptible.count} * {self.infectious.count} / {self.population}") self.susceptible.store_pending(-new_infections) self.incubating.store_pending(new_infections) self.incubating.pass_downstream() self.infectious.pass_downstream() diagnosed = self.isolated_holding.pending if len(self.scenario.hospital_door_aggregator) == 0: diagnagg = diagnosed else: diagnagg = self.scenario.hospital_door_aggregator[-1] + diagnosed self.scenario.hospital_door_aggregator.append(diagnagg) self.isolated_holding.pending = 0 subpop_out = [] for key, agegroup in self.subgroups.items(): subpop = diagnosed * agegroup.stats.pop_dist subpop_out.append(subpop) agegroup.apply_infections(subpop) agegroup.calculate_redistributions() self.susceptible.apply_pending() self.incubating.apply_pending() self.infectious.apply_pending() for key, agegroup in self.subgroups.items(): agegroup.apply_pending() self.total_days += 1 def gather_sums(self): time_increments = len(self.susceptible.domain) self.scenario.out_susceptible = self.susceptible.domain self.scenario.out_incubating = self.incubating.domain self.scenario.out_infectious = self.infectious.domain self.scenario.sum_isolated = [0] * time_increments self.scenario.sum_noncrit = [0] * time_increments self.scenario.sum_icu = [0] * time_increments self.scenario.sum_icu_vent = [0] * time_increments self.scenario.sum_recovered = [0] * time_increments self.scenario.sum_deceased = [0] * time_increments self.scenario.sum_hospitalized = [0] * time_increments for key, value in self.subgroups.items(): self.scenario.sum_isolated = np.add(self.scenario.sum_isolated, value.isolated.domain) self.scenario.sum_noncrit = np.add(self.scenario.sum_noncrit, value.h_noncrit.domain) self.scenario.sum_icu = np.add(self.scenario.sum_icu, value.h_icu.domain) self.scenario.sum_icu_vent = np.add(self.scenario.sum_icu_vent, value.h_icu_vent.domain) self.scenario.sum_recovered = np.add(self.scenario.sum_recovered, value.recovered.domain) self.scenario.sum_deceased = np.add(self.scenario.sum_deceased, value.deceased.domain) self.scenario.sum_hospitalized = np.add(self.scenario.sum_hospitalized, self.scenario.sum_icu) self.scenario.sum_hospitalized = np.add(self.scenario.sum_hospitalized, self.scenario.sum_noncrit) self.scenario.sum_hospitalized = np.add(self.scenario.sum_hospitalized, self.scenario.sum_icu_vent) self.scenario.fitset = dict() cursor = self.scenario.initial_date stoptime = cursor + timedelta(self.total_days) itr = 0 while cursor < stoptime: if cursor not in self.scenario.fitset: self.scenario.fitset[cursor] = dict() self.scenario.fitset[cursor][ 'current_hosp'] = self.scenario.sum_hospitalized[itr] self.scenario.fitset[cursor][ 'total_hosp'] = self.scenario.hospital_door_aggregator[itr] self.scenario.fitset[cursor][ 'total_deceased'] = self.scenario.sum_deceased[itr] itr += 1 cursor += ONEDAY def save_results(self, iteration): result = dict() result['iteration'] = iteration result['fitness'] = self.scenario.fitness result['scenario'] = self.scenario.parameters result['modelname'] = self.modelname result['total_days'] = self.total_days result['totalpop'] = self.population result['sum_isolated'] = self.scenario.sum_isolated result['sum_noncrit'] = self.scenario.sum_noncrit result['sum_icu'] = self.scenario.sum_icu result['sum_icu_vent'] = self.scenario.sum_icu_vent result['sum_recovered'] = self.scenario.sum_recovered result['sum_deceased'] = self.scenario.sum_deceased with open(f"best_fit{iteration}", "w") as bfi: json.dump(result, bfi) def actual_curves(self): cursor = self.scenario.initial_date finaldate = cursor + timedelta(self.total_days) act_hosp = [] act_death = [] while cursor < finaldate: if cursor in COLORADO_ACTUAL: act_hosp.append(COLORADO_ACTUAL[cursor]['hospitalized']) act_death.append(COLORADO_ACTUAL[cursor]['deceased']) else: act_hosp.append(None) act_death.append(None) cursor += ONEDAY act_death.append(None) act_hosp.append(None) return act_hosp, act_death def generate_png(self): hospitalized = self.scenario.hospital_door_aggregator startdate = self.scenario.initial_date time_domain = [startdate] cursor = startdate for _ in range(0, self.total_days): cursor += ONEDAY time_domain.append(cursor) # time_domain = np.linspace(0, model.total_days, model.total_days + 1) fig = plt.figure(facecolor='w') # ax = fig.add_subplot(111, axis_bgcolor='#dddddd', axisbelow=True) ax = fig.add_subplot(111, axisbelow=True) ### Vertical line indicating today plt.axvline(x=datetime.today(), alpha=.5, lw=2, label='Today') #------------- # Actual numbers, displayed when GA fitting #------------- act_hosp, act_death = self.actual_curves() ### Actual Hospitalized, as specified in the constants ax.plot(time_domain, act_hosp, color=(0, 0, .5), alpha=1, lw=2, label='Actual Hospitalized', linestyle='-') ### Actual Deaths, as specified in the constants ax.plot(time_domain, act_death, color=(0, 0, .5), alpha=1, lw=2, label='Actual Deaths', linestyle='-') #------------- # Basic SEIR #------------- ### Susceptible line, usually too tall # ax.plot(time_domain, self.scenario.out_susceptible, label='Susceptible', color=(0, 0, 1), alpha=.5, lw=2, linestyle='-') ### Exposed: pre-symptomatic, not infectious yet # ax.plot(time_domain, self.scenario.incubating, label='Exposed', color=TABLEAU_ORANGE, alpha=0.1, lw=2, linestyle='-') ### Infectious patients, not isolated # ax.plot(time_domain, self.scenario.infectious, label='Infected', color=TABLEAU_RED, alpha=0.5, lw=2, linestyle='-') ### Recovered/immune, usually too tall # ax.plot(time_domain, self.scenario.sum_recovered, label='Recovered', color=(0, .5, 0), alpha=.5, lw=2, linestyle='--') ### Infected, isolated at home # ax.plot(time_domain, self.scenario.sum_isolated, label='Home Iso', color=TAB_COLORS[8], alpha=.5, lw=2, linestyle='-') ### Deceased ax.plot(time_domain, self.scenario.sum_deceased, label='Deceased', color=(.25, .25, 0), alpha=.5, lw=2, linestyle='--') #------------- # Hospital Capacities #------------- # ax.plot(time_domain, self.scenario.sum_floor, label='Floor Beds', color=TABLEAU_BLUE, alpha=1, lw=2, linestyle='--') # ax.plot(time_domain, self.scenario.sum_icu, label='ICU Beds', color=TABLEAU_GREEN, alpha=1, lw=2, linestyle='--') # ax.plot(time_domain, self.scenario.sum_vent, label='ICU + Vent Beds', color=TABLEAU_RED, alpha=1, lw=2, linestyle='--') ax.plot(time_domain, hospitalized, label='Total Hospitalized', color=(1, 0, 0), alpha=.25, lw=2, linestyle='-') #------------- # Hospital Capacities - DH Specific #------------- # ax.plot(time_domain, [86] * (self.total_days + 1) , label='Supply - DH ICU Beds' , color=(1, 0, 0), alpha=1, lw=1, linestyle='-') # ax.plot(time_domain, [229] * (self.total_days + 1), label='Supply - DH Total Beds', color=(0, 0, 1), alpha=1, lw=1, linestyle='-') #------------- # Hospital Capacities - Denver County #------------- # ax.plot(time_domain, [695] * (self.total_days + 1) , label='Supply - 5C ICU Beds' , color=(1, 0, 0), alpha=1, lw=1, linestyle='-') # ax.plot(time_domain, [5907] * (self.total_days + 1), label='Supply - 5C Total Beds', color=(0, 0, 1), alpha=1, lw=1, linestyle='-') # ax.plot(time_domain, [1043] * (self.total_days + 1), label='Supply - 5C ICU Beds' , color=(1, 0, 0), alpha=1, lw=1, linestyle='-') # ax.plot(time_domain, [8861] * (self.total_days + 1), label='Supply - 5C Total Beds', color=(0, 0, 1), alpha=1, lw=1, linestyle='-') #------------- # Hospital Capacities - Five County #------------- # ax.plot(time_domain, [255] * (self.total_days + 1) , label='Supply - 5C ICU Beds' , color=(1, 0, 0), alpha=1, lw=1, linestyle='-') # ax.plot(time_domain, [1000] * (self.total_days + 1), label='Supply - 5C Total Beds', color=(0, 0, 1), alpha=1, lw=1, linestyle='-') # ax.plot(time_domain, [1043] * (self.total_days + 1), label='Supply - 5C ICU Beds' , color=(1, 0, 0), alpha=1, lw=1, linestyle='-') # ax.plot(time_domain, [4135] * (self.total_days + 1), label='Supply - 5C Total Beds', color=(0, 0, 1), alpha=1, lw=1, linestyle='-') # ------------ # Hospital Capacities - Five County - 50% # ------------ # ax.plot(time_domain, [348] * (model.total_days + 1) , label='Supply - 5County ICU', color=(0, 0, 1), alpha=1, lw=1, linestyle='--') # ax.plot(time_domain, [2954] * (model.total_days + 1), label='Supply - 5County Tot', color=(1, 0, 0), alpha=1, lw=1, linestyle='-') # ax.plot(time_domain, [521] * (model.total_days + 1) , label='1.5x 5County ICU' , color=(0, 0, 1), alpha=1, lw=1, linestyle='--') # ax.plot(time_domain, [4430] * (model.total_days + 1), label='1.5x 5County Tot' , color=(1, 0, 0), alpha=1, lw=1, linestyle='-') #make pretty # set the style of the axes and the text color plt.rcParams['axes.edgecolor'] = '#333F4B' plt.rcParams['axes.linewidth'] = 0.8 plt.rcParams['xtick.color'] = '#333F4B' plt.rcParams['ytick.color'] = '#333F4B' plt.rcParams['text.color'] = '#333F4B' # set axis ax.tick_params(axis='both', which='major', labelsize=12) ax.set_xlabel('Days') ax.set_ylabel('Number') chart_title = self.modelname plt.title(chart_title, fontsize=14) # ax.set_ylim(0,1.2) ax.yaxis.set_tick_params(length=4) ax.xaxis.set_tick_params(length=4) # ax.grid(b=True, which='minor', c='w', lw=1, ls='--') ax.grid() legend = ax.legend() legend.get_frame().set_alpha(0.5) for spine in ('top', 'right', 'bottom', 'left'): ax.spines[spine].set_visible(False) return plt def generate_csv(self): chart_title = self.modelname outfilename = "_".join( chart_title.replace("|", " ").replace(":", " ").replace(".", " ").split()) # Write a CSV to this directory with open(f"{outfilename}.csv", 'w') as outfile: for itr in range(0, len(self.scenario.out_susceptible)): outfile.write(f"{self.scenario.out_susceptible[itr]:.6f}, " f"{self.scenario.out_incubating[itr]:.6f}, " f"{self.scenario.out_infectious[itr]:.6f}, " f"{self.scenario.sum_isolated[itr]:.6f}, " f"{self.scenario.sum_noncrit[itr]:.6f}, " f"{self.scenario.sum_icu[itr]:.6f}, " f"{self.scenario.sum_icu_vent[itr]:.6f}, " f"{self.scenario.sum_recovered[itr]:.6f}, " f"{self.scenario.sum_deceased[itr]:.6f}, " f"{self.scenario.sum_hospitalized[itr]:.6f}\n")
def __init__(self): self.r0 = 2.65 self.total_days = 0 self.population = POP_DENVERMETRO - 1 self.beta = None self.susceptible = ProbState(period=0, count=self.population) self.incubating = ProbState(period=3) self.infectious = ProbState(period=3.8, count=1) self.isolated = ProbState(period=14) self.unhospitalized = ProbState(period=14) self.h_noncritical = ProbState(period=8) self.h_critical = ProbState(period=6) self.h_icu = ProbState(period=10) self.recovered = ProbState(period=10000) self.dead = ProbState(period=10000) self.incubating.add_exit_state(self.infectious, 1) self.incubating.normalize_states_over_period() self.isolated.add_exit_state(self.recovered, 1) self.isolated.normalize_states_over_period() self.unhospitalized.add_exit_state(self.recovered, .25) self.unhospitalized.add_exit_state(self.dead, .75) self.unhospitalized.normalize_states_over_period() self.infectious.add_exit_state(self.isolated, .85) self.infectious.add_exit_state(self.h_noncritical, .11) self.infectious.add_exit_state(self.h_critical, .4) self.infectious.normalize_states_over_period() self.h_noncritical.add_exit_state(self.recovered, 1) self.h_noncritical.normalize_states_over_period() self.h_critical.add_exit_state(self.recovered, 1) self.h_critical.normalize_states_over_period() self.h_icu.add_exit_state(self.recovered, .75) self.h_icu.add_exit_state(self.dead, .25) self.h_icu.normalize_states_over_period()
class HospitalFullModel: def __init__(self): self.r0 = 2.65 self.total_days = 0 self.population = POP_DENVERMETRO - 1 self.beta = None self.susceptible = ProbState(period=0, count=self.population) self.incubating = ProbState(period=3) self.infectious = ProbState(period=3.8, count=1) self.isolated = ProbState(period=14) self.unhospitalized = ProbState(period=14) self.h_noncritical = ProbState(period=8) self.h_critical = ProbState(period=6) self.h_icu = ProbState(period=10) self.recovered = ProbState(period=10000) self.dead = ProbState(period=10000) self.incubating.add_exit_state(self.infectious, 1) self.incubating.normalize_states_over_period() self.isolated.add_exit_state(self.recovered, 1) self.isolated.normalize_states_over_period() self.unhospitalized.add_exit_state(self.recovered, .25) self.unhospitalized.add_exit_state(self.dead, .75) self.unhospitalized.normalize_states_over_period() self.infectious.add_exit_state(self.isolated, .85) self.infectious.add_exit_state(self.h_noncritical, .11) self.infectious.add_exit_state(self.h_critical, .4) self.infectious.normalize_states_over_period() self.h_noncritical.add_exit_state(self.recovered, 1) self.h_noncritical.normalize_states_over_period() self.h_critical.add_exit_state(self.recovered, 1) self.h_critical.normalize_states_over_period() self.h_icu.add_exit_state(self.recovered, .75) self.h_icu.add_exit_state(self.dead, .25) self.h_icu.normalize_states_over_period() def reset(self): self.total_days = 0 self.susceptible.reset() self.susceptible.count = self.population - 1 self.incubating.reset() self.infectious.reset() self.infectious.count = 1 self.isolated.reset() self.unhospitalized.reset() self.h_noncritical.reset() self.h_critical.reset() self.h_icu.reset() self.recovered.reset() self.dead.reset() def set_r0(self, value): self.r0 = value def set_population(self, value): self.population = value def run_period(self, days): time_domain = np.linspace(0, days, days + 1) # Initial conditions vector init = (self.susceptible.count, self.incubating.count, self.infectious.count, self.isolated.count, self.h_noncritical.count, self.h_critical.count, self.h_icu.count, self.unhospitalized.count, self.recovered.count, self.dead.count) # Integrate the SIR equations over the time grid, t. results = odeint(deriv_seirh, init, time_domain, args=(self, )) (d_susceptible, d_incubating, d_infectious, d_isolated, d_noncritical, d_critical, d_icu, d_unhospitalized, d_recovered, d_dead) = results.T self.total_days += days self.susceptible.extend(d_susceptible) self.incubating.extend(d_incubating) self.infectious.extend(d_infectious) self.isolated.extend(d_isolated) self.h_noncritical.extend(d_noncritical) self.h_critical.extend(d_critical) self.h_icu.extend(d_icu) self.unhospitalized.extend(d_unhospitalized) self.recovered.extend(d_recovered) self.dead.extend(d_dead) def run_period2(self, days): # Integrate the SIR equations over the time grid, t. for _ in range(0, days): self.step_day() def run_r0_set(self, date_offsets, r0_values): self.reset() prev_date = 0 for itr in range(0, len(date_offsets)): self.set_r0(r0_values[itr]) self.beta = calc_beta(self.r0, self.dayspergen) self.recalculate() span = date_offsets[itr] - prev_date + 1 self.run_period2(span) prev_date = date_offsets[itr] if len(self.unhospitalized.domain) != len(self.dead.domain): raise ValueError( f"oops, {len(self.unhospitalized.domain)} != {len(self.dead.domain)}" ) def step_day(self): new_infections = self.beta * self.susceptible.count * self.infectious.count / self.population (new_symptomatic, ) = self.incubating.get_state_redist() new_isolated, new_noncritical, new_critical = self.infectious.get_state_redist( ) (recovered1, ) = self.isolated.get_state_redist() (recovered2, ) = self.h_noncritical.get_state_redist() (new_icu, ) = self.h_critical.get_state_redist() recovered3, dead1 = self.h_icu.get_state_redist() recovered4, dead2 = self.unhospitalized.get_state_redist() d_susceptible = -new_infections d_incubating = new_infections - new_symptomatic d_infectious = new_symptomatic - (new_isolated + new_noncritical + new_critical) d_isolated = new_isolated - recovered1 d_noncritical = new_noncritical - recovered2 d_critical = new_critical - new_icu d_icu = new_icu - (recovered3 + dead1) d_unhospitalized = -(recovered4 + dead2) d_recovered = recovered1 + recovered2 + recovered3 + recovered4 d_dead = dead1 + dead2 (d_noncritical, d_critical, d_icu, d_unhospitalized) = adjust_for_overload(self.h_noncritical.count, self.h_critical.count, self.h_icu.count, d_noncritical, d_critical, d_icu, d_unhospitalized) balances = (d_susceptible + d_incubating + d_infectious + d_isolated + d_noncritical + d_critical + d_icu + d_unhospitalized + d_recovered + d_dead) if int(balances) != 0: raise ValueError(f"balances {balances} != 0") self.susceptible.adjust(d_susceptible) self.incubating.adjust(d_incubating) self.infectious.adjust(d_infectious) self.isolated.adjust(d_isolated) self.h_noncritical.adjust(d_noncritical) self.h_critical.adjust(d_critical) self.h_icu.adjust(d_icu) self.unhospitalized.adjust(d_unhospitalized) self.recovered.adjust(d_recovered) self.dead.adjust(d_dead) self.total_days += 1
def __init__(self, subgroupstats, name=None): self.name = name self.stats = subgroupstats # Variables to store state changes # Probability states self.isolated = ProbState(TIMINGS['days home isolation'], name=f"{self.name}: isolated") self.nevercrit = ProbState(TIMINGS['days noncrit'], name=f"{self.name}: nevercrit") self.pre_icu = ProbState(TIMINGS['days preicu'], name=f"{self.name}: pre_icu") self.icu_novent = ProbState(TIMINGS['days icu nonvent'], name=f"{self.name}: icu") self.icu_vent = ProbState(TIMINGS['days icu vent'], name=f"{self.name}: icu_vent") self.post_icu = ProbState(TIMINGS['days posticu'], name=f"{self.name}: post_icu") # Recovered and dead are presumed permanent self.recovered = ProbState(1000, name=f"{self.name}: recovered") self.deceased = ProbState(1000, name=f"{self.name}: deceased") self.isolated.add_exit_state(self.recovered, 1) self.isolated.normalize_states_over_period() self.nevercrit.add_exit_state(self.recovered, 1) self.nevercrit.normalize_states_over_period() self.pre_icu.add_exit_state(self.icu_novent, self.stats.p_icu_nonvent) self.pre_icu.add_exit_state(self.icu_vent, self.stats.p_icu_vent) self.pre_icu.normalize_states_over_period() self.icu_novent.add_exit_state(self.deceased, self.stats.p_icu_death) self.icu_novent.add_exit_state(self.post_icu, self.stats.p_icu_recovery) self.icu_novent.normalize_states_over_period() self.icu_vent.add_exit_state(self.deceased, self.stats.p_icu_death) self.icu_vent.add_exit_state(self.post_icu, self.stats.p_icu_recovery) self.icu_vent.normalize_states_over_period() self.post_icu.add_exit_state(self.recovered, 1) self.post_icu.normalize_states_over_period()
class PathsByAge: def __init__(self, subgroupstats, name=None): self.name = name self.stats = subgroupstats # Variables to store state changes # Probability states self.isolated = ProbState(TIMINGS['days home isolation'], name=f"{self.name}: isolated") self.nevercrit = ProbState(TIMINGS['days noncrit'], name=f"{self.name}: nevercrit") self.pre_icu = ProbState(TIMINGS['days preicu'], name=f"{self.name}: pre_icu") self.icu_novent = ProbState(TIMINGS['days icu nonvent'], name=f"{self.name}: icu") self.icu_vent = ProbState(TIMINGS['days icu vent'], name=f"{self.name}: icu_vent") self.post_icu = ProbState(TIMINGS['days posticu'], name=f"{self.name}: post_icu") # Recovered and dead are presumed permanent self.recovered = ProbState(1000, name=f"{self.name}: recovered") self.deceased = ProbState(1000, name=f"{self.name}: deceased") self.isolated.add_exit_state(self.recovered, 1) self.isolated.normalize_states_over_period() self.nevercrit.add_exit_state(self.recovered, 1) self.nevercrit.normalize_states_over_period() self.pre_icu.add_exit_state(self.icu_novent, self.stats.p_icu_nonvent) self.pre_icu.add_exit_state(self.icu_vent, self.stats.p_icu_vent) self.pre_icu.normalize_states_over_period() self.icu_novent.add_exit_state(self.deceased, self.stats.p_icu_death) self.icu_novent.add_exit_state(self.post_icu, self.stats.p_icu_recovery) self.icu_novent.normalize_states_over_period() self.icu_vent.add_exit_state(self.deceased, self.stats.p_icu_death) self.icu_vent.add_exit_state(self.post_icu, self.stats.p_icu_recovery) self.icu_vent.normalize_states_over_period() self.post_icu.add_exit_state(self.recovered, 1) self.post_icu.normalize_states_over_period() def get_floor_counts(self): retval = [] for itr in range(0, len(self.nevercrit.domain)): val = self.nevercrit.domain[itr] + self.pre_icu.domain[ itr] + self.post_icu.domain[itr] retval.append(val) return retval def get_icu_counts(self): retval = [] for itr in range(0, len(self.icu_novent.domain)): val = self.icu_novent.domain[itr] + self.icu_vent.domain[itr] retval.append(val) return retval # Add N people to the list of infected def apply_infections(self, infections): inf_float = float(infections) n_isolated = inf_float * self.stats.p_selfisolate n_nevercrit = inf_float * self.stats.p_nevercrit n_pre_icu = inf_float * self.stats.p_pre_icu n_icu_vent = inf_float * self.stats.p_urgent_icu_vent n_icu_novent = inf_float * self.stats.p_urgent_icu_novent # print(f"Storing infections {self.name}: {n_nevercrit}, {n_pre_icu}, {n_icu_vent}, {n_icu_novent}") self.isolated.store_pending(n_isolated) self.nevercrit.store_pending(n_nevercrit) self.pre_icu.store_pending(n_pre_icu) self.icu_vent.store_pending(n_icu_vent) self.icu_novent.store_pending(n_icu_novent) def calculate_redistributions(self): self.isolated.pass_downstream() # self.ed_to_floor.pass_downstream() # self.ed_to_icu.pass_downstream() self.nevercrit.pass_downstream() self.pre_icu.pass_downstream() self.icu_novent.pass_downstream() self.icu_vent.pass_downstream() self.post_icu.pass_downstream() def apply_pending(self): self.isolated.apply_pending() # self.ed_to_floor.apply_pending() # self.ed_to_icu.apply_pending() self.nevercrit.apply_pending() self.pre_icu.apply_pending() self.icu_novent.apply_pending() self.icu_vent.apply_pending() self.post_icu.apply_pending() self.recovered.apply_pending() self.deceased.apply_pending()
class HospFloorModel: def __init__(self, scenario): if isinstance(scenario, str): self.scenario = EpiScenario(scenario) elif isinstance(scenario, EpiScenario): self.scenario = scenario self.modelname = self.scenario.modelname self.total_days = 0 self.r0 = self.scenario.parameters['initial_r0'] self.beta = None self.population = self.scenario.totalpop self.susceptible = ProbState(period=0, count=self.scenario.init_susceptible, name='susceptible') self.incubating = ProbState(period=self.scenario.incubation_period, count=self.scenario.init_infected, name='incubating') self.infectious = ProbState(period=self.scenario.prediagnosis, count=self.scenario.init_infectious, name='infectious') self.isolated_holding = ProbState(period=90, name='isolated_holding') self.incubating.add_exit_state(self.infectious, 1) self.incubating.normalize_states_over_period() self.infectious.add_exit_state(self.isolated_holding, 1) self.infectious.normalize_states_over_period() self.subgroups = dict() for key, value in self.scenario.subgrouprates.items(): self.subgroups[key] = PathsByAge(value, name=key) self.fitness = None def run(self): self.run_r0_set(self.scenario.r0_date_offsets, self.scenario.r0_values) def set_r0(self, value): self.r0 = value def run_r0_set(self, date_offsets, r0_values): day_counter = 0 for itr in range(0, len(date_offsets)): self.set_r0(r0_values[itr]) self.beta = calc_beta(self.r0, self.dayspergen) print(f"{self.beta} = {calc_beta(self.r0, self.dayspergen)})") while day_counter < date_offsets[itr]: self.step_day() day_counter += 1 def step_day(self): new_infections = self.beta * self.susceptible.count * self.infectious.count / self.population # print(f"Day {self.total_days} infections: {new_infections} = {self.beta} * {self.susceptible.count} * {self.infectious.count} / {self.population}") self.susceptible.store_pending(-new_infections) self.incubating.store_pending(new_infections) self.incubating.pass_downstream() self.infectious.pass_downstream() diagnosed = self.isolated_holding.pending self.isolated_holding.pending = 0 subpop_out = [] for key, agegroup in self.subgroups.items(): subpop = diagnosed * agegroup.stats.pop_dist subpop_out.append(subpop) agegroup.apply_infections(subpop) agegroup.calculate_redistributions() self.susceptible.apply_pending() self.incubating.apply_pending() self.infectious.apply_pending() for key, agegroup in self.subgroups.items(): agegroup.apply_pending() self.total_days += 1 def gather_sums(self): self.scenario.out_susceptible = self.susceptible.domain self.scenario.out_incubating = self.incubating.domain self.scenario.out_infectious = self.infectious.domain time_increments = len(self.susceptible.domain) self.scenario.sum_isolated = [0] * time_increments self.scenario.sum_floor = [0] * time_increments self.scenario.sum_icu = [0] * time_increments self.scenario.sum_vent = [0] * time_increments self.scenario.sum_recovered = [0] * time_increments self.scenario.sum_deceased = [0] * time_increments self.scenario.sum_hospitalized = [0] * time_increments for key, value in self.subgroups.items(): self.scenario.sum_isolated = np.add(self.scenario.sum_isolated, value.isolated.domain) self.scenario.sum_floor = np.add(self.scenario.sum_floor, value.get_floor_counts()) self.scenario.sum_icu = np.add(self.scenario.sum_icu, value.get_icu_counts()) self.scenario.sum_vent = np.add(self.scenario.sum_vent, value.icu_vent.domain) self.scenario.sum_recovered = np.add(self.scenario.sum_recovered, value.recovered.domain) self.scenario.sum_deceased = np.add(self.scenario.sum_deceased, value.deceased.domain) self.scenario.sum_hospitalized = np.add(self.scenario.sum_hospitalized, self.scenario.sum_floor) self.scenario.sum_hospitalized = np.add(self.scenario.sum_hospitalized, self.scenario.sum_icu) def calculate_fit(self, ideal): initial_offset = (ideal['start'] - self.scenario.initial_date).days fitcount = (ideal['end'] - ideal['start']).days final_offset = initial_offset + fitcount + 1 cursor = initial_offset fitcur = 0 hosp = self.scenario.sum_hospitalized dead = self.scenario.sum_deceased hosp_sum = 0 hosp_r2 = 0 dead_sum = 0 dead_r2 = 0 while cursor < final_offset: hosp_sum += hosp[cursor] dead_sum += hosp[cursor] hosp_r2 += (hosp[cursor] - ideal['hospitalized'][fitcur])**2 dead_r2 += (dead[cursor] - ideal['deceased'][fitcur])**2 fitcur += 1 cursor += 1 hosp_hold = math.sqrt(hosp_r2) dead_hold = math.sqrt(dead_r2) hosp_avg = hosp_sum / fitcount dead_avg = dead_sum / fitcount self.scenario.fitness = (hosp_hold / hosp_avg) + (dead_hold / dead_avg) def save_results(self, iteration): result = dict() result['iteration'] = iteration result['fitness'] = self.fitness result['scenario'] = self.scenario.parameters result['modelname'] = self.modelname result['total_days'] = self.total_days result['totalpop'] = self.population result['sum_isolated'] = self.scenario.sum_isolated result['sum_noncrit'] = self.scenario.sum_noncrit result['sum_icu'] = self.scenario.sum_icu result['sum_icu_vent'] = self.scenario.sum_icu_vent result['sum_recovered'] = self.scenario.sum_recovered result['sum_deceased'] = self.scenario.sum_deceased with open(f"best_fit{iteration}", "w") as bfi: json.dump(result, bfi) def actual_curves(self): cursor = self.scenario.initial_date finaldate = cursor + timedelta(self.scenario.maxdays) act_hosp = [] act_death = [] while cursor < finaldate: if cursor in COLORADO_ACTUAL: act_hosp.append(COLORADO_ACTUAL[cursor]['hospitalized']) act_death.append(COLORADO_ACTUAL[cursor]['deaths']) else: act_hosp.append(None) act_death.append(None) cursor += ONEDAY act_death.append(None) act_hosp.append(None) return act_hosp, act_death def generate_png(self): startdate = self.scenario.initial_date time_domain = [startdate] cursor = startdate for _ in range(0, self.total_days): cursor += ONEDAY time_domain.append(cursor) # time_domain = np.linspace(0, model.total_days, model.total_days + 1) fig = plt.figure(facecolor='w') # ax = fig.add_subplot(111, axis_bgcolor='#dddddd', axisbelow=True) ax = fig.add_subplot(111, axisbelow=True) ### Lines for comparison to actual act_hosp, act_death = self.actual_curves() ### Actual Hospitalized, as specified in the constants ax.plot(time_domain, act_hosp, color=(0, 0, .5), alpha=1, lw=2, label='Actual Hospitalized', linestyle='-') ### Actual Deaths, as specified in the constants ax.plot(time_domain, act_death, color=(0, 0, .5), alpha=1, lw=2, label='Actual Deaths', linestyle='-') ### Susceptible line, usually too tall # ax.plot(time_domain, self.scenario.susceptible, color=(0, 0, 1), alpha=.5, lw=2, label='Susceptible', linestyle='-') ### Recovered/immune, usually too tall # ax.plot(time_domain, self.scenario.sum_recovered, color=(0, .5, 0), alpha=.5, lw=2, label='Recovered', linestyle='--') ### Infected patients who aren't infectious yet # ax.plot(time_domain, self.scenario.incubating, color=TABLEAU_ORANGE, alpha=0.1, lw=2, label='Exposed', linestyle='-') ### Infectious patients who don't know they have it # ax.plot(time_domain, self.scenario.infectious, color=TABLEAU_RED, alpha=0.5, lw=2, label='Infected', linestyle='-') ### Known and unknown infected, isolated at home # ax.plot(time_domain, self.scenario.sum_isolated, color=TAB_COLORS[8], alpha=.5, lw=2, label='Home Iso', linestyle='-') ### Hospital floor patients ax.plot(time_domain, self.scenario.sum_floor, color=TABLEAU_BLUE, alpha=1, lw=2, label='Noncrit', linestyle='--') ### Non-ventilated ICU patients ax.plot(time_domain, self.scenario.sum_icu, color=TABLEAU_GREEN, alpha=1, lw=2, label='ICU', linestyle='--') ### Ventilated ICU patients ax.plot(time_domain, self.scenario.sum_vent, color=TABLEAU_RED, alpha=1, lw=2, label='ICU + Ventilator', linestyle='--') ### Total hospitalized in all areas ax.plot(time_domain, self.scenario.sum_hospitalized, color=(1, 0, 0), alpha=.25, lw=2, label='Total Hospitalized', linestyle='-') ### Deceased ax.plot(time_domain, self.scenario.sum_deceased, color=(0, 0, 0), alpha=.5, lw=2, label='Dead', linestyle=':') ### Max non-icu capacity # ax.plot(time_domain, [229] * (self.total_days + 1), color=(0, 0, 1), alpha=1, lw=1, label='229 Floor beds', linestyle='-') ### Max ICU capacity # ax.plot(time_domain, [86] * (self.total_days + 1), color=(1, 0, 0), alpha=1, lw=1, label='86 ICU units', linestyle='-') ### Vertical line indicating today plt.axvline(x=datetime.today(), alpha=.5, lw=2, label='Today') ax.set_xlabel('Days') ax.set_ylabel('Number') chart_title = self.modelname plt.title(chart_title, fontsize=14) # ax.set_ylim(0,1.2) ax.yaxis.set_tick_params(length=4) ax.xaxis.set_tick_params(length=4) # ax.grid(b=True, which='minor', c='w', lw=1, ls='--') ax.grid() legend = ax.legend() legend.get_frame().set_alpha(0.5) for spine in ('top', 'right', 'bottom', 'left'): ax.spines[spine].set_visible(False) return plt def generate_csv(self): chart_title = self.modelname outfilename = "_".join( chart_title.replace("|", " ").replace(":", " ").replace(".", " ").split()) # Write a CSV to this directory with open(f"{outfilename}.csv", 'w') as outfile: for itr in range(0, len(self.scenario.out_susceptible)): outfile.write(f"{self.scenario.out_susceptible[itr]:.6f}, " f"{self.scenario.out_incubating[itr]:.6f}, " f"{self.scenario.out_infectious[itr]:.6f}, " f"{self.scenario.sum_isolated[itr]:.6f}, " f"{self.scenario.sum_floor[itr]:.6f}, " f"{self.scenario.sum_icu[itr]:.6f}, " f"{self.scenario.sum_vent[itr]:.6f}, " f"{self.scenario.sum_recovered[itr]:.6f}, " f"{self.scenario.sum_deceased[itr]:.6f}, " f"{self.scenario.sum_hospitalized[itr]:.6f}\n")
def __init__(self, subgroupstats, name=None): self.name = name self.stats = subgroupstats # Variables to store state changes # Probability states if self.name is not None: self.isolated = ProbState(14, name=f"{self.name}: isolated") else: self.isolated = ProbState(14) self.h_noncrit = ProbState(8) self.h_post_icu = ProbState(6) self.h_icu = ProbState(10) self.h_icu_vent = ProbState(10) self.recovered = ProbState(1000) self.deceased = ProbState(1000) self.isolated.add_exit_state(self.recovered, 1) self.isolated.normalize_states_over_period() self.h_noncrit.add_exit_state(self.recovered, 1) self.h_noncrit.normalize_states_over_period() self.h_icu.add_exit_state(self.deceased, self.stats.p_icu_death) self.h_icu.add_exit_state(self.h_post_icu, self.stats.p_icu_recovery) self.h_icu.normalize_states_over_period() self.h_icu_vent.add_exit_state(self.deceased, self.stats.p_icu_death) self.h_icu_vent.add_exit_state(self.h_post_icu, self.stats.p_icu_recovery) self.h_icu_vent.normalize_states_over_period() self.h_post_icu.add_exit_state(self.recovered, 1) self.h_post_icu.normalize_states_over_period()
class AgeGroup: def __init__(self, subgroupstats, name=None): self.name = name self.stats = subgroupstats # Variables to store state changes # Probability states if self.name is not None: self.isolated = ProbState(14, name=f"{self.name}: isolated") else: self.isolated = ProbState(14) self.h_noncrit = ProbState(8) self.h_post_icu = ProbState(6) self.h_icu = ProbState(10) self.h_icu_vent = ProbState(10) self.recovered = ProbState(1000) self.deceased = ProbState(1000) self.isolated.add_exit_state(self.recovered, 1) self.isolated.normalize_states_over_period() self.h_noncrit.add_exit_state(self.recovered, 1) self.h_noncrit.normalize_states_over_period() self.h_icu.add_exit_state(self.deceased, self.stats.p_icu_death) self.h_icu.add_exit_state(self.h_post_icu, self.stats.p_icu_recovery) self.h_icu.normalize_states_over_period() self.h_icu_vent.add_exit_state(self.deceased, self.stats.p_icu_death) self.h_icu_vent.add_exit_state(self.h_post_icu, self.stats.p_icu_recovery) self.h_icu_vent.normalize_states_over_period() self.h_post_icu.add_exit_state(self.recovered, 1) self.h_post_icu.normalize_states_over_period() # Add N people to the list of infected def apply_infections(self, infections): inf_float = float(infections) self.isolated.store_pending(inf_float * self.stats.p_selfisolate) self.h_noncrit.store_pending(inf_float * self.stats.p_nevercrit) self.h_icu.store_pending(inf_float * self.stats.p_icu_nonvent) self.h_icu_vent.store_pending(inf_float * self.stats.p_icu_vent) def calculate_redistributions(self): self.isolated.pass_downstream() self.h_noncrit.pass_downstream() self.h_icu.pass_downstream() self.h_icu_vent.pass_downstream() def apply_pending(self): self.isolated.apply_pending() self.h_noncrit.apply_pending() self.h_icu.apply_pending() self.h_icu_vent.apply_pending() self.recovered.apply_pending() self.deceased.apply_pending()
class AgeAdjustedModel: def __init__(self): self.r0 = 2.65 self.total_days = 0 self.population = POP_DENVER self.beta = None self.susceptible = ProbState(period=0, count=self.population - 1, name='susceptible') self.incubating = ProbState(period=3, name='incubating') self.infectious = ProbState(period=3.8, count=1, name='infectious') self.isolated_holding = ProbState(period=90, name='isolated_holding') self.incubating.add_exit_state(self.infectious, 1) self.incubating.normalize_states_over_period() self.infectious.add_exit_state(self.isolated_holding, 1) self.infectious.normalize_states_over_period() self.subgroups = dict() for key, value in AGE_DISTRIBUTION.items(): self.subgroups[key] = AgeGroup(SubgroupRates(ICD[key], value), name=key) self.sum_isolated = None self.sum_noncrit = None self.sum_icu = None self.sum_icu_vent = None self.sum_recovered = None self.sum_deceased = None def gather_sums(self): # print(f"base:{len(self.susceptible.domain)}") self.sum_isolated = [0] * len(self.susceptible.domain) self.sum_noncrit = [0] * len(self.susceptible.domain) self.sum_icu = [0] * len(self.susceptible.domain) self.sum_icu_vent = [0] * len(self.susceptible.domain) self.sum_recovered = [0] * len(self.susceptible.domain) self.sum_deceased = [0] * len(self.susceptible.domain) # print(f"final isolated 0-9:{len(self.subgroups['0-9'].isolated.domain)} {self.subgroups['0-9'].isolated.pending}, {self.subgroups['0-9'].isolated.count}") for key, value in self.subgroups.items(): # print(f"adding isolated {key}: {self.sum_isolated} {value.isolated.domain}") self.sum_isolated = np.add(self.sum_isolated, value.isolated.domain) self.sum_noncrit = np.add(self.sum_noncrit, value.h_noncrit.domain) self.sum_icu = np.add(self.sum_icu, value.h_icu.domain) self.sum_icu_vent = np.add(self.sum_icu_vent, value.h_icu_vent.domain) self.sum_recovered = np.add(self.sum_recovered, value.recovered.domain) self.sum_deceased = np.add(self.sum_deceased, value.deceased.domain) def reset(self): self.total_days = 0 self.susceptible.reset() self.susceptible.count = self.population - 1 self.incubating.reset() self.infectious.reset() self.infectious.count = 1 self.subgroups = dict() for key, value in AGE_DISTRIBUTION.items(): self.subgroups[key] = AgeGroup(SubgroupRates(ICD[key], value), name=key) def set_r0(self, value): self.r0 = value def set_population(self, value): self.population = value def run_period(self, days): # Integrate the SIR equations over the time grid, t. for _ in range(0, days): self.step_day() def run_r0_set(self, date_offsets, r0_values): self.reset() day_counter = 0 for itr in range(0, len(date_offsets)): self.set_r0(r0_values[itr]) self.beta = calc_beta(self.r0, self.dayspergen) while day_counter < date_offsets[itr]: self.step_day() day_counter += 1 def step_day(self): new_infections = self.beta * self.susceptible.count * self.infectious.count / self.population # print(f"Day {self.total_days}, {self.beta} * {self.susceptible.count} * {self.infectious.count} / {self.population} = {new_infections}") self.susceptible.store_pending(-new_infections) self.incubating.store_pending(new_infections) self.incubating.pass_downstream() self.infectious.pass_downstream() diagnosed = self.isolated_holding.pending self.isolated_holding.pending = 0 for key, agegroup in self.subgroups.items(): subpop = diagnosed * agegroup.stats.pop_dist agegroup.apply_infections(subpop) agegroup.calculate_redistributions() self.susceptible.apply_pending() self.incubating.apply_pending() self.infectious.apply_pending() for key, agegroup in self.subgroups.items(): agegroup.apply_pending() self.total_days += 1
class SEIRHModel: def __init__(self): self.r0 = 2.65 self.total_days = 0 self.population = POP_DENVER self.beta = None self.susceptible = ProbState(period=0, count=self.population) self.incubating = ProbState(period=3) self.infectious = ProbState(period=3.8, count=1) self.isolated = ProbState(period=14) self.h_noncritical = ProbState(period=8) self.h_critical = ProbState(period=6) self.h_icu = ProbState(period=10) self.recovered = ProbState(period=10000) self.dead = ProbState(period=10000) self.incubating.add_exit_state(self.infectious, 1) self.incubating.normalize_states_over_period() self.isolated.add_exit_state(self.recovered, 1) self.isolated.normalize_states_over_period() self.infectious.add_exit_state(self.isolated, .85) self.infectious.add_exit_state(self.h_noncritical, .11) self.infectious.add_exit_state(self.h_critical, .4) self.infectious.normalize_states_over_period() self.h_noncritical.add_exit_state(self.recovered, 1) self.h_noncritical.normalize_states_over_period() self.h_critical.add_exit_state(self.recovered, 1) self.h_critical.normalize_states_over_period() self.h_icu.add_exit_state(self.recovered, .75) self.h_icu.add_exit_state(self.dead, .25) self.h_icu.normalize_states_over_period() def reset(self): self.total_days = 0 self.susceptible.reset() self.susceptible.count = self.population - 1 self.incubating.reset() self.infectious.reset() self.infectious.count = 1 self.isolated.reset() self.h_noncritical.reset() self.h_critical.reset() self.h_icu.reset() self.recovered.reset() self.dead.reset() def set_r0(self, value): self.r0 = value def set_population(self, value): self.population = value def recalculate(self): self.beta = calc_beta(self.r0, self.dayspergen) def run_period(self, days): time_domain = np.linspace(0, days, days + 1) # Initial conditions vector init = (self.susceptible.count, self.incubating.count, self.infectious.count, self.isolated.count, self.h_noncritical.count, self.h_critical.count, self.h_icu.count, self.recovered.count, self.dead.count) # Integrate the SIR equations over the time grid, t. results = odeint(deriv_seirh, init, time_domain, args=(self, )) (d_susceptible, d_incubating, d_infectious, d_isolated, d_noncritical, d_critical, d_icu, d_recovered, d_dead) = results.T self.total_days += days self.susceptible.extend(d_susceptible) self.incubating.extend(d_incubating) self.infectious.extend(d_infectious) self.isolated.extend(d_isolated) self.h_noncritical.extend(d_noncritical) self.h_critical.extend(d_critical) self.h_icu.extend(d_icu) self.recovered.extend(d_recovered) self.dead.extend(d_dead) def run_r0_set(self, date_offsets, r0_values): self.reset() prev_date = 0 for itr in range(0, len(date_offsets)): self.set_r0(r0_values[itr]) self.beta = calc_beta(self.r0, self.dayspergen) span = date_offsets[itr] - prev_date + 1 self.run_period(span) prev_date = date_offsets[itr]