Пример #1
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)
Пример #2
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)
Пример #3
0
def get_pop_mortality_functions(
    input_database, age_breaks, country_iso_code, emigration_value=0.0, emigration_start_time=1980.0
):
    """
    use the mortality rate data that can be obtained from find_age_specific_death_rates to fit time-variant mortality
        functions for each age group being implemented in the model

    :param age_breaks: list
        starting ages for each of the age groups
    :param country_iso_code: str
        the three digit iso3 code for the country of interest
    :param emigration_value: float
        an extra rate of migration to add on to the population-wide mortality rates to simulate net emigration
    :param emigration_start_time: float
        the point from which the additional net emigration commences
    :return: dict
        keys age breakpoints, values mortality functions
    """
    age_death_dict, data_years = find_age_specific_death_rates(
        input_database, age_breaks, country_iso_code
    )

    # add an extra fixed value after a particular time point for each mortality estimate
    for age_group in age_death_dict:
        for i_year in range(len(age_death_dict[age_group])):
            if data_years[i_year] > emigration_start_time:
                age_death_dict[age_group][i_year] += emigration_value

    # fit the curve functions to the aggregate data of mortality and net emigration
    return {
        age_group: scale_up_function(
            data_years, age_death_dict[age_group], smoothness=0.2, method=5
        )
        for age_group in age_death_dict
    }
Пример #4
0
def build_scale_up_function(values_dict, multiplier=1.0):
    return scale_up_function(
        values_dict.keys(),
        [val * multiplier for val in list(values_dict.values())],
        smoothness=0.2,
        method=5,
    )
Пример #5
0
def plot_mixing_params_over_time(mixing_params, npi_effectiveness_range):

    titles = {
        'home': 'Household',
        'work': 'Workplace',
        'school': 'School',
        'other_locations': 'Other locations'
    }
    y_labs = {'home': 'h', 'work': 'w', 'school': 's', 'other_locations': 'l'}
    date_ticks = {
        32: '1/2',
        47: '16/2',
        61: '1/3',
        76: '16/3',
        92: '1/4',
        107: '16/4',
        122: '1/5',
        137: '16/5',
        152: '1/6'
    }
    # use italics for y_labs
    for key in y_labs:
        y_labs[key] = '$\it{' + y_labs[key] + '}$(t)'

    plt.style.use("ggplot")
    for i_loc, location in enumerate([
            loc for loc in ["home", "other_locations", "school", "work"]
            if loc + "_times" in mixing_params
    ]):
        plt.figure(i_loc)
        x = list(np.linspace(0.0, 152.0, num=10000))
        y = []
        for indice_npi_effect_range in [0, 1]:
            npi_effect = {
                key: val[indice_npi_effect_range]
                for key, val in npi_effectiveness_range.items()
            }

            modified_mixing_params = apply_npi_effectiveness(
                copy.deepcopy(mixing_params), npi_effect)

            location_adjustment = scale_up_function(
                modified_mixing_params[location + "_times"],
                modified_mixing_params[location + "_values"],
                method=4)

            _y = [location_adjustment(t) for t in x]
            y.append(_y)
            plt.plot(x, _y, color='navy')

        plt.fill_between(x, y[0], y[1], color='cornflowerblue')
        plt.xlim((30., 152.))
        plt.ylim((0, 1.1))

        plt.xticks(list(date_ticks.keys()), list(date_ticks.values()))
        plt.xlabel('Date in 2020')
        plt.ylabel(y_labs[location])
        plt.title(titles[location])
        plt.savefig('mixing_adjustment_' + location + '.png')
Пример #6
0
 def __init__(self, age_mixing: Dict[str, TimeSeries]):
     """Build the time variant age adjustment functions"""
     self.adjustment_funcs = {}
     for age_idx, timeseries in age_mixing.items():
         func = scale_up_function(timeseries.times,
                                  timeseries.values,
                                  method=4)
         self.adjustment_funcs[str(age_idx)] = func
Пример #7
0
    def mixing_matrix_function(time):
        mixing_matrix = mixing_matrix_components["all_locations"]
        # Make adjustments by location
        for location in [
                loc for loc in ["home", "other_locations", "school", "work"]
                if loc + "_times" in mixing_params
        ]:
            location_adjustment = scale_up_function(
                mixing_params[location + "_times"],
                mixing_params[location + "_values"],
                method=4)
            mixing_matrix = np.add(
                mixing_matrix,
                (location_adjustment(time) - 1.0) *
                mixing_matrix_components[location],
            )

        # Make adjustments by age
        affected_age_indices = [
            age_index for age_index in range(16)
            if 'age_' + str(age_index) + '_times' in mixing_params
        ]
        complement_indices = [
            age_index for age_index in range(16)
            if age_index not in affected_age_indices
        ]

        for age_index_affected in affected_age_indices:
            age_adjustment = scale_up_function(
                mixing_params['age_' + str(age_index_affected) + "_times"],
                mixing_params['age_' + str(age_index_affected) + "_values"],
                method=4)
            for age_index_not_affected in complement_indices:
                mixing_matrix[age_index_affected,
                              age_index_not_affected] *= age_adjustment(time)
                mixing_matrix[age_index_not_affected,
                              age_index_affected] *= age_adjustment(time)

            # FIXME: patch for elderly cocooning in Victoria assuming
            for age_index_affected_bis in affected_age_indices:
                mixing_matrix[age_index_affected,
                              age_index_affected_bis] *= 1. - (
                                  1. - age_adjustment(time)) / 2.

        return mixing_matrix
