Example #1
0
def test_microdistancing__with_tanh_func_and_adjuster():
    params = {
        "foo": {
            "function_type": "tanh",
            "parameters": {
                "shape": -0.05,
                "inflection_time": 275,
                "lower_asymptote": 0.6,
                "upper_asymptote": 1,
            },
            "locations": LOCATIONS,
        },
        "foo_adjuster": {
            "function_type": "empiric",
            "parameters": {
                "max_effect": 0.6,
                "times": [0, 365],
                "values": [1, 100],
            },
            "locations": LOCATIONS,
        },
    }
    expect_func = tanh_based_scaleup(**params["foo"]["parameters"])
    expect_adj_func = scale_up_function([0, 365], [0.6, 60], method=4)
    params = {k: MicroDistancingFunc(**v) for k, v in params.items()}
    funcs = get_microdistancing_funcs(params=params, square_mobility_effect=False)
    assert funcs["work"](0) == 1 - expect_func(0) * expect_adj_func(0)
    assert funcs["home"](0) == 1 - expect_func(0) * expect_adj_func(0)
    assert funcs["work"](300) == 1 - expect_func(300) * expect_adj_func(300)
    assert funcs["home"](300) == 1 - expect_func(300) * expect_adj_func(300)
Example #2
0
def test_tanh_function():
    for grad in (0.1, 0.5, 1.0):
        for inflect_time in (0.0, 100.0, 500.0):
            for lower_asymptote in (0.0, 1.0):
                for upper_asymptote in (1.0, 100.0):

                    # Get the function
                    tanh_function = tanh_based_scaleup(
                        shape=grad,
                        inflection_time=inflect_time,
                        lower_asymptote=lower_asymptote,
                        upper_asymptote=upper_asymptote,
                    )

                    # Get the results
                    results = [
                        tanh_function(i_time)
                        for i_time in np.linspace(0.0, 100.0, 10)
                    ]
                    inflection_result = tanh_function(inflect_time)

                    # Make sure it makes sense
                    assert all(
                        [result >= lower_asymptote for result in results])
                    assert all(
                        [result <= upper_asymptote for result in results])
                    assert inflection_result == lower_asymptote / 2.0 + upper_asymptote / 2
Example #3
0
def get_posterior_percentiles_time_variant_profile(calibration_path,
                                                   function='detection',
                                                   burn_in=0):
    """
    :param calibration_path: string
    :param function: only 'detection' for now
    :param burn_in: integer
    """
    combined_burned_samples = combine_and_burn_samples(calibration_path,
                                                       burn_in)
    calculated_times = range(200)
    store_matrix = np.zeros(
        (len(calculated_times), combined_burned_samples.shape[0]))
    if function == 'detection':
        i = 0
        for index, row in combined_burned_samples.iterrows():
            my_func = tanh_based_scaleup(row['tv_detection_b'],
                                         row['tv_detection_c'], 0.)
            detect_vals = [
                row['prop_detected_among_symptomatic'] * my_func(t)
                for t in calculated_times
            ]
            store_matrix[:, i] = detect_vals
            i += 1
    perc = np.percentile(store_matrix, [2.5, 25, 50, 75, 97.5], axis=1)
    calculated_times = np.array([calculated_times])
    perc = np.concatenate((calculated_times, perc))
    np.savetxt(function + ".csv", perc, delimiter=',')
Example #4
0
def get_country_posterior_detection_percentiles(country_param_values):

    calculated_times = list(range(300))[30:]
    store_matrix = np.zeros(
        (len(calculated_times), len(country_param_values["time.start"])))

    for i in range(len(country_param_values["time.start"])):
        if "case_detection.lower_asymptote" in country_param_values:
            lower_asymptote = country_param_values[
                "case_detection.lower_asymptote"][i]
        else:
            lower_asymptote = 0.0
        my_func = tanh_based_scaleup(
            country_param_values["case_detection.shape"][i],
            country_param_values["case_detection.inflection_time"][i],
            lower_asymptote,
            country_param_values["case_detection.upper_asymptote"][i],
        )
        detect_vals = [my_func(float(t)) for t in calculated_times]
        store_matrix[:, i] = detect_vals

    perc = np.percentile(store_matrix, [2.5, 25, 50, 75, 97.5], axis=1)
    calculated_times = np.array([calculated_times])
    perc = np.concatenate((calculated_times, perc))

    return perc
