def test_get_population_by_agegroup__with_region(): age_breakpoints = [0, 10, 20, 30, 40, 50, 60, 70, 80] country_iso_code = "AUS" population = get_population_by_agegroup( age_breakpoints, country_iso_code, region="Victoria", year=2020 ) assert population == [816027, 768266, 1019387, 999073, 847833, 778843, 650779, 446567, 268029]
def get_infection_fatality_proportions( infection_fatality_props_10_year, infection_rate_multiplier, iso3, pop_region, pop_year, ): """ Returns the Proportion of people in age group who die, given the total number of people in that compartment. ie: dead / total infected """ if_props_10_year = [ apply_odds_ratio_to_proportion(i_prop, infection_rate_multiplier) for i_prop in infection_fatality_props_10_year ] # Calculate the proportion of 80+ years old among the 75+ population elderly_populations = inputs.get_population_by_agegroup([0, 75, 80], iso3, pop_region, year=pop_year) prop_over_80 = elderly_populations[2] / sum(elderly_populations[1:]) # Infection fatality rate by age group. # Data in props may have used 10 year bands 0-80+, but we want 5 year bands from 0-75+ # Calculate 75+ age bracket as weighted average between 75-79 and half 80+ if len(infection_fatality_props_10_year) == 17: last_ifr = if_props_10_year[-1] * prop_over_80 + if_props_10_year[ -2] * (1 - prop_over_80) ifrs_by_age = if_props_10_year[:-1] ifrs_by_age[-1] = last_ifr else: ifrs_by_age = repeat_list_elements_average_last_two( if_props_10_year, prop_over_80) return ifrs_by_age
def get_testing_pop(agegroup_strata: List[str], country: Country, pop: Population): """ Returns the age stratified population used for case detection testing. Use state denominator for testing rates for the Victorian health cluster models and temporarily use Philippines regional pops for all the Philippines sub-regions """ testing_region = "Victoria" if country.iso3 == "AUS" else pop.region testing_year = 2020 if country.iso3 == "AUS" else pop.year testing_pop = inputs.get_population_by_agegroup(agegroup_strata, country.iso3, testing_region, year=testing_year) return testing_pop, testing_region
def _build_age_strat(params: dict, uni_death_flow_names: list): # Apply age-stratification age_strat = AgeStratification("age", params["age_breakpoints"], COMPARTMENTS) # Set age demographics pop = get_population_by_agegroup(age_breakpoints=params["age_breakpoints"], country_iso_code=params["iso3"], year=2000) age_split_props = dict( zip(params["age_breakpoints"], [x / sum(pop) for x in pop])) age_strat.set_population_split(age_split_props) # Add age-based heterogeneous mixing mixing_matrix = get_mixing_matrix_specific_agegroups( country_iso_code=params["iso3"], requested_age_breaks=list(map(int, params["age_breakpoints"])), time_unit="years", ) # FIXME: These values break the solver because they are very big. # age_strat.set_mixing_matrix(mixing_matrix) # Add age-based flow adjustments. age_strat.add_flow_adjustments( "stabilisation", _adjust_all_multiply(params["stabilisation_rate_stratified"]["age"])) age_strat.add_flow_adjustments( "early_activation", _adjust_all_multiply( params["early_activation_rate_stratified"]["age"])) age_strat.add_flow_adjustments( "late_activation", _adjust_all_multiply(params["late_activation_rate_stratified"]["age"])) # Add age-specific all-causes mortality rate. death_rates_by_age, death_rate_years = get_death_rates_by_agegroup( params["age_breakpoints"], params["iso3"]) death_rates_by_age = { age: scale_up_function(death_rate_years, death_rates_by_age[int(age)], smoothness=0.2, method=5) for age in params["age_breakpoints"] } for uni_death_flow_name in uni_death_flow_names: age_strat.add_flow_adjustments( uni_death_flow_name, _adjust_all_multiply(death_rates_by_age)) return age_strat
def get_cluster_strat(params: Parameters) -> Stratification: cluster_strat = Stratification("cluster", CLUSTER_STRATA, COMPARTMENTS) country = params.country vic = params.victorian_clusters # Determine how to split up population by cluster # There is -0.5% to +4% difference per age group between sum of region population in 2018 and # total VIC population in 2020 region_pops = { region: sum( inputs.get_population_by_agegroup(AGEGROUP_STRATA, country.iso3, region.upper(), year=2018)) for region in CLUSTER_STRATA } sum_region_props = sum(region_pops.values()) cluster_split_props = { region: pop / sum_region_props for region, pop in region_pops.items() } cluster_strat.set_population_split(cluster_split_props) # Adjust contact rate multipliers contact_rate_adjustments = {} for cluster in Region.VICTORIA_METRO + [Region.BARWON_SOUTH_WEST]: cluster_name = cluster.replace("-", "_") contact_rate_multiplier = getattr( vic, f"contact_rate_multiplier_{cluster_name}") contact_rate_adjustments[cluster_name] = Multiply( contact_rate_multiplier) for cluster in Region.VICTORIA_RURAL: if cluster != Region.BARWON_SOUTH_WEST: cluster_name = cluster.replace("-", "_") contact_rate_multiplier = getattr( vic, "contact_rate_multiplier_regional") contact_rate_adjustments[cluster_name] = Multiply( contact_rate_multiplier) # Add in flow adjustments per-region so we can calibrate the contact rate for each region. cluster_strat.add_flow_adjustments("infection", contact_rate_adjustments) # Use an identity mixing matrix to temporarily declare no inter-cluster mixing, which will then be over-written cluster_mixing_matrix = np.eye(len(CLUSTER_STRATA)) cluster_strat.set_mixing_matrix(cluster_mixing_matrix) return cluster_strat
def test_get_population_by_agegroup(): age_breakpoints = [0, 10, 20, 30, 40, 50, 60, 70, 80] country_iso_code = "AUS" population = get_population_by_agegroup( age_breakpoints, country_iso_code, region=None, year=2020 ) assert population == [ 3308972, 3130480, 3375453, 3718346, 3306061, 3107734, 2651187, 1846377, 1055274, ]
def stratify_by_clinical(model, model_parameters, compartments): """ Stratify the infectious compartments of the covid model (not including the pre-symptomatic compartments, which are actually infectious) - notifications are derived from progress from early to late for some strata - the proportion of people moving from presymt to early infectious, conditioned on age group - rate of which people flow through these compartments (reciprocal of time, using within_* which is a rate of ppl / day) - infectiousness levels adjusted by early/late and for clinical strata - we start with an age stratified infection fatality rate - 50% of deaths for each age bracket die in ICU - the other deaths go to hospital, assume no-one else can die from COVID - should we ditch this? """ # General stratification agegroup_strata = model_parameters["all_stratifications"]["agegroup"] all_stratifications = model_parameters["all_stratifications"] clinical_strata = model_parameters["clinical_strata"] # Infection rate multiplication # Importation implement_importation = model_parameters["implement_importation"] traveller_quarantine = model_parameters["traveller_quarantine"] # Time variant case detection prop_detected_among_symptomatic = model_parameters["prop_detected_among_symptomatic"] # FIXME: Make it clear that this for tahn tv_detection_b = model_parameters["tv_detection_b"] tv_detection_c = model_parameters["tv_detection_c"] tv_detection_sigma = model_parameters["tv_detection_sigma"] # ??? within_hospital_early = model_parameters["within_hospital_early"] within_icu_early = model_parameters["within_icu_early"] # Strata entry and infection death proportions icu_prop = model_parameters["icu_prop"] icu_mortality_prop = model_parameters["icu_mortality_prop"] infection_fatality_props_10_year = model_parameters["infection_fatality_props"] hospital_props_10_year = model_parameters["hospital_props"] hospital_props_multiplier = model_parameters["hospital_props_multiplier"] symptomatic_props_10_year = model_parameters["symptomatic_props"] use_raw_mortality_estimates = model_parameters["use_raw_mortality_estimates"] ifr_double_exp_model_params = model_parameters["ifr_double_exp_model_params"] # Define stratification - only stratify infected compartments strata_to_implement = clinical_strata model_parameters["all_stratifications"]["clinical"] = strata_to_implement compartments_to_split = [ comp for comp in compartments if comp.startswith(Compartment.EARLY_INFECTIOUS) or comp.startswith(Compartment.LATE_INFECTIOUS) ] # FIXME: Set params to make comparison happy model_parameters["infection_fatality_props"] = infection_fatality_props_10_year model_parameters["hospital_props"] = hospital_props_10_year # Calculate the proportion of 80+ years old among the 75+ population elderly_populations = get_population_by_agegroup([0, 75, 80], model_parameters["iso3"], model_parameters["region"]) prop_over_80 = elderly_populations[2] / sum(elderly_populations[1:]) # Age dependent proportions of infected people who become symptomatic. # This is defined 8x10 year bands, 0-70+, which we transform into 16x5 year bands 0-75+ symptomatic_props = repeat_list_elements(2, symptomatic_props_10_year) # Age dependent proportions of symptomatic people who become hospitalised. # This is defined 9x10 year bands, 0-80+, which we trransform into 16x5 year bands 0-75+ # Calculate 75+ age bracket as half 75-79 and half 80+ hospital_props = repeat_list_elements_average_last_two( [p * hospital_props_multiplier for p in hospital_props_10_year], prop_over_80) # Infection fatality rate by age group. if use_raw_mortality_estimates: # Data in props used 10 year bands 0-80+, but we want 5 year bands from 0-75+ # Calculate 75+ age bracket as weighted average between 75-79 and half 80+ infection_fatality_props = repeat_list_elements_average_last_two( infection_fatality_props_10_year, prop_over_80 ) else: infection_fatality_props = age_specific_ifrs_from_double_exp_model( ifr_double_exp_model_params['k'], ifr_double_exp_model_params['m'], ifr_double_exp_model_params['last_representative_age'] ) # Find the absolute progression proportions. symptomatic_props_arr = np.array(symptomatic_props) hospital_props_arr = np.array(hospital_props) # Determine the absolute proportion of presymptomatic who become sympt vs non-sympt. sympt, non_sympt = subdivide_props(1, symptomatic_props_arr) # Determine the absolute proportion of sympt who become hospitalized vs non-hospitalized. sympt_hospital, sympt_non_hospital = subdivide_props(sympt, hospital_props_arr) # Determine the absolute proportion of hospitalized who become icu vs non-icu. sympt_hospital_icu, sympt_hospital_non_icu = subdivide_props(sympt_hospital, icu_prop) abs_props = { "sympt": sympt.tolist(), "non_sympt": non_sympt.tolist(), "hospital": sympt_hospital.tolist(), "sympt_non_hospital": sympt_non_hospital.tolist(), # Overidden by a by time-varying proprotion later "icu": sympt_hospital_icu.tolist(), "hospital_non_icu": sympt_hospital_non_icu.tolist(), } # Calculate the absolute proportion of all patients who should eventually reach hospital death or ICU death. # Find IFR that needs to be contributed by ICU and non-ICU hospital deaths hospital_death, icu_death = [], [] for age_idx, agegroup in enumerate(agegroup_strata): # If IFR for age group is greater than absolute proportion hospitalised, increased hospitalised proportion if infection_fatality_props[age_idx] > abs_props["hospital"][age_idx]: abs_props["hospital"][age_idx] = infection_fatality_props[age_idx] # Find the target absolute ICU mortality and the amount left over from IFRs to go to hospital, if any target_icu_abs_mort = abs_props["icu"][age_idx] * icu_mortality_prop left_over_mort = infection_fatality_props[age_idx] - target_icu_abs_mort # If some IFR will be left over for the hospitalised if left_over_mort > 0.0: hospital_death_prop = left_over_mort icu_death_prop = target_icu_abs_mort # Otherwise if all IFR taken up by ICU else: hospital_death_prop = 0.0 icu_death_prop = infection_fatality_props[age_idx] hospital_death.append(hospital_death_prop) icu_death.append(icu_death_prop) abs_props.update({"hospital_death": hospital_death, "icu_death": icu_death}) # FIXME: These depend on static variables which have been made time-variant. # fatality rate for hospitalised patients rel_props = { "hospital_death": element_wise_list_division( abs_props["hospital_death"], abs_props["hospital_non_icu"] ), "icu_death": element_wise_list_division(abs_props["icu_death"], abs_props["icu"]), } # Progression rates into the infectious compartment(s) # Define progresion rates into non-symptomatic compartments using parameter adjustment. stratification_adjustments = {} for age_idx, age in enumerate(agegroup_strata): key = f"to_infectiousXagegroup_{age}" stratification_adjustments[key] = { "non_sympt": non_sympt[age_idx], "icu": sympt_hospital_icu[age_idx], "hospital_non_icu": sympt_hospital_non_icu[age_idx], } # Create a function for the proportion of symptomatic people who are detected at timestep `t`. scale_up_multiplier = \ tanh_based_scaleup(tv_detection_b, tv_detection_c, tv_detection_sigma) # Create function describing the proportion of cases detected over time def prop_detect_among_sympt_func(t): # Raw value without adjustment for any improved detection intervention without_intervention_value = prop_detected_among_symptomatic * scale_up_multiplier(t) # Return value modified for any future intervention that narrows the case detection gap int_detect_gap_reduction = model_parameters['int_detection_gap_reduction'] return without_intervention_value + (1. - without_intervention_value) * int_detect_gap_reduction # Set time-varying isolation proportions for age_idx, agegroup in enumerate(agegroup_strata): # Pass the functions to the model tv_props = TimeVaryingProprotions(age_idx, abs_props, prop_detect_among_sympt_func) time_variants = [ [f"prop_sympt_non_hospital_{agegroup}", tv_props.get_abs_prop_sympt_non_hospital,], [f"prop_sympt_isolate_{agegroup}", tv_props.get_abs_prop_isolated], ] for name, func in time_variants: model.time_variants[name] = func # Tell the model to use these time varying functions for the stratification adjustments. agegroup_adj = stratification_adjustments[f"to_infectiousXagegroup_{agegroup}"] agegroup_adj["sympt_non_hospital"] = f"prop_sympt_non_hospital_{agegroup}" agegroup_adj["sympt_isolate"] = f"prop_sympt_isolate_{agegroup}" # Calculate death rates and progression rates for hospitalised and ICU patients progression_death_rates = {} for stratum in ("hospital", "icu"): ( progression_death_rates[stratum + "_infect_death"], progression_death_rates[stratum + "_within_late"], ) = find_rates_and_complements_from_ifr( rel_props[stratum + "_death"], 1, [model_parameters["within_" + stratum + "_late"]] * 16, ) # Death and non-death progression between infectious compartments towards the recovered compartment for param in ("within_late", "infect_death"): stratification_adjustments.update( adjust_upstream_stratified_parameter( param, strata_to_implement[3:], "agegroup", model_parameters["all_stratifications"]["agegroup"], [ progression_death_rates["hospital_" + param], progression_death_rates["icu_" + param], ], overwrite=True, ) ) # Over-write rate of progression for early compartments for hospital and ICU # FIXME: Ask Romain if he knows why we bother doing this. stratification_adjustments.update( { "within_infectious": { "hospital_non_icuW": model_parameters["within_hospital_early"], "icuW": model_parameters["within_icu_early"], }, } ) # Sort out all infectiousness adjustments for entire model here # Sort out all infectiousness adjustments for all compartments of the model. # Now make adjustment for asymptomatic patients only strata_infectiousness = {} for stratum in strata_to_implement: if stratum + "_infect_multiplier" in model_parameters: strata_infectiousness[stratum] = model_parameters[stratum + "_infect_multiplier"] # Make adjustment for isolation/quarantine for stratum in strata_to_implement: if stratum in model_parameters["late_infect_multiplier"]: model.individual_infectiousness_adjustments.append( [ [Compartment.LATE_INFECTIOUS, "clinical_" + stratum], model_parameters["late_infect_multiplier"][stratum], ] ) # FIXME: Ask Romain about importation # work out time-variant clinical proportions for imported cases accounting for quarantine if model_parameters["implement_importation"]: rep_age_group = ( "35" # the clinical split will be defined according to this representative age-group ) tvs = model.time_variants # to reduce verbosity # create scale-up function for quarantine quarantine_scale_up = scale_up_function( model_parameters["traveller_quarantine"]["times"], model_parameters["traveller_quarantine"]["values"], method=4, ) # set fixed clinical proportions for imported cases (hospital_non_icu and icu) importation_props_by_clinical = { "hospital_non_icu": stratification_adjustments[ "to_infectiousXagegroup_" + rep_age_group ]["hospital_non_icu"], "icu": stratification_adjustments["to_infectiousXagegroup_" + rep_age_group]["icu"], } # create time-variant function for remaining imported clinical proportions tv_prop_imported_non_sympt = lambda t: stratification_adjustments[ "to_infectiousXagegroup_" + rep_age_group ]["non_sympt"] * (1.0 - quarantine_scale_up(t)) tv_prop_imported_sympt_non_hospital = lambda t: tvs[ stratification_adjustments["to_infectiousXagegroup_" + rep_age_group][ "sympt_non_hospital" ] ](t) * (1.0 - quarantine_scale_up(t)) tv_prop_imported_sympt_isolate = lambda t: tvs[ stratification_adjustments["to_infectiousXagegroup_" + rep_age_group]["sympt_isolate"] ](t) + quarantine_scale_up(t) * ( tvs[ stratification_adjustments["to_infectiousXagegroup_" + rep_age_group][ "sympt_non_hospital" ] ](t) + stratification_adjustments["to_infectiousXagegroup_" + rep_age_group]["non_sympt"] ) # Pass time-variant functions to the model object model.time_variants["tv_prop_imported_non_sympt"] = tv_prop_imported_non_sympt model.time_variants[ "tv_prop_imported_sympt_non_hospital" ] = tv_prop_imported_sympt_non_hospital model.time_variants["tv_prop_imported_sympt_isolate"] = tv_prop_imported_sympt_isolate for stratum in ["non_sympt", "sympt_isolate", "sympt_non_hospital"]: importation_props_by_clinical[stratum] = "tv_prop_imported_" + stratum # create absolute time-variant case detection proportion that will be returned to be used to set importation flow def modelled_abs_detection_proportion_imported(t): return ( stratification_adjustments["to_infectiousXagegroup_" + rep_age_group]["icu"] + stratification_adjustments["to_infectiousXagegroup_" + rep_age_group][ "hospital_non_icu" ] + tvs[ stratification_adjustments["to_infectiousXagegroup_" + rep_age_group][ "sympt_isolate" ] ](t) ) else: importation_props_by_clinical = {} modelled_abs_detection_proportion_imported = None # Stratify the model using the SUMMER stratification function model.stratify( "clinical", strata_to_implement, compartments_to_split, infectiousness_adjustments=strata_infectiousness, requested_proportions={ stratum: 1.0 / len(strata_to_implement) for stratum in strata_to_implement }, adjustment_requests=stratification_adjustments, entry_proportions=importation_props_by_clinical, verbose=False, ) return modelled_abs_detection_proportion_imported
def multi_country_cdr(plotter, calib_dir_path, mcmc_tables, mcmc_params, targets, app_name, region_name): """ Code taken directly from the fit calibration file at this stage. """ from dash.dashboards.calibration_results.plots import get_cdr_constants param_name = "testing_to_detection.assumed_cdr_parameter" start_date = st.sidebar.slider("Start date", 1, 365, 1) end_date = st.sidebar.slider("End date", 1, 365, 275) samples = st.sidebar.slider("Samples", 1, 200, 10) label_rotation = st.sidebar.slider("Label rotation", 0, 90, 0) detected_proportions = [] # Get data for plotting for i_region in range(len(region_name)): # Extract parameters relevant to this function region = region_name[i_region].replace("-", "_") params = load_params(app_name, region) ( iso3, testing_year, assumed_tests_parameter, smoothing_period, agegroup_params, time_params, times, agegroup_strata, ) = get_cdr_constants(params["default"]) pop_region = params["default"]["population"]["region"] pop_year = params["default"]["population"]["year"] # Collate parameters into one structure testing_to_detection_values = [] for i_chain in range(len(mcmc_params)): param_mask = mcmc_params[i_chain][0]["name"] == param_name testing_to_detection_values += mcmc_params[i_chain][0]["value"][ param_mask].tolist() sampled_test_to_detect_vals = random.sample( testing_to_detection_values, samples) # Get CDR function - needs to be done outside of autumn, because it is importing from the apps testing_region = "Victoria" if iso3 == "AUS" else pop_region testing_year = 2020 if iso3 == "AUS" else pop_year testing_pops = inputs.get_population_by_agegroup(agegroup_strata, iso3, testing_region, year=testing_year) detected_proportions.append([]) for assumed_cdr_parameter in sampled_test_to_detect_vals: detected_proportions[i_region].append( find_cdr_function_from_test_data( assumed_tests_parameter, assumed_cdr_parameter, smoothing_period, iso3, testing_pops, subregion=testing_region, )) plots.calibration.plots.plot_multi_cdr_curves(plotter, times, detected_proportions, start_date, end_date, label_rotation, region_name)
def build_model(params: dict) -> CompartmentalModel: """ Build the compartmental model from the provided parameters. """ params = Parameters(**params) model = CompartmentalModel( times=[params.time.start, params.time.end], compartments=COMPARTMENTS, infectious_compartments=INFECTIOUS_COMPARTMENTS, timestep=params.time.step, ) # Population distribution country = params.country pop = params.population # Time periods calculated from periods (ie "sojourn times") compartment_periods = preprocess.compartments.calc_compartment_periods( params.sojourn) # Get country population by age-group total_pops = inputs.get_population_by_agegroup(AGEGROUP_STRATA, country.iso3, pop.region, year=pop.year) # Distribute infectious seed across infectious split sub-compartments total_disease_time = sum( [compartment_periods[c] for c in DISEASE_COMPARTMENTS]) init_pop = { c: params.infectious_seed * compartment_periods[c] / total_disease_time for c in DISEASE_COMPARTMENTS } # Assign the remainder starting population to the S compartment init_pop[Compartment.SUSCEPTIBLE] = sum(total_pops) - sum( init_pop.values()) model.set_initial_population(init_pop) # Add intercompartmental flows if params.seasonal_force: # Use a time-varying, sinusoidal seasonal forcing function for contact rate. contact_rate = get_seasonal_forcing(365.0, 173.0, params.seasonal_force, params.contact_rate) else: # Use a static contact rate. contact_rate = params.contact_rate # Adjust contact rate for Variant of Concerns if params.voc_emmergence: voc_multiplier = scale_up_function( x=[ params.voc_emmergence.start_time, params.voc_emmergence.end_time ], y=[ 1.0, 1.0 + params.voc_emmergence.final_proportion * (params.voc_emmergence.contact_rate_multiplier - 1.0), ], method=4, ) raw_contact_rate = contact_rate if isinstance(contact_rate, float): def contact_rate(t): return raw_contact_rate * voc_multiplier(t) else: def contact_rate(t): return raw_contact_rate(t) * voc_multiplier(t) model.add_infection_frequency_flow( name="infection", contact_rate=contact_rate, source=Compartment.SUSCEPTIBLE, dest=Compartment.EARLY_EXPOSED, ) # Infection progress flows. model.add_fractional_flow( name="infect_onset", fractional_rate=1.0 / compartment_periods[Compartment.EARLY_EXPOSED], source=Compartment.EARLY_EXPOSED, dest=Compartment.LATE_EXPOSED, ) model.add_fractional_flow( name="incidence", fractional_rate=1.0 / compartment_periods[Compartment.LATE_EXPOSED], source=Compartment.LATE_EXPOSED, dest=Compartment.EARLY_ACTIVE, ) model.add_fractional_flow( name="progress", fractional_rate=1.0 / compartment_periods[Compartment.EARLY_ACTIVE], source=Compartment.EARLY_ACTIVE, dest=Compartment.LATE_ACTIVE, ) # Recovery flows model.add_fractional_flow( name="recovery", fractional_rate=1.0 / compartment_periods[Compartment.LATE_ACTIVE], source=Compartment.LATE_ACTIVE, dest=Compartment.RECOVERED, ) # Infection death model.add_death_flow( name="infect_death", death_rate=0, # Will be overwritten later in clinical stratification. source=Compartment.LATE_ACTIVE, ) if params.waning_immunity_duration is not None: # Waning immunity (if requested) model.add_fractional_flow( name="warning_immunity", fractional_rate=1.0 / params.waning_immunity_duration, source=Compartment.RECOVERED, dest=Compartment.SUSCEPTIBLE, ) # Stratify the model by age group. age_strat = get_agegroup_strat(params, total_pops) model.stratify_with(age_strat) # Stratify the model by clinical status clinical_strat = get_clinical_strat(params) model.stratify_with(clinical_strat) # Stratify by immunity - which will include vaccination and infection history if params.stratify_by_immunity: immunity_strat = get_immunity_strat(params) model.stratify_with(immunity_strat) if params.vaccination: vacc_params = params.vaccination for roll_out_component in vacc_params.roll_out_components: add_vaccination_flows(model, roll_out_component, age_strat.strata) # Infection history stratification if params.stratify_by_infection_history: history_strat = get_history_strat(params, compartment_periods) model.stratify_with(history_strat) # Stratify model by Victorian subregion (used for Victorian cluster model). if params.victorian_clusters: cluster_strat = get_cluster_strat(params) model.stratify_with(cluster_strat) apply_post_cluster_strat_hacks(params, model) # Set up derived output functions if not params.victorian_clusters: request_standard_outputs(model, params) else: request_victorian_outputs(model, params) return model
def get_country_population_size(country): iso_3 = inputs.demography.queries.get_iso3_from_country_name(country) return sum(inputs.get_population_by_agegroup(["0"], iso_3, None, year=2020))
def plot_cdr_curves( plotter: StreamlitPlotter, calib_dir_path: str, mcmc_tables: List[pd.DataFrame], mcmc_params: List[pd.DataFrame], targets: dict, app_name: str, region: str, ): param_name = "testing_to_detection.assumed_cdr_parameter" region_name = region.replace("-", "_") end_date = st.sidebar.slider("End date", 1, 365, 275) samples = st.sidebar.slider("Samples", 1, 200, 10) label_rotation = st.sidebar.slider("Label rotation", 0, 90, 0) # Extract parameters relevant to this function params = load_params(app_name, region_name) ( iso3, testing_year, assumed_tests_parameter, smoothing_period, agegroup_params, time_params, times, agegroup_strata, ) = get_cdr_constants(params["default"]) # Collate parameters into one structure testing_to_detection_values = [] for i_chain in range(len(mcmc_params)): param_mask = mcmc_params[i_chain]["name"] == param_name testing_to_detection_values += mcmc_params[i_chain]["value"][ param_mask].tolist() # Sample testing values from all the ones available, to avoid plotting too many curves if samples > len(testing_to_detection_values): st.write( "Warning: Requested samples greater than detection values estimated" ) samples = len(testing_to_detection_values) sampled_test_to_detect_vals = random.sample(testing_to_detection_values, samples) # Get CDR function - needs to be done outside of autumn, because it is importing from the apps testing_pops = inputs.get_population_by_agegroup(agegroup_strata, iso3, None, year=testing_year) if iso3 == "AUS": st.write("WARNING - testing populations are not correct for Victoria") detected_proportion = [] for assumed_cdr_parameter in sampled_test_to_detect_vals: detected_proportion.append( find_cdr_function_from_test_data( assumed_tests_parameter, assumed_cdr_parameter, smoothing_period, iso3, testing_pops, )) plots.calibration.plots.plot_cdr_curves(plotter, times, detected_proportion, end_date, label_rotation)
def build_model(params: dict) -> StratifiedModel: """ Build the master function to run the TB model for Covid-19 """ validate_params(params) # Get the agegroup strata breakpoints. agegroup_max = params["agegroup_breaks"][0] agegroup_step = params["agegroup_breaks"][1] agegroup_strata = list(range(0, agegroup_max, agegroup_step)) # Look up the country population size by age-group, using UN data country_iso3 = params["iso3"] region = params["region"] total_pops = inputs.get_population_by_agegroup(agegroup_strata, country_iso3, region, year=2020) life_expectancy = inputs.get_life_expectancy_by_agegroup( agegroup_strata, country_iso3)[0] life_expectancy_latest = [ life_expectancy[agegroup][-1] for agegroup in life_expectancy ] # Define compartments compartments = [ Compartment.SUSCEPTIBLE, Compartment.EXPOSED, Compartment.PRESYMPTOMATIC, Compartment.EARLY_INFECTIOUS, Compartment.LATE_INFECTIOUS, Compartment.RECOVERED, ] # Indicate whether the compartments representing active disease are infectious is_infectious = { Compartment.EXPOSED: False, Compartment.PRESYMPTOMATIC: True, Compartment.EARLY_INFECTIOUS: True, Compartment.LATE_INFECTIOUS: True, } # Calculate compartment periods # FIXME: Needs tests. base_compartment_periods = params["compartment_periods"] compartment_periods_calc = params["compartment_periods_calculated"] compartment_periods = preprocess.compartments.calc_compartment_periods( base_compartment_periods, compartment_periods_calc) # Get progression rates from sojourn times, distinguishing to_infectious in order to split this parameter later compartment_exit_flow_rates = {} for compartment in compartment_periods: param_key = f"within_{compartment}" compartment_exit_flow_rates[ param_key] = 1.0 / compartment_periods[compartment] # Distribute infectious seed across infectious compartments infectious_seed = params["infectious_seed"] total_disease_time = sum([compartment_periods[c] for c in is_infectious]) init_pop = { c: infectious_seed * compartment_periods[c] / total_disease_time for c in is_infectious } # Force the remainder starting population to go to S compartment (Required as entry_compartment is late_infectious) init_pop[Compartment.SUSCEPTIBLE] = sum(total_pops) - sum( init_pop.values()) # Set integration times start_time = params["start_time"] end_time = params["end_time"] time_step = params["time_step"] integration_times = get_model_times_from_inputs( round(start_time), end_time, time_step, ) # Add inter-compartmental transition flows flows = preprocess.flows.DEFAULT_FLOWS # Choose a birth approach is_importation_active = params["implement_importation"] birth_approach = BirthApproach.ADD_CRUDE if is_importation_active else BirthApproach.NO_BIRTH # Build mixing matrix. static_mixing_matrix = preprocess.mixing_matrix.build_static(country_iso3) dynamic_mixing_matrix = None dynamic_location_mixing_params = params["mixing"] dynamic_age_mixing_params = params["mixing_age_adjust"] microdistancing = params["microdistancing"] if dynamic_location_mixing_params or dynamic_age_mixing_params: npi_effectiveness_params = params["npi_effectiveness"] google_mobility_locations = params["google_mobility_locations"] is_periodic_intervention = params.get("is_periodic_intervention") periodic_int_params = params.get("periodic_intervention") dynamic_mixing_matrix = preprocess.mixing_matrix.build_dynamic( country_iso3, region, dynamic_location_mixing_params, dynamic_age_mixing_params, npi_effectiveness_params, google_mobility_locations, is_periodic_intervention, periodic_int_params, end_time, microdistancing, ) # FIXME: Remove params from model_parameters model_parameters = {**params, **compartment_exit_flow_rates} model_parameters["to_infectious"] = model_parameters["within_presympt"] # Instantiate SUMMER model model = StratifiedModel( integration_times, compartments, init_pop, model_parameters, flows, birth_approach=birth_approach, entry_compartment=Compartment. LATE_INFECTIOUS, # to model imported cases starting_population=sum(total_pops), infectious_compartment=[ i_comp for i_comp in is_infectious if is_infectious[i_comp] ], ) if dynamic_mixing_matrix: model.find_dynamic_mixing_matrix = dynamic_mixing_matrix model.dynamic_mixing_matrix = True # Implement seasonal forcing if requested, making contact rate a time-variant rather than constant if model_parameters["seasonal_force"]: seasonal_forcing_function = \ get_seasonal_forcing( 365., 173., model_parameters["seasonal_force"], model_parameters["contact_rate"] ) model.time_variants["contact_rate"] = \ seasonal_forcing_function model.adaptation_functions["contact_rate"] = \ seasonal_forcing_function model.parameters["contact_rate"] = \ "contact_rate" # Stratify model by age # Coerce age breakpoint numbers into strings - all strata are represented as strings agegroup_strata = [str(s) for s in agegroup_strata] # Create parameter adjustment request for age stratifications age_based_susceptibility = params["age_based_susceptibility"] adjust_requests = { # No change, but distinction is required for later stratification by clinical status "to_infectious": {s: 1 for s in agegroup_strata}, "infect_death": {s: 1 for s in agegroup_strata}, "within_late": {s: 1 for s in agegroup_strata}, # Adjust susceptibility across age groups "contact_rate": age_based_susceptibility, } if is_importation_active: adjust_requests[ "import_secondary_rate"] = preprocess.mixing_matrix.get_total_contact_rates_by_age( static_mixing_matrix, direction="horizontal") # Distribute starting population over agegroups requested_props = { agegroup: prop for agegroup, prop in zip(agegroup_strata, normalise_sequence(total_pops)) } # We use "agegroup" instead of "age" for this model, to avoid triggering automatic demography features # (which work on the assumption that the time unit is years, so would be totally wrong) model.stratify( "agegroup", agegroup_strata, compartment_types_to_stratify=[], # Apply to all compartments requested_proportions=requested_props, mixing_matrix=static_mixing_matrix, adjustment_requests=adjust_requests, # FIXME: This seems awfully a lot like a parameter that should go in a YAML file. entry_proportions=preprocess.importation.IMPORTATION_PROPS_BY_AGE, ) model_parameters["all_stratifications"] = {"agegroup": agegroup_strata} modelled_abs_detection_proportion_imported = stratify_by_clinical( model, model_parameters, compartments) # Set time-variant importation rate if is_importation_active: import_times = params["data"]["times_imported_cases"] import_cases = params["data"]["n_imported_cases"] import_rate_func = preprocess.importation.get_importation_rate_func_as_birth_rates( import_times, import_cases, modelled_abs_detection_proportion_imported, total_pops, ) model.parameters["crude_birth_rate"] = "crude_birth_rate" model.time_variants["crude_birth_rate"] = import_rate_func # Define output connections to collate # Track compartment output connections. stratum_names = list( set([find_all_strata(x) for x in model.compartment_names])) incidence_connections = outputs.get_incidence_connections(stratum_names) progress_connections = outputs.get_progress_connections(stratum_names) model.output_connections = { **incidence_connections, **progress_connections, } # Add notifications to derived_outputs implement_importation = model.parameters["implement_importation"] model.derived_output_functions[ "notifications"] = outputs.get_calc_notifications_covid( implement_importation, modelled_abs_detection_proportion_imported, ) model.derived_output_functions[ "incidence_icu"] = outputs.calculate_incidence_icu_covid model.derived_output_functions[ "prevXlateXclinical_icuXamong"] = outputs.calculate_icu_prev model.derived_output_functions[ "hospital_occupancy"] = outputs.calculate_hospital_occupancy model.derived_output_functions[ "proportion_seropositive"] = outputs.calculate_proportion_seropositive model.death_output_categories = list_all_strata_for_mortality( model.compartment_names) model.derived_output_functions[ "years_of_life_lost"] = outputs.get_calculate_years_of_life_lost( life_expectancy_latest) return model