Пример #8
0
def stratify_by_age(model_to_stratify, age_specific_latency_parameters,
                    age_strata):
    # FIXME: This is Marshall Islands specifc - DO NOT USE outside of Marshall Islands
    age_breakpoints = [int(i_break) for i_break in age_strata]
    age_infectiousness = get_parameter_dict_from_function(
        logistic_scaling_function(10.0), age_breakpoints)
    age_params = get_adapted_age_parameters(age_breakpoints,
                                            age_specific_latency_parameters)
    age_params.update(split_age_parameter(age_breakpoints, "contact_rate"))

    death_rates_by_age, death_rate_years = get_death_rates_by_agegroup(
        age_breakpoints, "FSM")

    # Add an extra fixed value after a particular time point for each mortality estimate,
    # to simulate emigration.
    emigration_value = 0.0075
    emigration_start_time = 1990.0
    # for age_group in age_breakpoints:
    #     for i_year in range(len(death_rate_years)):
    #         if data_years[i_year] > emigration_start_time:
    #             age_death_dict[age_group][i_year] += emigration_value

    pop_morts = {}
    for age_group in age_breakpoints:

        pop_morts[age_group] = scale_up_function(death_rate_years,
                                                 death_rates_by_age[age_group],
                                                 smoothness=0.2,
                                                 method=5)

    age_params["universal_death_rate"] = {}
    for age_break in age_breakpoints:
        model_to_stratify.time_variants["universal_death_rateXage_" +
                                        str(age_break)] = pop_morts[age_break]
        model_to_stratify.parameters[
            "universal_death_rateXage_" +
            str(age_break)] = "universal_death_rateXage_" + str(age_break)
        age_params["universal_death_rate"][
            str(age_break) +
            "W"] = "universal_death_rateXage_" + str(age_break)
    model_to_stratify.parameters["universal_death_rateX"] = 0.0

    # Add BCG effect without stratification assuming constant 100% coverage
    bcg_wane = create_sloping_step_function(15.0, 0.3, 30.0, 1.0)
    age_bcg_efficacy_dict = get_parameter_dict_from_function(
        lambda value: bcg_wane(value), age_breakpoints)
    age_params.update({"contact_rate": age_bcg_efficacy_dict})
    model_to_stratify.stratify(
        "age",
        deepcopy(age_breakpoints),
        [],
        {},
        adjustment_requests=age_params,
        infectiousness_adjustments=age_infectiousness,
        verbose=False,
    )
    return model_to_stratify
Пример #9
0
def make_intervention_adjustment_func(time_variant_screening_rate, sensitivity,
                                      prop_detected_effectively_moving):
    acf_detection_func = scale_up_function(
        list(time_variant_screening_rate.keys()),
        [
            v * sensitivity * prop_detected_effectively_moving
            for v in list(time_variant_screening_rate.values())
        ],
        method=4,
    )
    return acf_detection_func
Пример #10
0
def test_scale_up_function(verify, method, func_name, func):
    """
    Acceptance test for scale_up_function."""
    case_str = f"scale-up-func-{func_name}-{method}"
    scaled_func = scale_up_function(x=INPUT_TIMES,
                                    y=[func(t) for t in INPUT_TIMES],
                                    method=method)
    outputs = np.zeros_like(TEST_TIMES)
    for idx in range(66):
        outputs[idx] = scaled_func(TEST_TIMES[idx])

    verify(outputs, case_str)
Пример #11
0
def build_mongolia_timevariant_tsr():
    tsr = {
        1950.0: 0.0,
        1970.0: 0.2,
        1994.0: 0.6,
        2000.0: 0.85,
        2010.0: 0.87,
        2016: 0.9
    }
    return scale_up_function(tsr.keys(),
                             tsr.values(),
                             smoothness=0.2,
                             method=5)
Пример #12
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
Пример #13
0
def build_mongolia_timevariant_cdr(cdr_multiplier):
    cdr = {
        1950.0: 0.0,
        1980.0: 0.10,
        1990.0: 0.15,
        2000.0: 0.20,
        2010.0: 0.30,
        2015: 0.33,
    }
    return scale_up_function(
        cdr.keys(),
        [c * cdr_multiplier for c in list(cdr.values())],
        smoothness=0.2,
        method=5,
    )
Пример #14
0
 def __init__(self, mixing_age_adjust: dict):
     """Build the time variant age adjustment functions"""
     self.age_adjustment_functions = {}
     affected_age_indices = [
         i for i in AGE_INDICES if f"age_{i}" in mixing_age_adjust
     ]
     for age_idx in affected_age_indices:
         key = f"age_{age_idx}"
         mixing_age_adjust[key]["times"] = [
             (time_date - BASE_DATE).days
             for time_date in mixing_age_adjust[key]["times"]
         ]
         age_times = mixing_age_adjust[key]["times"]
         age_vals = mixing_age_adjust[key]["values"]
         func = scale_up_function(age_times, age_vals, method=4)
         self.age_adjustment_functions[age_idx] = func
Пример #15
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
Пример #16
0
def find_cdr_function_from_test_data(
    assumed_tests_parameter,
    assumed_cdr_parameter,
    smoothing_period,
    country_iso3,
    total_pops,
    subregion=None,
):

    # Get the appropriate testing data
    if country_iso3 == "AUS":
        test_dates, test_values = get_dhhs_testing_numbers()
    elif country_iso3 == "PHL":
        phl_region = subregion.lower() if subregion else "philippines"
        test_dates, test_values = get_phl_subregion_testing_numbers(phl_region)
    elif subregion == "Sabah":
        test_dates, test_values = get_international_testing_numbers(
            country_iso3)
    else:
        test_dates, test_values = get_international_testing_numbers(
            country_iso3)

    # Convert test numbers to per capita testing rates
    per_capita_tests = [i_tests / sum(total_pops) for i_tests in test_values]

    # Smooth the testing data if requested
    smoothed_per_capita_tests = (apply_moving_average(per_capita_tests,
                                                      smoothing_period)
                                 if smoothing_period > 1 else per_capita_tests)

    # Calculate CDRs and the resulting CDR function over time
    cdr_from_tests_func = create_cdr_function(assumed_tests_parameter,
                                              assumed_cdr_parameter)
    return scale_up_function(
        test_dates,
        [
            cdr_from_tests_func(i_test_rate)
            for i_test_rate in smoothed_per_capita_tests
        ],
        smoothness=0.2,
        method=4,
        bound_low=0.0,
    )
Пример #17
0
def test_microdistancing__with_empiric_func():
    params = {
        "foo": {
            "function_type": "empiric",
            "parameters": {
                "max_effect": 0.6,
                "times": [0, 365],
                "values": [1, 100],
            },
            "locations": LOCATIONS,
        }
    }
    expect_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)
    assert funcs["home"](0) == 1 - expect_func(0)
    assert funcs["work"](300) == 1 - expect_func(300)
    assert funcs["home"](300) == 1 - expect_func(300)