Example #5
0
def get_country_posterior_detection_percentiles(country_param_values):

    calculated_times = range(213)
    store_matrix = np.zeros(
        (len(calculated_times), len(country_param_values['start_time'])))

    for i in range(len(country_param_values['start_time'])):
        if 'tv_detection_sigma' in country_param_values:
            sigma = country_param_values['tv_detection_sigma'][i]
        else:
            sigma = 0.
        my_func = tanh_based_scaleup(country_param_values['tv_detection_b'][i],
                                     country_param_values['tv_detection_c'][i],
                                     sigma)
        detect_vals = [
            country_param_values['prop_detected_among_symptomatic'][i] *
            my_func(float(t)) for t in calculated_times
        ]
        store_matrix[:, i] = detect_vals

    perc = np.percentile(store_matrix, [2.5, 25, 50, 75, 97.5], axis=1)
    calculated_times = np.array([calculated_times])
    perc = np.concatenate((calculated_times, perc))

    return perc
Example #6
0
def calculate_screening_rate(time_idx, model, compartment_values,
                             derived_outputs):
    screening_rate_func = tanh_based_scaleup(
        model.parameters["time_variant_tb_screening_rate"]["shape"],
        model.parameters["time_variant_tb_screening_rate"]["inflection_time"],
        model.parameters["time_variant_tb_screening_rate"]["lower_asymptote"],
        model.parameters["time_variant_tb_screening_rate"]["upper_asymptote"],
    )
    return screening_rate_func(model.times[time_idx])
Example #7
0
    def __init__(
        self,
        country_iso3: str,
        region: str,
        mixing: dict,
        npi_effectiveness_params: dict,
        google_mobility_locations: dict,
        is_periodic_intervention: bool,
        periodic_int_params: dict,
        periodic_end_time: float,
        microdistancing_params: dict,
    ):
        """Build the time variant location adjustment functions"""
        # Load mobility data
        google_mobility_values, google_mobility_days = get_mobility_data(
            country_iso3, region, BASE_DATETIME, google_mobility_locations)
        # Build mixing data timeseries
        mixing = update_mixing_data(
            mixing,
            npi_effectiveness_params,
            google_mobility_values,
            google_mobility_days,
            is_periodic_intervention,
            periodic_int_params,
            periodic_end_time,
        )
        # Build the time variant location adjustment functions from mixing timeseries
        mixing_locations = [loc for loc in LOCATIONS if loc in mixing]
        self.loc_adj_funcs = {}
        for loc_key in mixing_locations:
            loc_times = mixing[loc_key]["times"]
            loc_vals = mixing[loc_key]["values"]
            self.loc_adj_funcs[loc_key] = scale_up_function(loc_times,
                                                            loc_vals,
                                                            method=4)

        # Work out microdistancing function to be applied to all non-household locations
        if not microdistancing_params:
            self.microdistancing_function = None
        elif microdistancing_params["function_type"] == "tanh":
            self.microdistancing_function = tanh_based_scaleup(
                **microdistancing_params["parameters"])
        elif microdistancing_params["function_type"] == "empiric":
            micro_times = microdistancing_params["parameters"]["times"]
            micro_vals = microdistancing_params["parameters"]["values"]
            self.microdistancing_function = scale_up_function(micro_times,
                                                              micro_vals,
                                                              method=4)

        # Load all location-specific mixing info.
        self.matrix_components = {}
        for sheet_type in ["all_locations"] + LOCATIONS:
            # Loads a 16x16 ndarray
            self.matrix_components[sheet_type] = get_country_mixing_matrix(
                sheet_type, country_iso3)
