Exemplo n.º 1
0
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]
Exemplo n.º 2
0
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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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,
    ]
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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)
Exemplo n.º 9
0
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
Exemplo n.º 10
0
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))
Exemplo n.º 11
0
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)
Exemplo n.º 12
0
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