Пример #18
0
def get_mobility_funcs(
    country: Country,
    region: str,
    mixing: Dict[str, MixingLocation],
    google_mobility_locations: List[str],
    npi_effectiveness_params: Dict[str, float],
    square_mobility_effect: bool,
    smooth_google_data: bool,
) -> Dict[str, Callable[[float], float]]:
    """
    Loads google mobility data, combines it with user requested timeseries data
    and then returns a mobility function for each location.
    """
    google_mobility_values, google_mobility_days = get_mobility_data(
        country.iso3, region, BASE_DATETIME, google_mobility_locations)
    if smooth_google_data:
        for loc in google_mobility_values:
            google_mobility_values[loc] = apply_moving_average(
                google_mobility_values[loc], 7)

    # Build mixing data timeseries
    mixing = update_mixing_data(
        {k: v.dict()
         for k, v in mixing.items()},
        npi_effectiveness_params,
        google_mobility_values,
        google_mobility_days,
    )

    # Build the time variant location-specific macrodistancing adjustment functions from mixing timeseries
    mobility_funcs = {}
    for location, timeseries in mixing.items():
        if square_mobility_effect:
            loc_vals = [v**2 for v in timeseries["values"]]
        else:
            loc_vals = timeseries["values"]
        mobility_funcs[location] = scale_up_function(timeseries["times"],
                                                     loc_vals,
                                                     method=4)

    return mobility_funcs
Пример #19
0
def get_importation_rate_func_as_birth_rates(
    importation_times: List[float],
    importation_n_cases: List[float],
    detect_prop_func,
    starting_pops: list,
):
    """
    When imported cases are explicitly simulated as part of the modelled population. They enter the late_infectious
    compartment through a birth process
    """
    # inflate importation numbers to account for undetected cases (assumed to be asymptomatic or sympt non hospital)
    for i, time in enumerate(importation_times):
        importation_n_cases[i] /= detect_prop_func(time)
    # scale-up curve for importation numbers
    importation_numbers_scale_up = scale_up_function(importation_times,
                                                     importation_n_cases,
                                                     method=4,
                                                     smoothness=5.0,
                                                     bound_low=0.0)

    def recruitment_rate(t):
        return importation_numbers_scale_up(t) / sum(starting_pops)

    return recruitment_rate