Example #8
0
def get_microdist_func_component(func_params: MicroDistancingFunc):
    """
    Get one function of time using the standard parameter request structure for any microdistancing function or
    adjustment to a microdistancing function.
    """
    if func_params.function_type == "tanh":
        return tanh_based_scaleup(**func_params.parameters.dict())
    elif func_params.function_type == "empiric":
        micro_times = func_params.parameters.times
        multiplier = func_params.parameters.max_effect
        micro_vals = [
            multiplier * value for value in func_params.parameters.values
        ]
        return scale_up_function(micro_times, micro_vals, method=4)
    elif func_params.function_type == "constant":
        return lambda time: func_params.parameters.effect
Example #9
0
def test_microdistancing__with_tanh_func_and_square_mobility_effect():
    params = {
        "foo": {
            "function_type": "tanh",
            "parameters": {
                "shape": -0.05,
                "inflection_time": 275,
                "lower_asymptote": 0.6,
                "upper_asymptote": 1,
            },
            "locations": LOCATIONS,
        }
    }

    expect_func = tanh_based_scaleup(**params["foo"]["parameters"])
    params = {k: MicroDistancingFunc(**v) for k, v in params.items()}
    funcs = get_microdistancing_funcs(params=params, square_mobility_effect=True)
    assert funcs["work"](0) == (1 - expect_func(0)) ** 2
    assert funcs["home"](0) == (1 - expect_func(0)) ** 2
    assert funcs["work"](300) == (1 - expect_func(300)) ** 2
    assert funcs["home"](300) == (1 - expect_func(300)) ** 2
Example #10
0
def edit_adjustments_for_diabetes(
    model,
    adjustments,
    age_breakpoints,
    prop_diabetes,
    rr_progression_diabetes,
    future_diabetes_multiplier,
):
    diabetes_scale_up = tanh_based_scaleup(shape=0.05,
                                           inflection_time=1980,
                                           lower_asymptote=0.0,
                                           upper_asymptote=1.0)
    future_diabetes_trend = make_linear_curve(x_0=2020,
                                              x_1=2050,
                                              y_0=1,
                                              y_1=future_diabetes_multiplier)

    def combined_diabetes_scale_up(t):
        multiplier = 1.0
        if t > 2020:
            multiplier = future_diabetes_trend(t)
        return multiplier * diabetes_scale_up(t)

    for i, age_breakpoint in enumerate(age_breakpoints):
        for stage in ["early", "late"]:
            param_name = stage + "_activation_rate"
            stratified_param_name = param_name + "Xage_" + str(age_breakpoint)
            function_name = stratified_param_name + "_func"
            unadjusted_progression_rate = adjustments[param_name][str(
                age_breakpoint)]
            adjustments[param_name][str(age_breakpoint)] = function_name
            model.time_variants[
                function_name] = make_age_diabetes_scaleup_func(
                    prop_diabetes[age_breakpoint],
                    combined_diabetes_scale_up,
                    rr_progression_diabetes,
                    unadjusted_progression_rate,
                )
            model.parameters[stratified_param_name] = stratified_param_name
    return adjustments
def build_detected_proportion_func(
    agegroup_strata: List[str],
    country: Country,
    pop: Population,
    testing: TestingToDetection,
    case_detection: CaseDetection,
):
    """
    Returns a time varying function that gives us the proportion of cases detected.
    """
    if testing is not None:
        # More empiric approach based on per capita testing rates
        assumed_tests_parameter = testing.assumed_tests_parameter
        assumed_cdr_parameter = testing.assumed_cdr_parameter
        smoothing_period = testing.smoothing_period

        testing_pop, testing_region = get_testing_pop(agegroup_strata, country,
                                                      pop)
        detected_proportion = find_cdr_function_from_test_data(
            assumed_tests_parameter,
            assumed_cdr_parameter,
            smoothing_period,
            country.iso3,
            testing_pop,
            subregion=testing_region,
        )
    else:
        # Approach based on a hyperbolic tan function
        detected_proportion = tanh_based_scaleup(
            case_detection.shape,
            case_detection.inflection_time,
            case_detection.lower_asymptote,
            case_detection.upper_asymptote,
        )

    return detected_proportion
