From eac16bff8d92da2270233504d530384979886ed5 Mon Sep 17 00:00:00 2001 From: Jonathan Bloedow Date: Wed, 22 Nov 2023 11:54:26 -0800 Subject: [PATCH] Vax dedup (#26) * Added support for intervention replacement. Requires new version of model that not ready yet. * Add sft tests and update unittests and integration tests (#23) * Add broadcast co_event and doc for intervention * Add path to manifest.py so we can run test at any dir * Added README.md for tests * Add unittests coverage test * Add coverage_check.py * Change coverage test file name to get_unittest_coverage.py * Point setup to 2018 req file. Adding new examples for Pakistan provinces and various coverage regimes. * Updated to new model in req's. * Combine deduplication model should set _D_A_D to 0. * deduplication policy will not have a default. * Upgrade hard version dep of model to 0.0.5. --------- Co-authored-by: Jonathan Bloedow Co-authored-by: Sharon Chen <38014764+shchen-idmod@users.noreply.github.com> Co-authored-by: bamboouser-IDM --- .../interventions/typhoid_vaccine.py | 15 +++++- examples/future_campaign/multi_sweep.py | 46 +++++++++++++------ examples/future_campaign/requirements.txt | 2 +- requirements_2018.txt | 2 +- 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/emodpy_typhoid/interventions/typhoid_vaccine.py b/emodpy_typhoid/interventions/typhoid_vaccine.py index 75028c7..b9d9642 100644 --- a/emodpy_typhoid/interventions/typhoid_vaccine.py +++ b/emodpy_typhoid/interventions/typhoid_vaccine.py @@ -26,7 +26,6 @@ def new_intervention( camp, efficacy=0.82, mode="Shedding", constant_period=0, d decay_constant (float, optional): The decay time constant for the waning effect. Default is 6935.0. expected_expiration (float, optional): The mean duration before efficacy becomes 0. If this is set to non-zero value, the constant_period and decay_constant are ignored. These are two different modes of waning. - Returns: TyphoidVaccine: A fully configured instance of the TyphoidVaccine intervention with the specified parameters. """ @@ -36,14 +35,16 @@ def new_intervention( camp, efficacy=0.82, mode="Shedding", constant_period=0, d intervention.Mode = mode intervention.Changing_Effect = _get_waning( constant_period=constant_period, decay_constant=decay_constant, expected_expiration=expected_expiration ) intervention.Changing_Effect.Initial_Effect = efficacy + return intervention -def new_vax( camp, efficacy=0.82, mode="Acquisition", constant_period=0, decay_constant=0, expected_expiration=0 ): +def new_vax( camp, deduplication_policy, efficacy=0.82, mode="Acquisition", constant_period=0, decay_constant=0, expected_expiration=0 ): """ Create a new 'SimpleVaccine' intervention with specified parameters. If you use this function directly, you'll need to distribute the intervention with a function like ScheduledCampaignEvent or TriggeredCampaignEvent from emod_api.interventions.common. Args: camp (Camp): The camp to which the intervention is applied. + deduplication_policy (string): "replace" (default) or "combine". If giving vax to someone who already has one, based on Intervention_Name which defaults to intervention classname ("SimpleVaccine"), "replace" will purge the existing one, and "combine" will add the new one without replacement, and rely on code and configuration to calculate the combinatorix. If using "combine", make sure you _know_ the combinatorix. efficacy (float, optional): The efficacy of the Typhoid vaccine. Default is 0.82. mode (str, optional): The mode of the intervention. Default is "Acquisition" Can also be "Transmission" or "All". constant_period (float, optional): The constant period of the waning effect in days. Default is 0. @@ -64,6 +65,16 @@ def new_vax( camp, efficacy=0.82, mode="Acquisition", constant_period=0, decay_c else: raise ValueError( f"mode {mode} not recognized. Options are: 'Acquisition', 'Transmission', or 'All'." ) + # combine: DAD=0 + # replace: DAD=1, EIR=1 + # abort: DAD=1, EIR=0 -- not supported + if deduplication_policy == "replace": + intervention.Enable_Intervention_Replacement = 1 # D_A_D should be set implicitly + elif deduplication_policy == "combine": + intervention.Dont_Allow_Duplicates = 0 + else: + raise ValueError( f"deduplication_policy needs to be 'replace' or 'combine', not '{deduplication_policy}'." ) + intervention.Waning_Config = _get_waning( constant_period=constant_period, decay_constant=decay_constant, expected_expiration=expected_expiration ) intervention.Waning_Config.Initial_Effect = efficacy return intervention diff --git a/examples/future_campaign/multi_sweep.py b/examples/future_campaign/multi_sweep.py index 03a051b..3c48892 100644 --- a/examples/future_campaign/multi_sweep.py +++ b/examples/future_campaign/multi_sweep.py @@ -94,14 +94,14 @@ def add_historical_vax( camp, ria_coverage=0.75, camp_coverage=0.75, efficacy=0. ria = tv.new_routine_immunization(camp, efficacy=efficacy, constant_period=0, - expected_expiration=expiration, - #decay_constant=values['decay_constant'], + #expected_expiration=expiration, + decay_constant=expiration, start_day=year_to_days(CAMP_START_YEAR), coverage=ria_coverage) tv_iv = tv.new_vax(camp, efficacy=efficacy, - expected_expiration=expiration, - #decay_constant=values['decay_constant'], + #expected_expiration=expiration, + decay_constant=expiration, constant_period=0) notification_iv = comm.BroadcastEvent(camp, "VaccineDistributed") @@ -113,7 +113,7 @@ def add_historical_vax( camp, ria_coverage=0.75, camp_coverage=0.75, efficacy=0. Demographic_Coverage=camp_coverage, Target_Age_Min=0.75, Target_Age_Max=15 - ) + ) camp.add(one_time_campaign) #add_historical_vax( camp ) @@ -150,7 +150,10 @@ def add_vax_intervention(campaign, values, min_age=0.75, max_age=15, binary_immu import emodpy_typhoid.interventions.typhoid_vaccine as tv print(f"Telling emod-api to use {manifest.schema_file} as schema.") campaign.set_schema(manifest.schema_file) - camp_coverage = values['coverage'] + for key in values.keys(): + if 'coverage' in key: + camp_coverage = values[key] + break if binary_immunity: tv_iv = tv.new_vax(campaign, @@ -164,6 +167,12 @@ def add_vax_intervention(campaign, values, min_age=0.75, max_age=15, binary_immu constant_period=0) notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") + # NOTE: the order of interventions in Intervention_List matters. This is because multiple + # interventions are delivered using a MultiInterventionDistributor intervention and de-duplication + # operates on Intervention_Name, so that when we try to distribute this intervention 'package', + # the model looks at the name of the existing intervention, which is the vax, and the name of the + # 'package' here, which would be MultiInterventionDistributor. But there is code in emod_api which + # sets the MID name to the name of the first intervention in the list, which here will be SimpleVaccine. one_time_campaign = comm.ScheduledCampaignEvent(campaign, Start_Day=year_to_days(FWD_CAMP_START_YEAR), Intervention_List=[tv_iv, notification_iv], @@ -286,6 +295,17 @@ def get_sweep_list_duration(): sweep_list.append({'start_day_offset': c[0], 'efficacy': c[1], 'coverage': c[2], 'decay_constant': c[3]}) return sweep_list + def get_sweep_list_just_one(): + start_day_offset = [1] + vax_effs = [1] + decay = [3000] + cov = [0.75] + combinations = list(itertools.product(start_day_offset, vax_effs, cov, decay)) + sweep_list = [] + for c in combinations: + sweep_list.append({'start_day_offset': c[0], 'efficacy': c[1], 'coverage_camp': c[2], 'decay_constant': c[3]}) + return sweep_list + def get_sweep_list_from_csv(): # This is wrong. Just load rows. Code is recreating. But have to stop work for now. import pandas as pd @@ -293,8 +313,8 @@ def get_sweep_list_from_csv(): raise NotImplemented( "get_sweep_list_from_csv" ) def get_config_sweep_list(): - tac = [ 13435, 15320 ] - tel = [ 5.0, 7.0 ] + tac = [ 13435 ] + tel = [ 7.0 ] combinations = list(itertools.product(tac, tel)) sweep_list = [] for c in combinations: @@ -307,7 +327,8 @@ def get_config_sweep_list(): "Coverage": get_sweep_list_coverage, "Coverage_RIA": get_sweep_list_coverage_ria, "Coverage_Camp": get_sweep_list_coverage_camp, - "Vax_Duration": get_sweep_list_duration + "Vax_Duration": get_sweep_list_duration, + "Just_One": get_sweep_list_just_one } if sweep_choice not in sweep_selections.keys(): @@ -319,10 +340,7 @@ def get_config_sweep_list(): else: avi_age_coverage = partial( add_vax_intervention, min_age=0, max_age=125 ) - if binary_immunity: - avi_decay = partial( avi_age_coverage, binary_immunity=True ) - else: - avi_decay = partial( avi_age_coverage, binary_immunity=False ) + avi_decay = partial( avi_age_coverage, binary_immunity=binary_immunity ) builders = get_sweep_builders(sweep_list, get_config_sweep_list(), add_vax_fn=avi_decay) @@ -344,4 +362,4 @@ def get_config_sweep_list(): dtk.setup(manifest.model_dl_dir) import sys - run( sys.argv[1] if len(sys.argv)>1 else "Efficacy" ) + run( sys.argv[1] if len(sys.argv)>1 else "Just_One", binary_immunity=False ) diff --git a/examples/future_campaign/requirements.txt b/examples/future_campaign/requirements.txt index 73d4c93..5c3ff76 100644 --- a/examples/future_campaign/requirements.txt +++ b/examples/future_campaign/requirements.txt @@ -2,4 +2,4 @@ emodpy-typhoid==0.0.6.dev0 emodpy==1.22.0.dev3 emod-api==1.31.0.dev1 -emod-typhoid==0.0.4.dev5 +emod-typhoid==0.0.4.dev7 diff --git a/requirements_2018.txt b/requirements_2018.txt index 945a4ac..3946a35 100644 --- a/requirements_2018.txt +++ b/requirements_2018.txt @@ -1,3 +1,3 @@ emodpy==1.22.0.dev3 emod-api==1.31.0.dev1 -emod-typhoid==0.0.4.dev5 +emod-typhoid==0.0.5