Пример #20
0
def build_model(params: dict) -> StratifiedModel:
    external_params = deepcopy(params)
    model_parameters = {
        "contact_rate":
        external_params["contact_rate"],
        "contact_rate_recovered":
        external_params["contact_rate"] *
        external_params["rr_transmission_recovered"],
        "contact_rate_late_latent":
        external_params["contact_rate"] *
        external_params["rr_transmission_late_latent"],
        "recovery":
        external_params["self_recovery_rate"],
        "infect_death":
        external_params["tb_mortality_rate"],
        **external_params,
    }
    stratify_by = external_params["stratify_by"]
    derived_output_types = external_params["derived_outputs"]

    n_iter = (int(
        round((external_params["end_time"] - external_params["start_time"]) /
              external_params["time_step"])) + 1)
    integration_times = numpy.linspace(external_params["start_time"],
                                       external_params["end_time"],
                                       n_iter).tolist()

    model_parameters.update(
        change_parameter_unit(provide_aggregated_latency_parameters(),
                              365.251))

    # sequentially add groups of flows
    flows = add_standard_infection_flows([])
    flows = add_standard_latency_flows(flows)
    flows = add_standard_natural_history_flows(flows)

    # compartments
    compartments = [
        "susceptible",
        "early_latent",
        "late_latent",
        "infectious",
        "recovered",
    ]

    # define model     #replace_deaths  add_crude_birth_rate
    init_pop = {"infectious": 1000, "late_latent": 1000000}

    tb_model = StratifiedModel(
        integration_times,
        compartments,
        init_pop,
        model_parameters,
        flows,
        birth_approach="replace_deaths",
        starting_population=external_params["start_population"],
        output_connections={},
        derived_output_functions={},
        death_output_categories=((), ("age_0", )),
    )

    # add crude birth rate from un estimates
    birth_rates, years = get_crude_birth_rate("MNG")
    # Provisional patch to birth rates
    for i in range(len(birth_rates)):
        if years[i] > 1990.0:
            birth_rates[i] = 0.04

    tb_model.time_variants["crude_birth_rate"] = scale_up_function(
        years, birth_rates, smoothness=0.2, method=5)

    # add case detection process to basic model
    tb_model.add_transition_flow({
        "type": "standard_flows",
        "parameter": "case_detection",
        "origin": "infectious",
        "to": "recovered",
    })

    # Add IPT as a customised flow
    def ipt_flow_func(model, n_flow, _time, _compartment_values):
        """
        Work out the number of detected individuals from the relevant active TB compartments (with regard to the origin
        latent compartment of n_flow) multiplied with the proportion of the relevant infected contacts that is from this
        latent compartment.
        """
        dict_flows = model.transition_flows_dict
        origin_comp_name = dict_flows["origin"][n_flow]
        components_latent_comp = find_name_components(origin_comp_name)

        # find compulsory tags to be found in relevant infectious compartments
        tags = []
        for component in components_latent_comp:
            if "location_" in component or "strain_" in component:
                tags.append(component)

        # loop through all relevant infectious compartments
        total_tb_detected = 0.0
        for comp_ind in model.infectious_indices["all_strains"]:
            active_components = find_name_components(
                model.compartment_names[comp_ind])
            if all(elem in active_components for elem in tags):
                infectious_pop = _compartment_values[comp_ind]
                detection_indices = [
                    index for index, val in dict_flows["parameter"].items()
                    if "case_detection" in val
                ]
                flow_index = [
                    index for index in detection_indices
                    if dict_flows["origin"][index] ==
                    model.compartment_names[comp_ind]
                ][0]
                param_name = dict_flows["parameter"][flow_index]
                detection_tx_rate = model.get_parameter_value(
                    param_name, _time)
                tsr = mongolia_tsr(_time) + external_params[
                    "reduction_negative_tx_outcome"] * (1.0 -
                                                        mongolia_tsr(_time))
                if "strain_mdr" in model.compartment_names[comp_ind]:
                    tsr = external_params["mdr_tsr"] * external_params[
                        "prop_mdr_detected_as_mdr"]
                if tsr > 0.0:
                    total_tb_detected += infectious_pop * detection_tx_rate / tsr

        # list all latent compartments relevant to the relevant infectious population
        relevant_latent_compartments_indices = [
            i for i, comp_name in enumerate(model.compartment_names)
            if find_stem(comp_name) == "early_latent" and all(elem in comp_name
                                                              for elem in tags)
        ]

        total_relevant_latent_size = sum(
            _compartment_values[i]
            for i in relevant_latent_compartments_indices)
        current_latent_size = _compartment_values[
            model.compartment_names.index(origin_comp_name)]
        prop_of_relevant_latent = (current_latent_size /
                                   total_relevant_latent_size if
                                   total_relevant_latent_size > 0.0 else 0.0)

        return total_tb_detected * prop_of_relevant_latent

    tb_model.add_transition_flow({
        "type": "customised_flows",
        "parameter": "ipt_rate",
        "origin": "early_latent",
        "to": "recovered",
        "function": ipt_flow_func,
    })

    # add ACF flow
    tb_model.add_transition_flow({
        "type": "standard_flows",
        "parameter": "acf_rate",
        "origin": "infectious",
        "to": "recovered",
    })

    # load time-variant case detection rate
    cdr_scaleup_overall = build_mongolia_timevariant_cdr(
        external_params["cdr_multiplier"])

    # targeted TB prevalence proportions by organ
    prop_smearpos = 0.25
    prop_smearneg = 0.40
    prop_extrapul = 0.35

    # disease duration by organ
    overall_duration = prop_smearpos * 1.6 + 5.3 * (1 - prop_smearpos)
    disease_duration = {
        "smearpos": 1.6,
        "smearneg": 5.3,
        "extrapul": 5.3,
        "overall": overall_duration,
    }

    # work out the CDR for smear-positive TB
    def cdr_smearpos(time):
        # Had to replace external_params['diagnostic_sensitivity_smearneg'] with its hard-coded value .7 to avoid
        # cdr_smearpos to be affected when increasing diagnostic_sensitivity_smearneg in interventions (e.g. Xpert)

        # return (cdr_scaleup_overall(time) /
        #         (prop_smearpos + prop_smearneg * external_params['diagnostic_sensitivity_smearneg'] +
        #          prop_extrapul * external_params['diagnostic_sensitivity_extrapul']))
        return cdr_scaleup_overall(time) / (
            prop_smearpos + prop_smearneg * 0.7 +
            prop_extrapul * external_params["diagnostic_sensitivity_extrapul"])

    def cdr_smearneg(time):
        return cdr_smearpos(
            time) * external_params["diagnostic_sensitivity_smearneg"]

    def cdr_extrapul(time):
        return cdr_smearpos(
            time) * external_params["diagnostic_sensitivity_extrapul"]

    cdr_by_organ = {
        "smearpos": cdr_smearpos,
        "smearneg": cdr_smearneg,
        "extrapul": cdr_extrapul,
        "overall": cdr_scaleup_overall,
    }
    detect_rate_by_organ = {}
    for organ in ["smearpos", "smearneg", "extrapul", "overall"]:
        prop_to_rate = convert_competing_proportion_to_rate(
            1.0 / disease_duration[organ])
        detect_rate_by_organ[organ] = return_function_of_function(
            cdr_by_organ[organ], prop_to_rate)

    # load time-variant treatment success rate
    mongolia_tsr = build_mongolia_timevariant_tsr()

    # create a treatment succes rate function adjusted for treatment support intervention
    tsr_function = lambda t: mongolia_tsr(t) + external_params[
        "reduction_negative_tx_outcome"] * (1.0 - mongolia_tsr(t))

    # tb control recovery rate (detection and treatment) function set for overall if not organ-specific, smearpos otherwise
    if "organ" not in stratify_by:
        tb_control_recovery_rate = lambda t: tsr_function(
            t) * detect_rate_by_organ["overall"](t)
    else:
        tb_control_recovery_rate = lambda t: tsr_function(
            t) * detect_rate_by_organ["smearpos"](t)

    # initialise ipt_rate function assuming coverage of 1.0 before age stratification
    ipt_rate_function = (lambda t: 1.0 * external_params[
        "yield_contact_ct_tstpos_per_detected_tb"] * external_params[
            "ipt_efficacy"])

    # initialise acf_rate function
    acf_rate_function = (
        lambda t: external_params["acf_coverage"] * external_params[
            "acf_sensitivity"] *
        (mongolia_tsr(t) + external_params["reduction_negative_tx_outcome"] *
         (1.0 - mongolia_tsr(t))))

    # assign newly created functions to model parameters
    tb_model.adaptation_functions["case_detection"] = tb_control_recovery_rate
    tb_model.parameters["case_detection"] = "case_detection"

    tb_model.adaptation_functions["ipt_rate"] = ipt_rate_function
    tb_model.parameters["ipt_rate"] = "ipt_rate"

    tb_model.adaptation_functions["acf_rate"] = acf_rate_function
    tb_model.parameters["acf_rate"] = "acf_rate"

    if "strain" in stratify_by:
        mdr_adjustment = (external_params["prop_mdr_detected_as_mdr"] *
                          external_params["mdr_tsr"] / 0.9
                          )  # /.9 for last DS TSR

        tb_model.stratify(
            "strain",
            ["ds", "mdr"],
            ["early_latent", "late_latent", "infectious"],
            verbose=False,
            requested_proportions={"mdr": 0.0},
            adjustment_requests={
                "contact_rate": {
                    "ds": 1.0,
                    "mdr": 1.0
                },
                "case_detection": {
                    "mdr": mdr_adjustment
                },
                "ipt_rate": {
                    "ds": 1.0,  # external_params['ds_ipt_switch'],
                    "mdr": external_params["mdr_ipt_switch"],
                },
            },
            infectiousness_adjustments={
                "ds": 1.0,
                "mdr": external_params["mdr_infectiousness_multiplier"],
            },
        )

        tb_model.add_transition_flow({
            "type":
            "standard_flows",
            "parameter":
            "dr_amplification",
            "origin":
            "infectiousXstrain_ds",
            "to":
            "infectiousXstrain_mdr",
            "implement":
            len(tb_model.all_stratifications),
        })

        dr_amplification_rate = (
            lambda t: detect_rate_by_organ["overall"](t) *
            (1.0 - mongolia_tsr(t)) *
            (1.0 - external_params["reduction_negative_tx_outcome"]
             ) * external_params["dr_amplification_prop_among_nonsuccess"])

        tb_model.adaptation_functions[
            "dr_amplification"] = dr_amplification_rate
        tb_model.parameters["dr_amplification"] = "dr_amplification"

    if "age" in stratify_by:
        age_breakpoints = [0, 5, 15, 60]
        age_infectiousness = get_parameter_dict_from_function(
            logistic_scaling_function(10.0), age_breakpoints)
        age_params = get_adapted_age_parameters(age_breakpoints)
        age_params.update(split_age_parameter(age_breakpoints, "contact_rate"))

        # adjustment of latency parameters
        for param in ["early_progression", "late_progression"]:
            for age_break in age_breakpoints:
                if age_break > 5:
                    age_params[param][
                        str(age_break) +
                        "W"] *= external_params["adult_latency_adjustment"]

        death_rates_by_age, death_rate_years = get_death_rates_by_agegroup(
            age_breakpoints, "MNG")
        pop_morts = {}
        for age_group in age_breakpoints:
            pop_morts[age_group] = scale_up_function(
                death_rate_years,
                death_rates_by_age[age_group],
                smoothness=0.2,
                method=5)

        age_params["universal_death_rate"] = {}
        for age_break in age_breakpoints:
            tb_model.time_variants["universal_death_rateXage_" +
                                   str(age_break)] = pop_morts[age_break]
            tb_model.parameters[
                "universal_death_rateXage_" +
                str(age_break)] = "universal_death_rateXage_" + str(age_break)

            age_params["universal_death_rate"][
                str(age_break) +
                "W"] = "universal_death_rateXage_" + str(age_break)
        tb_model.parameters["universal_death_rateX"] = 0.0

        # age-specific IPT
        ipt_by_age = {"ipt_rate": {}}
        for age_break in age_breakpoints:
            ipt_by_age["ipt_rate"][str(age_break)] = external_params[
                "ipt_age_" + str(age_break) + "_ct_coverage"]
        age_params.update(ipt_by_age)

        # add BCG effect without stratification assuming constant 100% coverage
        bcg_wane = create_sloping_step_function(15.0, 0.3, 30.0, 1.0)
        age_bcg_efficacy_dict = get_parameter_dict_from_function(
            lambda value: bcg_wane(value), age_breakpoints)
        age_params.update({"contact_rate": age_bcg_efficacy_dict})

        tb_model.stratify(
            "age",
            deepcopy(age_breakpoints),
            [],
            {},
            adjustment_requests=age_params,
            infectiousness_adjustments=age_infectiousness,
            verbose=False,
        )

        # patch for IPT to overwrite parameters when ds_ipt has been turned off while we still need some coverage at baseline
        if external_params["ds_ipt_switch"] == 0.0 and external_params[
                "mdr_ipt_switch"] == 1.0:
            tb_model.parameters["ipt_rateXstrain_dsXage_0"] = 0.17
            for age_break in [5, 15, 60]:
                tb_model.parameters["ipt_rateXstrain_dsXage_" +
                                    str(age_break)] = 0.0

    if "organ" in stratify_by:
        props_smear = {
            "smearpos": external_params["prop_smearpos"],
            "smearneg": 1.0 - (external_params["prop_smearpos"] + 0.20),
            "extrapul": 0.20,
        }
        mortality_adjustments = {
            "smearpos": 1.0,
            "smearneg": 0.064,
            "extrapul": 0.064
        }
        recovery_adjustments = {
            "smearpos": 1.0,
            "smearneg": 0.56,
            "extrapul": 0.56
        }

        # workout the detection rate adjustment by organ status
        adjustment_smearneg = (detect_rate_by_organ["smearneg"](2015.0) /
                               detect_rate_by_organ["smearpos"](2015.0) if
                               detect_rate_by_organ["smearpos"](2015.0) > 0.0
                               else 1.0)
        adjustment_extrapul = (detect_rate_by_organ["extrapul"](2015.0) /
                               detect_rate_by_organ["smearpos"](2015.0) if
                               detect_rate_by_organ["smearpos"](2015.0) > 0.0
                               else 1.0)

        tb_model.stratify(
            "organ",
            ["smearpos", "smearneg", "extrapul"],
            ["infectious"],
            infectiousness_adjustments={
                "smearpos": 1.0,
                "smearneg": 0.25,
                "extrapul": 0.0,
            },
            verbose=False,
            requested_proportions=props_smear,
            adjustment_requests={
                "recovery": recovery_adjustments,
                "infect_death": mortality_adjustments,
                "case_detection": {
                    "smearpos": 1.0,
                    "smearneg": adjustment_smearneg,
                    "extrapul": adjustment_extrapul,
                },
                "early_progression": props_smear,
                "late_progression": props_smear,
            },
        )

    if "location" in stratify_by:
        props_location = {
            "rural_province": 0.48,
            "urban_nonger": 0.368,
            "urban_ger": 0.15,
            "prison": 0.002,
        }
        raw_relative_risks_loc = {"rural_province": 1.0}
        for stratum in ["urban_nonger", "urban_ger", "prison"]:
            raw_relative_risks_loc[stratum] = external_params[
                "rr_transmission_" + stratum]
        scaled_relative_risks_loc = scale_relative_risks_for_equivalence(
            props_location, raw_relative_risks_loc)

        # dummy matrix for mixing by location
        location_mixing = numpy.array([
            0.899,
            0.05,
            0.05,
            0.001,
            0.049,
            0.7,
            0.25,
            0.001,
            0.049,
            0.25,
            0.7,
            0.001,
            0.1,
            0.1,
            0.1,
            0.7,
        ]).reshape((4, 4))
        location_mixing *= 3.0  # adjusted such that heterogeneous mixing yields similar overall burden as homogeneous

        location_adjustments = {}
        for beta_type in ["", "_late_latent", "_recovered"]:
            location_adjustments["contact_rate" +
                                 beta_type] = scaled_relative_risks_loc

        location_adjustments["acf_rate"] = {}
        for stratum in [
                "rural_province", "urban_nonger", "urban_ger", "prison"
        ]:
            location_adjustments["acf_rate"][stratum] = external_params[
                "acf_" + stratum + "_switch"]

        tb_model.stratify(
            "location",
            ["rural_province", "urban_nonger", "urban_ger", "prison"],
            [],
            requested_proportions=props_location,
            verbose=False,
            entry_proportions=props_location,
            adjustment_requests=location_adjustments,
            mixing_matrix=location_mixing,
        )

    # tb_model.transition_flows.to_csv("transitions.csv")
    # tb_model.death_flows.to_csv("deaths.csv")

    # create some customised derived_outputs

    if "notifications" in derived_output_types:

        def notification_function_builder(stratum):
            """
                example of stratum: "Xage_0Xstrain_mdr"
            """
            def calculate_notifications(model, time):

                total_notifications = 0.0
                dict_flows = model.transition_flows_dict

                comp_ind = model.compartment_names.index("infectious" +
                                                         stratum)
                infectious_pop = model.compartment_values[comp_ind]
                detection_indices = [
                    index for index, val in dict_flows["parameter"].items()
                    if "case_detection" in val
                ]
                flow_index = [
                    index for index in detection_indices
                    if dict_flows["origin"][index] ==
                    model.compartment_names[comp_ind]
                ][0]
                param_name = dict_flows["parameter"][flow_index]
                detection_tx_rate = model.get_parameter_value(param_name, time)
                tsr = mongolia_tsr(time) + external_params[
                    "reduction_negative_tx_outcome"] * (1.0 -
                                                        mongolia_tsr(time))
                if "strain_mdr" in model.compartment_names[comp_ind]:
                    tsr = external_params["mdr_tsr"] * external_params[
                        "prop_mdr_detected_as_mdr"]
                if tsr > 0.0:
                    total_notifications += infectious_pop * detection_tx_rate / tsr

                return total_notifications

            return calculate_notifications

        for compartment in tb_model.compartment_names:
            if "infectious" in compartment:
                stratum = compartment.split("infectious")[1]
                tb_model.derived_output_functions[
                    "notifications" +
                    stratum] = notification_function_builder(stratum)
                # tb_model.derived_output_functions['popsize_treatment_support' + stratum] = notification_function_builder(stratum)

    if "incidence" in derived_output_types:
        # add output_connections for all stratum-specific incidence outputs
        incidence_output_conns = create_output_connections_for_incidence_by_stratum(
            tb_model.compartment_names)
        tb_model.output_connections.update(incidence_output_conns)
        # Create a 'combined incidence' derived output
        early_names = [
            k for k in incidence_output_conns.keys()
            if k.startswith("incidence_early")
        ]
        for early_name in early_names:
            rootname = early_name[15:]
            late_name = f"incidence_late{rootname}"
            combined_name = f"incidence{rootname}"

            def add_combined_incidence(model, time, e=early_name, l=late_name):
                time_idx = model.times.index(time)
                early_incidence = model.derived_outputs[e][time_idx]
                late_incidence = model.derived_outputs[l][time_idx]
                return early_incidence + late_incidence

            tb_model.derived_output_functions[
                combined_name] = add_combined_incidence

    if "mortality" in derived_output_types:
        # prepare death outputs for all strata
        tb_model.death_output_categories = list_all_strata_for_mortality(
            tb_model.compartment_names)

    ############################################
    #       population sizes for costing
    ############################################
    if "popsizes" in derived_output_types:
        # nb of detected individuals by strain:
        def detected_popsize_function_builder(tag):
            """
                example of tag: "starin_mdr" or "organ_smearpos"
            """
            def calculate_nb_detected(model, time):
                nb_treated = 0.0
                for key, value in model.derived_outputs.items():
                    if "notifications" in key and tag in key:
                        this_time_index = model.times.index(time)
                        nb_treated += value[this_time_index]
                return nb_treated

            return calculate_nb_detected

        for tag in [
                "strain_mdr",
                "strain_ds",
                "organ_smearpos",
                "organ_smearneg",
                "organ_extrapul",
        ]:
            tb_model.derived_output_functions[
                "popsizeXnb_detectedX" +
                tag] = detected_popsize_function_builder(tag)

        # ACF popsize: number of people screened
        def popsize_acf(model, time):
            if external_params["acf_coverage"] == 0.0:
                return 0.0
            pop_urban_ger = sum([
                model.compartment_values[i]
                for i, c_name in enumerate(model.compartment_names)
                if "location_urban_ger" in c_name
            ])
            return external_params["acf_coverage"] * pop_urban_ger

        tb_model.derived_output_functions[
            "popsizeXnb_screened_acf"] = popsize_acf

    return tb_model