Example #12
0
def process_unstratified_parameter_values(params, implement_acf, implement_ltbi_screening):
    """
    This function calculates some unstratified parameter values for parameters that need pre-processing. This usually
    involves combining multiple input parameters to determine a model parameter
    :return:
    """

    # Set unstratified detection flow parameter
    if "organ" in params["stratify_by"]:
        params["detection_rate"] = 1
        detection_rate_func = None
    else:
        screening_rate_func = tanh_based_scaleup(
            params["time_variant_tb_screening_rate"]["shape"],
            params["time_variant_tb_screening_rate"]["inflection_time"],
            params["time_variant_tb_screening_rate"]["lower_asymptote"],
            params["time_variant_tb_screening_rate"]["upper_asymptote"],
        )

        def detection_rate_func(t):
            return screening_rate_func(t) * params["passive_screening_sensitivity"]["unstratified"]

        params["detection_rate"] = "detection_rate"

    # Set unstratified treatment-outcome-related parameters
    if (
        "age" in params["stratify_by"]
    ):  # relapse and treatment death need to be adjusted by age later
        params["treatment_recovery_rate"] = 1.0
        params["treatment_death_rate"] = 1.0
        params["relapse_rate"] = 1.0
        treatment_recovery_func = None
        treatment_death_func = None
        relapse_func = None
    else:
        time_variant_tsr = scale_up_function(
            list(params["time_variant_tsr"].keys()),
            list(params["time_variant_tsr"].values()),
            method=4,
        )

        def treatment_recovery_func(t):
            return max(
                1 / params["treatment_duration"],
                params["universal_death_rate"]
                / params["prop_death_among_negative_tx_outcome"]
                * (1.0 / (1.0 - time_variant_tsr(t)) - 1.0),
            )

        def treatment_death_func(t):
            return (
                params["prop_death_among_negative_tx_outcome"]
                * treatment_recovery_func(t)
                * (1.0 - time_variant_tsr(t))
                / time_variant_tsr(t)
                - params["universal_death_rate"]
            )

        def relapse_func(t):
            return (treatment_death_func(t) + params["universal_death_rate"]) * (
                1.0 / params["prop_death_among_negative_tx_outcome"] - 1.0
            )

        params["treatment_recovery_rate"] = "treatment_recovery_rate"
        params["treatment_death_rate"] = "treatment_death_rate"
        params["relapse_rate"] = "relapse_rate"

    # adjust late reactivation parameters using multiplier
    for latency_stage in ["early", "late"]:
        param_name = f"{latency_stage}_activation_rate"
        for key in params["age_specific_latency"][param_name]:
            params["age_specific_latency"][param_name][key] *= params["progression_multiplier"]
    # load unstratified latency parameters
    params = get_unstratified_parameter_values(params)

    # set reinfection contact rate parameters
    for state in ["latent", "recovered"]:
        params["contact_rate_from_" + state] = (
            params["contact_rate"] * params["rr_infection_" + state]
        )

    # assign unstratified parameter values to infection death and self-recovery processes
    for param_name in ["infect_death_rate", "self_recovery_rate"]:
        params[param_name] = params[param_name + "_dict"]["unstratified"]

    # if age-stratification is used, the baseline mortality rate is set to 1 so it can get multiplied by a time-variant
    if "age" in params["stratify_by"]:
        params["universal_death_rate"] = 1.0

    # PT in household contacts
    contact_rate_functions = {}
    if params["hh_contacts_pt"]:
        scaleup_screening_prop = scale_up_function(
            x=[params["hh_contacts_pt"]["start_time"], params["hh_contacts_pt"]["start_time"] + 1],
            y=[0, params["hh_contacts_pt"]["prop_hh_contacts_screened"]],
            method=4,
        )

        def make_contact_rate_func(raw_contact_rate_value):
            def contact_rate_func(t):
                rel_reduction = (
                    params["hh_contacts_pt"]["prop_smearpos_among_prev_tb"]
                    * params["hh_contacts_pt"]["prop_hh_transmission"]
                    * scaleup_screening_prop(t)
                    * params["ltbi_screening_sensitivity"]
                    * params["hh_contacts_pt"]["prop_pt_completion"]
                )
                return raw_contact_rate_value * (1 - rel_reduction)

            return contact_rate_func

        for suffix in ["", "_from_latent", "_from_recovered"]:
            param_name = f"contact_rate{suffix}"
            raw_value = params[param_name]
            params[param_name] = param_name
            contact_rate_functions[param_name] = make_contact_rate_func(raw_value)

    # ACF flow parameter
    acf_detection_func = None
    if implement_acf:
        if (
            len(params["time_variant_acf"]) == 1
            and params["time_variant_acf"][0]["stratum_filter"] is None
        ):
            # universal ACF is applied
            acf_detection_func = scale_up_function(
                list(params["time_variant_acf"][0]["time_variant_screening_rate"].keys()),
                [
                    v * params["acf_screening_sensitivity"]
                    for v in list(
                        params["time_variant_acf"][0]["time_variant_screening_rate"].values()
                    )
                ],
                method=4,
            )
            params["acf_detection_rate"] = "acf_detection_rate"
        else:
            params["acf_detection_rate"] = 1.0

    # Preventive treatment flow parameters
    preventive_treatment_func = None
    if implement_ltbi_screening:
        if (
            len(params["time_variant_ltbi_screening"]) == 1
            and params["time_variant_ltbi_screening"][0]["stratum_filter"] is None
        ):
            # universal LTBI screening is applied
            preventive_treatment_func = scale_up_function(
                list(
                    params["time_variant_ltbi_screening"][0]["time_variant_screening_rate"].keys()
                ),
                [
                    v * params["ltbi_screening_sensitivity" * params["pt_efficacy"]]
                    for v in list(
                        params["time_variant_ltbi_screening"][0][
                            "time_variant_screening_rate"
                        ].values()
                    )
                ],
                method=4,
            )
            params["preventive_treatment_rate"] = "preventive_treatment_rate"
        else:
            params["preventive_treatment_rate"] = 1.0

    return (
        params,
        treatment_recovery_func,
        treatment_death_func,
        relapse_func,
        detection_rate_func,
        acf_detection_func,
        preventive_treatment_func,
        contact_rate_functions,
    )