Пример #21
0
 def time_variant_DST():
     return scale_up_function(params['dst'].keys(), params['dst'].values(), smoothness=0.2, method=5)
Пример #22
0
def build_model(params: dict) -> StratifiedModel:
    """
    Build the master function to run a tuberculosis model
    """
    validate_params(params)  # perform validation of parameter format
    check_param_values(params)  # perform validation of some parameter values

    # Define model times.
    time = params["time"]
    integration_times = get_model_times_from_inputs(round(time["start"]),
                                                    time["end"], time["step"],
                                                    time["critical_ranges"])

    # Define model compartments.
    compartments = [
        Compartment.SUSCEPTIBLE,
        Compartment.EARLY_LATENT,
        Compartment.LATE_LATENT,
        Compartment.INFECTIOUS,
        Compartment.ON_TREATMENT,
        Compartment.RECOVERED,
    ]
    infectious_comps = [
        Compartment.INFECTIOUS,
        Compartment.ON_TREATMENT,
    ]

    # Define initial conditions - 1 infectious person.
    init_conditions = {
        Compartment.INFECTIOUS: 1,
    }

    # prepare infectiousness adjustment for individuals on treatment
    treatment_infectiousness_adjustment = [{
        "comp_name":
        Compartment.ON_TREATMENT,
        "comp_strata": {},
        "value":
        params["on_treatment_infect_multiplier"],
    }]

    # Define inter-compartmental flows.
    flows = deepcopy(preprocess.flows.DEFAULT_FLOWS)

    # is ACF implemented?
    implement_acf = len(params["time_variant_acf"]) > 0
    if implement_acf:
        flows.append(preprocess.flows.ACF_FLOW)

    # is ltbi screening implemented?
    implement_ltbi_screening = len(params["time_variant_ltbi_screening"]) > 0
    if implement_ltbi_screening:
        flows += preprocess.flows.get_preventive_treatment_flows(
            params["pt_destination_compartment"])

    # Set some parameter values or parameters that require pre-processing
    (
        params,
        treatment_recovery_func,
        treatment_death_func,
        relapse_func,
        detection_rate_func,
        acf_detection_rate_func,
        preventive_treatment_func,
        contact_rate_functions,
    ) = preprocess.flows.process_unstratified_parameter_values(
        params, implement_acf, implement_ltbi_screening)

    # Create the model.
    tb_model = StratifiedModel(
        times=integration_times,
        compartment_names=compartments,
        initial_conditions=init_conditions,
        parameters=params,
        requested_flows=flows,
        infectious_compartments=infectious_comps,
        birth_approach=BirthApproach.ADD_CRUDE,
        entry_compartment=Compartment.SUSCEPTIBLE,
        starting_population=int(params["start_population_size"]),
    )

    # register acf_detection_func
    if acf_detection_rate_func is not None:
        tb_model.time_variants["acf_detection_rate"] = acf_detection_rate_func
    # register preventive_treatment_func
    if preventive_treatment_func is not None:
        tb_model.time_variants[
            "preventive_treatment_rate"] = preventive_treatment_func
    # register time-variant contact-rate functions:
    for param_name, func in contact_rate_functions.items():
        tb_model.time_variants[param_name] = func

    # Apply infectiousness adjustment for individuals on treatment
    tb_model.individual_infectiousness_adjustments = treatment_infectiousness_adjustment

    # apply age stratification
    if "age" in params["stratify_by"]:
        stratify_by_age(tb_model, params, compartments)
    else:
        # set time-variant functions for treatment death and relapse rates
        tb_model.time_variants[
            "treatment_recovery_rate"] = treatment_recovery_func
        tb_model.time_variants["treatment_death_rate"] = treatment_death_func
        tb_model.time_variants["relapse_rate"] = relapse_func

    # Load time-variant birth rates
    birth_rates, years = inputs.get_crude_birth_rate(params["iso3"])
    birth_rates = [b / 1000.0 for b in birth_rates
                   ]  # birth rates are provided / 1000 population
    tb_model.time_variants["crude_birth_rate"] = scale_up_function(
        years, birth_rates, smoothness=0.2, method=5)
    tb_model.parameters["crude_birth_rate"] = "crude_birth_rate"

    # apply user-defined stratifications
    user_defined_stratifications = [
        s for s in list(params["user_defined_stratifications"].keys())
        if s in params["stratify_by"]
    ]
    for stratification in user_defined_stratifications:
        assert "_" not in stratification, "Stratification name should not include '_'"
        stratification_details = params["user_defined_stratifications"][
            stratification]
        apply_user_defined_stratification(
            tb_model,
            compartments,
            stratification,
            stratification_details,
            implement_acf,
            implement_ltbi_screening,
        )

    if "organ" in params["stratify_by"]:
        stratify_by_organ(tb_model, params)
    else:
        tb_model.time_variants["detection_rate"] = detection_rate_func

    # Register derived output functions, which are calculations based on the model's compartment values or flows.
    # These are calculated after the model is run.
    outputs.get_all_derived_output_functions(params["calculated_outputs"],
                                             params["outputs_stratification"],
                                             tb_model)

    return tb_model