Example #13
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
Example #14
0
def stratify_by_organ(model, params):

    compartments_to_stratify = [
        Compartment.INFECTIOUS,
        Compartment.ON_TREATMENT,
    ]
    organ_strata = [
        OrganStratum.SMEAR_POSITIVE,
        OrganStratum.SMEAR_NEGATIVE,
        OrganStratum.EXTRAPULMONARY,
    ]

    # Define infectiousness adjustment by organ status
    strata_infectiousness = {}
    for stratum in organ_strata:
        if stratum + "_infect_multiplier" in params:
            strata_infectiousness[stratum] = params[stratum +
                                                    "_infect_multiplier"]

    # define differential natural history by organ status
    flow_adjustments = {}
    for param_name in ["infect_death_rate", "self_recovery_rate"]:
        stratified_param_names = get_stratified_param_names(
            param_name, model.stratifications)
        for stratified_param_name in stratified_param_names:
            flow_adjustments[stratified_param_name] = {}
            for organ_stratum in organ_strata:
                organ_stratum_ = (organ_stratum if
                                  organ_stratum != OrganStratum.EXTRAPULMONARY
                                  else OrganStratum.SMEAR_NEGATIVE)
                flow_adjustments[stratified_param_name][
                    organ_stratum + "W"] = params[param_name +
                                                  "_dict"][organ_stratum_]

    # define differential detection rates by organ status
    screening_rate_func = tanh_based_scaleup(
        params["time_variant_tb_screening_rate"]["shape"],
        params["time_variant_tb_screening_rate"]["inflection_time"],
        params["time_variant_tb_screening_rate"]["lower_asymptote"],
        params["time_variant_tb_screening_rate"]["upper_asymptote"],
    )
    if params["awareness_raising"]:
        awaireness_linear_scaleup = make_linear_curve(
            x_0=params["awareness_raising"]["scale_up_range"][0],
            x_1=params["awareness_raising"]["scale_up_range"][1],
            y_0=1,
            y_1=params["awareness_raising"]["relative_screening_rate"],
        )

        def awaireness_multiplier(t):
            if t <= params["awareness_raising"]["scale_up_range"][0]:
                return 1.0
            elif t >= params["awareness_raising"]["scale_up_range"][1]:
                return params["awareness_raising"]["relative_screening_rate"]
            else:
                return awaireness_linear_scaleup(t)

    else:
        awaireness_multiplier = lambda t: 1.0

    combined_screening_rate_func = lambda t: screening_rate_func(
        t) * awaireness_multiplier(t)

    stratified_param_names = get_stratified_param_names(
        "detection_rate", model.stratifications)
    for stratified_param_name in stratified_param_names:
        flow_adjustments[stratified_param_name] = {}
        for organ_stratum in organ_strata:
            flow_adjustments[stratified_param_name][organ_stratum] = (
                stratified_param_name + "_" + organ_stratum)
            model.time_variants[stratified_param_name + "_" +
                                organ_stratum] = make_detection_func(
                                    organ_stratum, params,
                                    combined_screening_rate_func)
            model.parameters[stratified_param_name + "_" +
                             organ_stratum] = (stratified_param_name + "_" +
                                               organ_stratum)

    # Adjust the progression rates by organ using the requested incidence proportions
    splitting_proportions = {
        "smear_positive":
        params["incidence_props_pulmonary"] *
        params["incidence_props_smear_positive_among_pulmonary"],
        "smear_negative":
        params["incidence_props_pulmonary"] *
        (1.0 - params["incidence_props_smear_positive_among_pulmonary"]),
        "extrapulmonary":
        1.0 - params["incidence_props_pulmonary"],
    }
    for stage in ["early", "late"]:
        param_stem = stage + "_activation_rate"
        stratified_param_names = get_stratified_param_names(
            param_stem, model.stratifications)
        for stratified_param_name in stratified_param_names:
            flow_adjustments[stratified_param_name] = splitting_proportions

    # trigger model stratification
    model.stratify(
        "organ",
        organ_strata,
        compartments_to_stratify,
        infectiousness_adjustments=strata_infectiousness,
        flow_adjustments=flow_adjustments,
    )