Пример #23
0
def build_model(params: dict) -> StratifiedModel:
    """
    Build the master function to run the TB model for the Republic of the Marshall Islands

    :param update_params: dict
        Any parameters that need to be updated for the current run
    :return: StratifiedModel
        The final model with all parameters and stratifications
    """

    # Define compartments and initial conditions.
    compartments = [
        Compartment.SUSCEPTIBLE,
        Compartment.EARLY_LATENT,
        Compartment.LATE_LATENT,
        Compartment.EARLY_INFECTIOUS,
        Compartment.ON_TREATMENT,
        Compartment.RECOVERED,
        # Compartment.LTBI_TREATED,
    ]
    init_pop = {Compartment.EARLY_INFECTIOUS: 10, Compartment.LATE_LATENT: 100}

    model_parameters = params

    # Update partial immunity/susceptibility parameters
    model_parameters = update_transmission_parameters(
        model_parameters,
        [
            Compartment.RECOVERED, Compartment.LATE_LATENT,
            Compartment.LTBI_TREATED
        ],
    )

    # Set integration times
    integration_times = get_model_times_from_inputs(
        model_parameters["start_time"],
        model_parameters["end_time"],
        model_parameters["time_step"],
    )

    # Sequentially add groups of flows to flows list
    flows = add_standard_infection_flows([])
    flows = add_standard_latency_flows(flows)
    flows = add_standard_natural_history_flows(flows)
    # flows = add_latency_progression(flows)
    flows = add_case_detection(flows, compartments)
    flows = add_treatment_flows(flows)
    # flows = add_acf(flows, compartments)
    # flows = add_acf_ltbi(flows)

    # Make sure incidence and notifications are tracked during integration
    out_connections = {}
    out_connections.update(
        create_request_stratified_incidence(
            model_parameters["incidence_stratification"],
            model_parameters["all_stratifications"],
        ))
    out_connections.update(
        create_request_stratified_notifications(
            model_parameters["notification_stratifications"],
            model_parameters["all_stratifications"],
        ))

    # Define model
    tb_model = StratifiedModel(
        integration_times,
        compartments,
        init_pop,
        model_parameters,
        flows,
        birth_approach="add_crude_birth_rate",
        starting_population=model_parameters["start_population"],
        output_connections=out_connections,
        death_output_categories=list_all_strata_for_mortality(compartments),
    )

    # Add crude birth rate from UN estimates (using Federated States of Micronesia as a proxy as no data for RMI)
    birth_rates, years = inputs.get_crude_birth_rate("FSM")
    tb_model.time_variants["crude_birth_rate"] = scale_up_function(
        years, birth_rates, smoothness=0.2, method=5)

    # Find raw case detection rate with multiplier, which is 1 by default, and adjust for differences by organ status
    cdr_scaleup_raw = build_scale_up_function(
        model_parameters["cdr"], model_parameters["cdr_multiplier"])
    detect_rate_by_organ = find_organ_specific_cdr(
        cdr_scaleup_raw,
        model_parameters,
        model_parameters["all_stratifications"]["organ"],
        target_organ_props=model_parameters["target_organ_props"],
    )

    # Find base case detection rate and time-variant treatment completion function
    base_detection_rate = detect_rate_by_organ[
        "smearpos" if "organ" in
        model_parameters["stratify_by"] else "overall"]
    treatment_success_rate = (
        lambda time: build_scale_up_function(model_parameters["tsr"])
        (time) / model_parameters["treatment_duration"])
    treatment_nonsuccess_rate = (
        lambda time: (1.0 - build_scale_up_function(model_parameters["tsr"])
                      (time)) / model_parameters["treatment_duration"])

    # Set acf screening rate using proportion of population reached and duration of intervention
    # acf_screening_rate = -numpy.log(1 - 0.9) / 0.5
    # acf_rate_over_time = progressive_step_function_maker(
    #     2018.2, 2018.7, acf_screening_rate, scaling_time_fraction=0.3
    # )

    # Initialise acf_rate function
    # acf_rate_function = (
    #     lambda t: model_parameters["acf_coverage"]
    #               * (acf_rate_over_time(t))
    #               * model_parameters["acf_sensitivity"]
    # )
    # acf_ltbi_rate_function = (
    #     lambda t: model_parameters["acf_coverage"]
    #               * (acf_rate_over_time(t))
    #               * model_parameters["acf_ltbi_sensitivity"]
    #               * model_parameters["acf_ltbi_efficacy"]
    # )

    # Assign newly created functions to model parameters
    add_time_variant_parameter_to_model(
        tb_model,
        "case_detection",
        base_detection_rate,
        len(model_parameters["stratify_by"]),
    )
    add_time_variant_parameter_to_model(
        tb_model,
        "treatment_success",
        treatment_success_rate,
        len(model_parameters["stratify_by"]),
    )
    add_time_variant_parameter_to_model(
        tb_model,
        "treatment_nonsuccess",
        treatment_nonsuccess_rate,
        len(model_parameters["stratify_by"]),
    )
    # add_time_variant_parameter_to_model(
    #     tb_model, 'acf_rate', acf_rate_function, len(model_parameters['stratify_by']))
    # add_time_variant_parameter_to_model(
    #     tb_model, 'acf_ltbi_rate', acf_ltbi_rate_function, len(model_parameters['stratify_by']))

    # Stratification processes
    if "age" in model_parameters["stratify_by"]:
        age_specific_latency_parameters = manually_create_age_specific_latency_parameters(
            model_parameters)
        tb_model = stratify_by_age(
            tb_model,
            age_specific_latency_parameters,
            model_parameters["all_stratifications"]["age"],
        )
    if "diabetes" in model_parameters["stratify_by"]:
        tb_model = stratify_by_diabetes(
            tb_model,
            model_parameters,
            model_parameters["all_stratifications"]["diabetes"],
            model_parameters["diabetes_target_props"],
            age_specific_prevalence=False,
        )
    if "organ" in model_parameters["stratify_by"]:
        tb_model = stratify_by_organ(
            tb_model,
            model_parameters,
            detect_rate_by_organ,
            model_parameters["all_stratifications"]["organ"],
        )
    if "location" in model_parameters["stratify_by"]:
        tb_model = stratify_by_location(
            tb_model,
            model_parameters,
            model_parameters["all_stratifications"]["location"],
        )

    # Capture reported prevalence in Majuro assuming over-reporting (needed for calibration)
    def calculate_reported_majuro_prevalence(model, time):
        true_prev = 0.0
        pop_majuro = 0.0
        for i, compartment in enumerate(model.compartment_names):
            if "majuro" in compartment:
                pop_majuro += model.compartment_values[i]
                if "infectious" in compartment:
                    true_prev += model.compartment_values[i]
        return (
            1.0e5 * true_prev / pop_majuro *
            (1.0 + model_parameters["over_reporting_prevalence_proportion"]))

    tb_model.derived_output_functions.update(
        {"reported_majuro_prevalence": calculate_reported_majuro_prevalence})

    return tb_model
Пример #24
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
Пример #25
0
def stratify_by_age(model, params, compartments):

    flow_adjustments = {}

    # age-specific all-causes mortality rate
    death_rates_by_age, death_rate_years = get_death_rates_by_agegroup(
        params["age_breakpoints"], params["iso3"])
    flow_adjustments["universal_death_rate"] = {}
    for age_group in params["age_breakpoints"]:
        name = "universal_death_rate_" + str(age_group)
        flow_adjustments["universal_death_rate"][str(age_group)] = name
        model.time_variants[name] = scale_up_function(
            death_rate_years,
            death_rates_by_age[age_group],
            smoothness=0.2,
            method=5)
        model.parameters[name] = name

    # age-specific latency progression rates
    flow_adjustments.update(
        get_adapted_age_parameters(params["age_breakpoints"],
                                   params["age_specific_latency"]))
    if params["inflate_reactivation_for_diabetes"]:
        flow_adjustments = edit_adjustments_for_diabetes(
            model,
            flow_adjustments,
            params["age_breakpoints"],
            params["extra_params"]["prop_diabetes"],
            params["extra_params"]["rr_progression_diabetes"],
            params["extra_params"]["future_diabetes_multiplier"],
        )

    # age-specific infectiousness
    strata_infectiousness = calculate_age_specific_infectiousness(
        params["age_breakpoints"], params["age_infectiousness_switch"])

    # age-specific treatment recovery, relapse and treatment death rates
    time_variant_tsr = scale_up_function(
        list(params["time_variant_tsr"].keys()),
        list(params["time_variant_tsr"].values()),
        method=4,
    )
    factory_functions = {
        "treatment_recovery_rate": make_treatment_recovery_func,
        "treatment_death_rate": make_treatment_death_func,
        "relapse_rate": make_relapse_rate_func,
    }
    for param_stem in factory_functions:
        flow_adjustments[param_stem] = {}
        for age_group in params["age_breakpoints"]:
            flow_adjustments[param_stem][str(
                age_group)] = param_stem + "_" + str(age_group)

            model.time_variants[
                param_stem + "_" +
                str(age_group)] = factory_functions[param_stem](
                    age_group, model, params, time_variant_tsr)
            model.parameters[
                param_stem + "_" +
                str(age_group)] = param_stem + "_" + str(age_group)

    # get mixing matrix
    mixing_matrix = get_mixing_matrix_specific_agegroups(
        params["iso3"], params["age_breakpoints"])

    # add BCG effect without stratifying for BCG
    bcg_wane = create_sloping_step_function(15.0, 0.3, 30.0, 1.0)
    bcg_susceptibility_multilier_dict = get_parameter_dict_from_function(
        lambda value: bcg_wane(value), params["age_breakpoints"])
    bcg_coverage_func = scale_up_function(
        list(params["time_variant_bcg_perc"].keys()),
        list(params["time_variant_bcg_perc"].values()),
        method=5,
        bound_low=0,
        bound_up=100,
        smoothness=1.5,
    )
    for agegroup, multiplier in bcg_susceptibility_multilier_dict.items():
        if multiplier < 1.0:
            average_age = get_average_age_for_bcg(agegroup,
                                                  params["age_breakpoints"])
            name = "contact_rate_" + agegroup
            bcg_susceptibility_multilier_dict[agegroup] = name
            model.time_variants[name] = make_bcg_multiplier_func(
                bcg_coverage_func, multiplier, average_age)
            model.parameters[name] = name
    flow_adjustments.update(
        {"contact_rate": bcg_susceptibility_multilier_dict})

    # trigger model stratification
    model.stratify(
        "age",
        params["age_breakpoints"],
        compartments,
        infectiousness_adjustments=strata_infectiousness,
        flow_adjustments=flow_adjustments,
        mixing_matrix=mixing_matrix,
    )
Пример #26
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,
    )
Пример #27
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