Example #15
0
def stratify_by_clinical(_covid_model, model_parameters, compartments):
    """
    Stratify the infectious compartments of the covid model (not including the pre-symptomatic compartments, which are
    actually infectious)
    """
    for i_age, h_prop in enumerate(model_parameters['hospital_props']):
        if h_prop > model_parameters['prop_detected_among_symptomatic']:
            print("Warning: Hospital proportions had to be reduced for age-group " + str(i_age))
            model_parameters['hospital_props'][i_age] = model_parameters['prop_detected_among_symptomatic']

    # Define stratification
    strata_to_implement = \
        model_parameters["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)
    ]

    # Find unadjusted parameters
    model_parameters.update(get_raw_clinical_props(model_parameters))

    # Find the absolute progression proportions from the requested splits
    abs_props = split_prop_into_two_subprops([1.0] * 16, "", model_parameters["raw_sympt"], "sympt")
    abs_props.update(
        split_prop_into_two_subprops(abs_props["sympt"], "sympt", model_parameters["raw_hospital"], "hospital")
    )
    abs_props.update(
        split_prop_into_two_subprops(abs_props["hospital"], "hospital", [model_parameters["icu_prop"]] * 16, "icu")
    )

    # Find the absolute proportion dying in hospital and in ICU
    abs_props.update(find_abs_death_props(model_parameters, abs_props))

    # CFR for non-ICU 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)
    fixed_prop_strata = ["non_sympt"]
    stratification_adjustments = adjust_upstream_stratified_parameter(
        "to_infectious",
        fixed_prop_strata,
        "agegroup",
        model_parameters["all_stratifications"]["agegroup"],
        [abs_props[stratum] for stratum in fixed_prop_strata],
    )

    # Set time-variant proportion of sympt_isolate among all symptomatics
    # create a scale-up function converging to 1
    scale_up_multiplier = tanh_based_scaleup(model_parameters['tv_detection_b'],
                                             model_parameters['tv_detection_c'],
                                             model_parameters['tv_detection_sigma'])
    # use the input parameter 'prop_detected_among_symptomatic', specifying the maximum prop of isolates among all sympt
    _tv_prop_detect_among_sympt = lambda t: model_parameters['prop_detected_among_symptomatic'] * scale_up_multiplier(t)

    # Set isolation rates as absolute proportions
    _covid_model, stratification_adjustments = \
        set_isolation_props(_covid_model, model_parameters, abs_props, stratification_adjustments,
                            _tv_prop_detect_among_sympt)

    # 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"],
            model_parameters["n_compartment_repeats"][Compartment.LATE_INFECTIOUS],
            [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
    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
    _covid_model, stratification_adjustments, strata_infectiousness = \
        adjust_infectiousness(_covid_model, model_parameters, strata_to_implement, stratification_adjustments)

    # work out time-variant clinical proportions for imported cases accounting for quarantine
    if model_parameters['implement_importation'] and model_parameters['imported_cases_explict']:
        rep_age_group = '35'  # the clinical split will be defined according to this representative age-group
        tvs = _covid_model.time_variants  # to reduce verbosity

        quarantine_scale_up = scale_up_function(model_parameters['traveller_quarantine']['times'],
                                                model_parameters['traveller_quarantine']['values'],
                                                method=4
                                                )

        tv_prop_imported_non_sympt =\
            lambda t: stratification_adjustments['to_infectiousXagegroup_' + rep_age_group]['non_sympt'] *\
                      (1. - 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. - 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'])

        tv_prop_imported_hospital_non_icu = lambda t: \
            tvs[stratification_adjustments['to_infectiousXagegroup_' + rep_age_group]['hospital_non_icu']](t)

        tv_prop_imported_icu = lambda t: \
            tvs[stratification_adjustments['to_infectiousXagegroup_' + rep_age_group]['icu']](t)

        _covid_model.time_variants['tv_prop_imported_non_sympt'] = tv_prop_imported_non_sympt
        _covid_model.time_variants['tv_prop_imported_sympt_non_hospital'] = tv_prop_imported_sympt_non_hospital
        _covid_model.time_variants['tv_prop_imported_sympt_isolate'] = tv_prop_imported_sympt_isolate
        _covid_model.time_variants['tv_prop_imported_hospital_non_icu'] = tv_prop_imported_hospital_non_icu
        _covid_model.time_variants['tv_prop_imported_icu'] = tv_prop_imported_icu

        importation_props_by_clinical = {}
        for stratum in strata_to_implement:
            importation_props_by_clinical[stratum] = "tv_prop_imported_" + stratum
    else:
        importation_props_by_clinical = {}

    # Stratify the model using the SUMMER stratification function
    _covid_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 _covid_model, model_parameters