diff --git a/.bump_version.cfg b/.bump_version.cfg index ef4a6a9..2c464c5 100644 --- a/.bump_version.cfg +++ b/.bump_version.cfg @@ -1,5 +1,5 @@ [bumpversion] -__version__ = '0.0.4' +__version__ = '0.1.0' commit = True tag = False diff --git a/.github/workflows/run_test.yml b/.github/workflows/run_test.yml index 30faca8..14887a1 100644 --- a/.github/workflows/run_test.yml +++ b/.github/workflows/run_test.yml @@ -31,9 +31,9 @@ jobs: - name: install test dependencies run: | pip3 install unittest-xml-reporting pytest -# - name: Install idm-test package -# run: | -# pip install idm-test>=0.0.13 --extra-index-url https://packages.idmod.org/api/pypi/pypi-production/simple + - name: Install idm-test package + run: | + pip install idm-test>=0.1.2 --extra-index-url https://packages.idmod.org/api/pypi/pypi-production/simple - name: Login to comps2 run: | python ./.dev_scripts/create_auth_token_args.py --username "${{ secrets.COMPS_USER }}" --password "${{ secrets.COMPS_PASSWORD }}" @@ -56,6 +56,17 @@ jobs: if: failure() uses: actions/upload-artifact@v3 with: - name: unittest_results + name: workflow_results + path: | + **/test_results.xml + - name: run sft tests + run: | + cd tests/sft_tests + python run_all_sft_tests.py + - name: Upload workflow tests results to artifactory + if: failure() + uses: actions/upload-artifact@v3 + with: + name: sft_results path: | **/test_results.xml \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index 35a638e..7570ecd 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -88,6 +88,19 @@ podTemplate( build_ok = false echo e.toString() } + try{ + stage('SFT Test') { + echo "Running SFT Tests" + dir('tests/sft_tests') { + sh 'pip3 install idm-test>=0.1.2 --index-url=https://packages.idmod.org/api/pypi/pypi-production/simple' + sh 'python3 run_all_sft_tests.py' + junit '**/test_results.xml' + } + } + } catch(e) { + build_ok = false + echo e.toString() + } // stage('Run Examples') { // echo "Running examples" diff --git a/docs/conf.py b/docs/conf.py index df7d08e..1eb025b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -291,7 +291,7 @@ search_project = os.environ["READTHEDOCS_PROJECT"] search_version = os.environ["READTHEDOCS_VERSION"] - rtd_sphinx_search_default_filter = f"subprojects:{search_project_parent}/{search_version}" + rtd_sphinx_search_default_filter = f"subprojects:{search_project}/{search_version}" rtd_sphinx_search_filters = { "Search this project": f"project:{search_project}/{search_version}", diff --git a/emodpy_typhoid/interventions/typhoid_vaccine.py b/emodpy_typhoid/interventions/typhoid_vaccine.py index f8499c1..91a9cb2 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,9 +35,10 @@ 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, efficacy=0.82, mode="Acquisition", constant_period=0, decay_constant=0, expected_expiration=0, deduplication_policy="replace" ): """ 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. @@ -49,6 +49,7 @@ def new_vax( camp, efficacy=0.82, mode="Acquisition", constant_period=0, decay_c constant_period (float, optional): The constant period of the waning effect in days. Default is 0. 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. + 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. Returns: SimpleVaccine: A fully configured instance of the SimpleVaccine intervention with the specified parameters. @@ -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 @@ -95,14 +106,18 @@ def new_triggered_intervention( coverage (float, optional): Demographic coverage of the intervention. Default is 1.0. node_ids (list, optional): List of node IDs where the intervention is applied. Default is None. property_restrictions_list (list, optional): List of property restrictions for the intervention. Default is an empty list. - co_event (None, optional): Expansion slot for future use. + co_event (None, optional): The name of the event to be broadcast. This event name can be set in the Report_Event_Recorder_Events configuration parameter. It will be collected in ReportEventRecorder.csv with default event "VaccineDistributed". Returns: TriggeredCampaignEvent: An instance of a triggered campaign event with the TyphoidVaccine intervention. """ iv = new_intervention( camp, efficacy=efficacy, mode=mode, constant_period=constant_period, decay_constant=decay_constant ) - - event = common.TriggeredCampaignEvent( camp, Start_Day=start_day, Triggers=triggers, Demographic_Coverage=coverage, Intervention_List=[ iv ], Node_Ids=node_ids, Property_Restrictions=property_restrictions_list, Event_Name="Triggered Typhoid Vax" ) + if co_event: + signal = common.BroadcastEvent(camp, co_event) + iv = [iv, signal] + else: + iv = [iv] + event = common.TriggeredCampaignEvent( camp, Start_Day=start_day, Triggers=triggers, Demographic_Coverage=coverage, Intervention_List=iv, Node_Ids=node_ids, Property_Restrictions=property_restrictions_list, Event_Name="Triggered Typhoid Vax" ) return event @@ -135,11 +150,12 @@ def new_routine_immunization( coverage (float, optional): Demographic coverage of the intervention. Default is 1.0. node_ids (list, optional): List of node IDs where the intervention is applied. Default is None. property_restrictions_list (list, optional): List of property restrictions for the intervention. Default is an empty list. - co_event (None, optional): Expansion slot for future use. + co_event (str, optional): The name of the event to be broadcast. This event name can be set in the Report_Event_Recorder_Events configuration parameter. It will be collected in ReportEventRecorder.csv with default event "VaccineDistributed" if not set with other name. Returns: TriggeredCampaignEvent: An instance of a triggered campaign event with the TyphoidVaccine intervention. """ + # routine_immunization will always be a first vax (unless something is really whack) so mode doesn't matter. iv = new_vax( camp, efficacy=efficacy, mode=mode, constant_period=constant_period, decay_constant=decay_constant, expected_expiration=expected_expiration ) if co_event: signal = common.BroadcastEvent( camp, co_event ) @@ -185,17 +201,19 @@ def new_scheduled_intervention( coverage (float, optional): Demographic coverage of the intervention. Default is 1.0. node_ids (list, optional): List of node IDs where the intervention is applied. Default is None. property_restrictions_list (list, optional): List of property restrictions for the intervention. Default is an empty list. - co_event (None, optional): Expansion slot for future use. + co_event (None, optional): The name of the event to be broadcast. This event name can be set in the Report_Event_Recorder_Events configuration parameter. It will be collected in ReportEventRecorder.csv if set not None or "". Returns: ScheduledCampaignEvent: An instance of a scheduled campaign event with the TyphoidVaccine intervention. """ iv = new_intervention( camp, efficacy=efficacy, mode=mode, constant_period=constant_period, decay_constant=decay_constant ) - - #event = common.ScheduledCampaignEvent( camp, Start_Day=start_day, Demographic_Coverage=coverage, Intervention_List=[ act_intervention, bcast_intervention ], Node_Ids=nodeIDs, Property_Restrictions=property_restrictions_list ) - event = common.ScheduledCampaignEvent( camp, Start_Day=start_day, Demographic_Coverage=coverage, Intervention_List=[ iv ], Node_Ids=node_ids, Property_Restrictions=property_restrictions_list ) - + if co_event: + signal = common.BroadcastEvent(camp, co_event) + iv = [iv, signal] + else: + iv = [iv] + event = common.ScheduledCampaignEvent( camp, Start_Day=start_day, Demographic_Coverage=coverage, Intervention_List=iv, Node_Ids=node_ids, Property_Restrictions=property_restrictions_list ) return event def new_intervention_as_file( camp, start_day, filename=None ): diff --git a/examples/HINTy/requirements.txt b/examples/HINTy/requirements.txt index f6ada09..5b99404 100644 --- a/examples/HINTy/requirements.txt +++ b/examples/HINTy/requirements.txt @@ -1,4 +1,4 @@ -emodpy-typhoid==0.0.5.dev2 +emodpy-typhoid==0.0.5 emodpy==1.22.0.dev3 emod-api==1.31.0.dev0 emod-typhoid==0.0.4.dev2 diff --git a/examples/future_campaign/multi_sweep.py b/examples/future_campaign/multi_sweep.py index 29472d0..3c48892 100644 --- a/examples/future_campaign/multi_sweep.py +++ b/examples/future_campaign/multi_sweep.py @@ -22,8 +22,8 @@ from emodpy_typhoid.utility.sweeping import ItvFn, CfgFn, set_param, sweep_functions -BASE_YEAR = 2005 -SIMULATION_DURATION_IN_YEARS = 25 +BASE_YEAR = 1990 +SIMULATION_DURATION_IN_YEARS = 40 CAMP_START_YEAR = 2015 FWD_CAMP_START_YEAR = 2024.25 @@ -40,14 +40,13 @@ def set_param_fn(config): config.parameters.Inset_Chart_Reporting_Start_Year = BASE_YEAR config.parameters.Inset_Chart_Reporting_Stop_Year = 2030 config.parameters.Enable_Demographics_Reporting = 0 - #config.parameters.Enable_Property_Output = 1 # crash - #config.parameters.Report_Event_Recorder_Events = ["VaccineDistributed", "PropertyChange", "NewInfectionEvent" ] + config.parameters.Enable_Property_Output = 0 config.parameters.Report_Event_Recorder_Events = ["NewInfectionEvent" ] config.parameters["Listed_Events"] = ["VaccineDistributed"] # old school config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 2010 config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2030 - config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" + #config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" # move to after config.parameters.Typhoid_3year_Susceptible_Fraction = 0 config.parameters.Typhoid_6month_Susceptible_Fraction = 0 config.parameters.Typhoid_6year_Susceptible_Fraction = 0 @@ -95,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") @@ -114,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 ) @@ -151,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_camp'] + for key in values.keys(): + if 'coverage' in key: + camp_coverage = values[key] + break if binary_immunity: tv_iv = tv.new_vax(campaign, @@ -165,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], @@ -219,6 +227,7 @@ def run( sweep_choice="All", age_targeted=True, binary_immunity=True ): task.config.parameters.Demographics_Filenames = ["demographics.json","TestDemographics_pak_updated.json"] task.config.parameters.Death_Rate_Dependence = "NONDISEASE_MORTALITY_BY_YEAR_AND_AGE_FOR_EACH_GENDER" task.config.parameters.Birth_Rate_Dependence = "INDIVIDUAL_PREGNANCIES_BY_AGE_AND_YEAR" + task.config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" # this is dumb task.common_assets.add_directory(assets_directory=manifest.assets_input_dir) task.set_sif(manifest.sif) @@ -234,7 +243,7 @@ def get_sweep_list_from_values(start_day_offset, vax_effs, cov, decay): def get_sweep_list_full(): start_day_offset = [1] vax_effs = np.linspace(0.1, 1.0, 10) - decay = [1, 365, 3650, 365000] + decay = [1, 365, 3650, 36500] cov = np.linspace(start=0.0, stop=1.0, num=5) return get_sweep_list_from_values(start_day_offset, vax_effs, cov, decay) @@ -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/multi_sweep_hint.py b/examples/future_campaign/multi_sweep_hint.py new file mode 100644 index 0000000..ee682f3 --- /dev/null +++ b/examples/future_campaign/multi_sweep_hint.py @@ -0,0 +1,360 @@ +#!/usr/bin/env python +import itertools + +import numpy as np # just for linspace +from functools import partial + +# idmtools ... +from idmtools.builders import SimulationBuilder +from idmtools.core import ItemType +from idmtools.core.platform_factory import Platform +from idmtools.entities.experiment import Experiment +from idmtools.entities.templated_simulation import TemplatedSimulations + + +# emodpy +from emodpy.emod_task import EMODTask + +import emod_api.interventions.common as comm + +import manifest + +from emodpy_typhoid.utility.sweeping import ItvFn, CfgFn, set_param, sweep_functions + + +BASE_YEAR = 2005 +SIMULATION_DURATION_IN_YEARS = 25 +CAMP_START_YEAR = 2015 +FWD_CAMP_START_YEAR = 2024.25 + + +def year_to_days(year): + return ((year - BASE_YEAR) * 365) + +def set_param_fn(config): + config.parameters.Simulation_Type = "TYPHOID_SIM" + config.parameters.Simulation_Duration = SIMULATION_DURATION_IN_YEARS * 365.0 + config.parameters.Base_Individual_Sample_Rate = 0.2 + + config.parameters.Base_Year = BASE_YEAR + config.parameters.Inset_Chart_Reporting_Start_Year = BASE_YEAR + config.parameters.Inset_Chart_Reporting_Stop_Year = 2030 + config.parameters.Enable_Demographics_Reporting = 0 + #config.parameters.Enable_Property_Output = 1 # crash + #config.parameters.Report_Event_Recorder_Events = ["VaccineDistributed", "PropertyChange", "NewInfectionEvent" ] + config.parameters.Report_Event_Recorder_Events = ["NewInfectionEvent" ] + config.parameters["Listed_Events"] = ["VaccineDistributed"] # old school + + config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 2010 + config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2030 + config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" + config.parameters.Typhoid_3year_Susceptible_Fraction = 0 + config.parameters.Typhoid_6month_Susceptible_Fraction = 0 + config.parameters.Typhoid_6year_Susceptible_Fraction = 0 + config.parameters.Typhoid_Acute_Infectiousness = 13435 + config.parameters.Typhoid_Carrier_Probability = 0.108 + config.parameters.Typhoid_Carrier_Removal_Year = 2500 + config.parameters.Typhoid_Chronic_Relative_Infectiousness = 0.241 + config.parameters.Typhoid_Contact_Exposure_Rate = 0.06918859049226553 + config.parameters.Typhoid_Environmental_Exposure_Rate = 0.06169346985005757 + config.parameters.Typhoid_Environmental_Cutoff_Days = 157.20690133538764 + config.parameters.Typhoid_Environmental_Peak_Start = 355.0579483941714 + config.parameters.Typhoid_Environmental_Ramp_Down_Duration = 112.30224910440123 + config.parameters.Typhoid_Environmental_Ramp_Up_Duration = 39.540475369174146 + config.parameters.Typhoid_Exposure_Lambda = 7.0 + config.parameters.Typhoid_Prepatent_Relative_Infectiousness = 0.5 + config.parameters.Typhoid_Protection_Per_Infection = 0.98 + config.parameters.Typhoid_Subclinical_Relative_Infectiousness = 1 + config.parameters.Typhoid_Symptomatic_Fraction = 0.07 + # config.parameters.x_Birth = 1.2 + + # when using 2018 binary + import emodpy_typhoid.config as config_utils + config_utils.cleanup_for_2018_mode(config) + return config + + +def build_camp(): + import emod_api.campaign as camp + + print(f"Telling emod-api to use {manifest.schema_file} as schema.") + camp.set_schema(manifest.schema_file) + import emodpy_typhoid.interventions.outbreak as ob + ob_event = ob.add_outbreak_individual(start_day=1, + demographic_coverage=0.05, + node_ids=[1], + repetitions= 5, + timesteps_between_repetitions=30, + ind_property_restrictions=["Region:Rural"] # just to show we can and stuff + ) + camp.add(ob_event) + + def add_historical_vax( camp, ria_coverage=0.75, camp_coverage=0.75, efficacy=0.8, expiration=3650 ): + import emodpy_typhoid.interventions.typhoid_vaccine as tv + + ria = tv.new_routine_immunization(camp, + efficacy=efficacy, + constant_period=0, + expected_expiration=expiration, + #decay_constant=values['decay_constant'], + 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'], + constant_period=0) + + notification_iv = comm.BroadcastEvent(camp, "VaccineDistributed") + camp.add(ria) + + one_time_campaign = comm.ScheduledCampaignEvent(camp, + Start_Day=year_to_days(CAMP_START_YEAR), + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=camp_coverage, + Target_Age_Min=0.75, + Target_Age_Max=15 + ) + camp.add(one_time_campaign) + + #add_historical_vax( camp ) + add_historical_vax( camp, ria_coverage=1.0, camp_coverage=1.0, efficacy=1.0, expiration=36500 ) + return camp + + +def build_demog(): + """ + Build a demographics input file for the DTK using emod_api. + """ + import emodpy_typhoid.demographics.TyphoidDemographics as Demographics # OK to call into emod-api + + demog = Demographics.from_template_node(lat=0, lon=0, pop=10000, name=1, forced_id=1) + # We're getting all our demographics from a static file overlay. + demog.AddIndividualPropertyAndHINT( Property="Region", + Values = [ "Rural", "Urban" ], + InitialDistribution = [ 0.8, 0.2 ], + TransmissionMatrix = [ + [ 1, 0.1 ], + [ 0.1, 1 ] + ], + EnviroTransmissionMatrix = [ + [ 1, 0.1 ], + [ 0.1, 1 ] + ] + ) + + return demog + + +def add_vax_intervention(campaign, values, min_age=0.75, max_age=15, binary_immunity=True): + """ + Add 1 or both vaccine interventions: + 1) 'campaign' intervention is a one-time vax to an age-banded segment of the population. + 2) 'ria' intervention is a vax given to infants at 9-months. + + Args: + campaign: Central campaign builder object. + values: Dictionary that helps with sweeping, includes 'coverage', 'efficacy', 'decay_constant', and 'start_day_offset'. + min_age: Minimum age in years for 'campaign'. Can be 0. + max_age: Maximum age in years for 'campaign'. Can be 125. + binary_immunity: Vax efficacy can wane continuosly (False) or drop to 0 all at once (True). + """ + + 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_camp'] + + if binary_immunity: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + expected_expiration=values['decay_constant'], + constant_period=0) + else: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + decay_constant=values['decay_constant'], + constant_period=0) + + notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") + one_time_campaign = comm.ScheduledCampaignEvent(campaign, + Start_Day=year_to_days(FWD_CAMP_START_YEAR), + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=camp_coverage, + Target_Age_Min=min_age, + Target_Age_Max=max_age + ) + campaign.add(one_time_campaign) + return { + "start_day": values['start_day_offset'], + 'efficacy': values['efficacy'], + 'coverage_camp': camp_coverage, + 'decay': values['decay_constant'] + } + +def sweep_config_func(config, values): + config.parameters.Typhoid_Acute_Infectiousness = values['typhoid_acute_infectiousness'] + config.parameters.Typhoid_Exposure_Lambda = values['typhoid_exposure_lambda'] + return {'Typhoid_Acute_Infectiousness': values['typhoid_acute_infectiousness'], 'Typhoid_Exposure_Lambda': values['typhoid_exposure_lambda']} + +def get_sweep_builders(camp_sweep_list, config_sweep_list, add_vax_fn=add_vax_intervention): + """ + Build simulation builders. + Args: + kwargs: User inputs may overwrite the entries in the block. + + Returns: + lis of Simulation builders + """ + builder = SimulationBuilder() + funcs_list = [[ + ItvFn(add_vax_fn, ce), + partial(set_param, param='Run_Number', value=x), + CfgFn(sweep_config_func, y) + ] + for ce in camp_sweep_list # for sweep on sweep_list + for x in range(2) # for sweep Run_Number + for y in config_sweep_list + ] + + builder.add_sweep_definition(sweep_functions, funcs_list) + + return [builder] + + +def run( sweep_choice="All", age_targeted=True, binary_immunity=True ): + platform = Platform("SLURM", node_group="idm_48cores", priority="Highest") + #platform = Platform("SLURMStage", node_group="idm_48cores", priority="Highest") + + task = EMODTask.from_default2(config_path="config.json", eradication_path=manifest.eradication_path, + campaign_builder=build_camp, demog_builder=build_demog, schema_path=manifest.schema_file, + param_custom_cb=set_param_fn, ep4_custom_cb=None) + # normally we don't force-set parameters at this point + task.config.parameters.Demographics_Filenames = ["demographics.json","TestDemographics_pak_updated.json"] + task.config.parameters.Death_Rate_Dependence = "NONDISEASE_MORTALITY_BY_YEAR_AND_AGE_FOR_EACH_GENDER" + task.config.parameters.Birth_Rate_Dependence = "INDIVIDUAL_PREGNANCIES_BY_AGE_AND_YEAR" + # this is dumb + task.common_assets.add_directory(assets_directory=manifest.assets_input_dir) + task.set_sif(manifest.sif) + + # Create simulation sweep with builder + def get_sweep_list_from_values(start_day_offset, vax_effs, cov, decay): + sweep_list = [] + combinations = list(itertools.product(start_day_offset, vax_effs, cov, decay)) + for c in combinations: + 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_full(): + start_day_offset = [1] + vax_effs = np.linspace(0.1, 1.0, 10) + decay = [1, 365, 3650, 365000] + cov = np.linspace(start=0.0, stop=1.0, num=5) + return get_sweep_list_from_values(start_day_offset, vax_effs, cov, decay) + + def get_sweep_list_efficacy(): + start_day_offset = [1] + vax_effs = np.linspace(0.1, 1.0, 10) + decay = [3000] + cov = [1] + return get_sweep_list_from_values(start_day_offset, vax_effs, cov, decay) + + def get_sweep_list_coverage(): + start_day_offset = [1] + vax_effs = [1] + decay = [3000] + cov = np.linspace(start=0.0, stop=1.0, num=5) + return get_sweep_list_from_values(start_day_offset, vax_effs, cov, decay) + + def get_sweep_list_coverage_ria(): + start_day_offset = [1] + vax_effs = [1] + decay = [3000] + cov = np.linspace(start=0.0, stop=1.0, num=5) + 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_ria': c[2], 'decay_constant': c[3]}) + return sweep_list + + def get_sweep_list_coverage_camp(): + start_day_offset = [1] + vax_effs = [1] + decay = [3000] + cov = np.linspace(start=0.0, stop=1.0, num=5) + 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_duration(): + start_day_offset = [1] + vax_effs = [1] + decays = [1,365,3650,36500] + covs = [1.0] + + combinations = list(itertools.product(start_day_offset, vax_effs, covs, decays)) + sweep_list = [] + for c in combinations: + 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_from_csv(): + # This is wrong. Just load rows. Code is recreating. But have to stop work for now. + import pandas as pd + df = pd.load_csv( manifest.sweep_config ) + raise NotImplemented( "get_sweep_list_from_csv" ) + + def get_config_sweep_list(): + tac = [ 13435, 15320 ] + tel = [ 5.0, 7.0 ] + combinations = list(itertools.product(tac, tel)) + sweep_list = [] + for c in combinations: + sweep_list.append({'typhoid_acute_infectiousness': c[0], 'typhoid_exposure_lambda': c[1]}) + return sweep_list + + sweep_selections = { + "All": get_sweep_list_full, + "Efficacy": get_sweep_list_efficacy, + "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 + } + + if sweep_choice not in sweep_selections.keys(): + raise ValueError( f"{sweep_choice} not found in {sweep_selections.keys()}." ) + sweep_list = sweep_selections[ sweep_choice ]() + + if age_targeted: + avi_age_coverage = add_vax_intervention + 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 ) + + builders = get_sweep_builders(sweep_list, get_config_sweep_list(), add_vax_fn=avi_decay) + + # create TemplatedSimulations from task and builders + ts = TemplatedSimulations(base_task=task, builders=builders) + # create experiment from TemplatedSimulations + experiment = Experiment.from_template(ts, name=f"{sweep_choice} Sweep") + experiment.run(wait_until_done=True, platform=platform) + task.handle_experiment_completion(experiment) + + # download and plot some stuff. + EMODTask.get_file_from_comps(experiment.uid, ["InsetChart.json", "ReportEventRecorder.csv"]) + task.cache_experiment_metadata_in_sql(experiment.uid) + return str(experiment.uid) + +if __name__ == "__main__": + import emod_typhoid.bootstrap as dtk + + dtk.setup(manifest.model_dl_dir) + + run( "Coverage_Camp" ) diff --git a/examples/future_campaign/requirements.txt b/examples/future_campaign/requirements.txt index a75e372..5c3ff76 100644 --- a/examples/future_campaign/requirements.txt +++ b/examples/future_campaign/requirements.txt @@ -1,5 +1,5 @@ -i https://packages.idmod.org/api/pypi/pypi-production/simple emodpy-typhoid==0.0.6.dev0 emodpy==1.22.0.dev3 -emod-api==1.31.0.dev0 -emod-typhoid==0.0.4.dev2 +emod-api==1.31.0.dev1 +emod-typhoid==0.0.4.dev7 diff --git a/examples/vaccination/example_mutil_sweep.py b/examples/vaccination/example_multi_sweep.py similarity index 51% rename from examples/vaccination/example_mutil_sweep.py rename to examples/vaccination/example_multi_sweep.py index ff1c279..ad420ce 100644 --- a/examples/vaccination/example_mutil_sweep.py +++ b/examples/vaccination/example_multi_sweep.py @@ -11,7 +11,6 @@ from idmtools.entities.experiment import Experiment from idmtools.entities.templated_simulation import TemplatedSimulations - # emodpy from emodpy.emod_task import EMODTask @@ -19,11 +18,12 @@ import manifest -from emodpy_typhoid.utility.sweeping import ItvFn, set_param, sweep_functions +from emodpy_typhoid.utility.sweeping import ItvFn, set_param, sweep_functions, CfgFn -BASE_YEAR = 2005 -SIMULATION_DURATION_IN_YEARS = 20 -CAMP_START_YEAR = 2015 +BASE_YEAR = 1990 +SIMULATION_DURATION_IN_YEARS = 40 +CAMP_START_YEAR = 2020 +FWD_CAMP_START_YEAR = 2024.25 def update_sim_bic(simulation, value): @@ -38,18 +38,18 @@ def year_to_days(year): def set_param_fn(config): config.parameters.Simulation_Type = "TYPHOID_SIM" config.parameters.Simulation_Duration = SIMULATION_DURATION_IN_YEARS * 365.0 - config.parameters.Base_Individual_Sample_Rate = 0.2 + config.parameters.Base_Individual_Sample_Rate = 1 config.parameters.Base_Year = BASE_YEAR - config.parameters.Inset_Chart_Reporting_Start_Year = 2010 - config.parameters.Inset_Chart_Reporting_Stop_Year = 2030 + config.parameters.Inset_Chart_Reporting_Start_Year = 1990 + config.parameters.Inset_Chart_Reporting_Stop_Year = 2040 config.parameters.Enable_Demographics_Reporting = 0 # config.parameters.Enable_Property_Output = 1 - config.parameters.Report_Event_Recorder_Events = ["VaccineDistributed", "PropertyChange"] + config.parameters.Report_Event_Recorder_Events = ["VaccineDistributed"] config.parameters["Listed_Events"] = ["VaccineDistributed"] # old school - config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 2010 - config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2030 + config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 1990 + config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2040 config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" config.parameters.Typhoid_3year_Susceptible_Fraction = 0 config.parameters.Typhoid_6month_Susceptible_Fraction = 0 @@ -82,20 +82,28 @@ def build_camp(): Build a campaign input file for the DTK using emod_api. Right now this function creates the file and returns the filename. If calling code just needs an asset that's fine. """ + # import emod_api.campaign as camp + # + # print(f"Telling emod-api to use {manifest.schema_file} as schema.") + # camp.set_schema(manifest.schema_file) + # import emod_api.interventions.outbreak as ob + # for x in range(10): + # event = ob.new_intervention(camp, timestep=1 + x, cases=1) + # camp.add(event) + # return camp import emod_api.campaign as camp print(f"Telling emod-api to use {manifest.schema_file} as schema.") camp.set_schema(manifest.schema_file) import emod_api.interventions.outbreak as ob - for x in range(10): - event = ob.new_intervention(camp, timestep=1 + x, cases=1) - camp.add(event) + ob.seed(camp, Start_Day=1, Coverage=0.5, Honor_Immunity=False) + ob.seed(camp, Start_Day=365, Coverage=0.005, Tot_Rep=10, Rep_Interval=30, Honor_Immunity=False) return camp def build_demog(): """ - Build a demographics input file for the DTK using emod_api. + Build a demographics input file for the DTK using emod_api. """ import emodpy_typhoid.demographics.TyphoidDemographics as Demographics # OK to call into emod-api @@ -117,38 +125,99 @@ def build_demog(): return demog -def add_vax_intervention(campaign, values): +def add_vax_intervention(campaign, values, min_age=0.75, max_age=15, binary_immunity=True): + """ + Add 1 or both vaccine interventions: + 1) 'campaign' intervention is a one-time vax to an age-banded segment of the population. + 2) 'ria' intervention is a vax given to infants at 9-months. + + Args: + campaign: Central campaign builder object. + values: Dictionary that helps with sweeping, includes 'coverage', 'efficacy', 'decay_constant', and 'start_day_offset'. + min_age: Minimum age in years for 'campaign'. Can be 0. + max_age: Maximum age in years for 'campaign'. Can be 125. + binary_immunity: Vax efficacy can wane continuosly (False) or drop to 0 all at once (True). + """ + 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) - ria = tv.new_routine_immunization(campaign, - efficacy=values['efficacy'], - decay_constant=values['decay_constant'], - start_day=year_to_days(CAMP_START_YEAR) + values['start_day_offset'], - coverage=values['coverage'] - ) + camp_coverage = values['coverage'] + + if binary_immunity: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + expected_expiration=values['expected_expiration'], + constant_period=0) + else: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + decay_constant=values['decay_constant'], + constant_period=0) + + def add_historical_vax(camp, ria_coverage=0.75, camp_coverage=0.75, efficacy=0.9, expiration=6 * 365): + import emodpy_typhoid.interventions.typhoid_vaccine as tv + + ria = tv.new_routine_immunization(camp, + efficacy=efficacy, + constant_period=0, + expected_expiration=expiration, + # decay_constant=values['decay_constant'], + 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'], + constant_period=0) + + notification_iv = comm.BroadcastEvent(camp, "VaccineDistributed") + camp.add(ria) + + one_time_campaign = comm.ScheduledCampaignEvent(camp, + Start_Day=year_to_days(CAMP_START_YEAR), + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=camp_coverage, + Target_Age_Min=min_age, + Target_Age_Max=max_age + ) + camp.add(one_time_campaign) + + # add_historical_vax( camp ) + add_historical_vax(campaign, ria_coverage=0.75, camp_coverage=camp_coverage, efficacy=values['efficacy'], + expiration=365 * 6) notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") - campaign.add(ria) - # - tv_iv = tv.new_vax(campaign, - efficacy=values['efficacy'], - decay_constant=values['decay_constant'], - constant_period=0 - ) one_time_campaign = comm.ScheduledCampaignEvent(campaign, - Start_Day=year_to_days(CAMP_START_YEAR) + values['start_day_offset'], + Start_Day=year_to_days(FWD_CAMP_START_YEAR), Intervention_List=[tv_iv, notification_iv], - Demographic_Coverage=values['coverage'], - Target_Age_Min=0.75, - Target_Age_Max=15 + Demographic_Coverage=camp_coverage, + Target_Age_Min=min_age, + Target_Age_Max=max_age ) campaign.add(one_time_campaign) - return {"start_day": values['efficacy'], 'efficacy': values['efficacy'], 'coverage': values['coverage'], - 'decay': values['decay_constant']} - - -def get_sweep_builders(sweep_list): + return { + "start_day": values['start_day_offset'], + 'efficacy': values['efficacy'], + 'coverage_camp': camp_coverage, + 'expected_expiration': values['expected_expiration'] + } + + +def sweep_config_func(config, values): + config.parameters.Typhoid_Acute_Infectiousness = values['Typhoid_Acute_Infectiousness'] + config.parameters.Typhoid_Exposure_Lambda = values['Typhoid_Exposure_Lambda'] + config.parameters.Typhoid_Environmental_Exposure_Rate = values['Typhoid_Environmental_Exposure_Rate'] + config.parameters.Typhoid_Contact_Exposure_Rate = values['Typhoid_Contact_Exposure_Rate'] + config.parameters.Typhoid_Symptomatic_Fraction = values['Typhoid_Symptomatic_Fraction'] + return {'Typhoid_Acute_Infectiousness': values['Typhoid_Acute_Infectiousness'], + 'Typhoid_Exposure_Lambda': values['Typhoid_Exposure_Lambda'], + 'Typhoid_Environmental_Exposure_Rate': values['Typhoid_Environmental_Exposure_Rate'], + 'Typhoid_Contact_Exposure_Rate': values['Typhoid_Contact_Exposure_Rate'], + 'Typhoid_Symptomatic_Fraction': values['Typhoid_Symptomatic_Fraction']} + + +def get_sweep_builders(sweep_list, sweep_config): """ Build simulation builders. Args: @@ -161,9 +230,11 @@ def get_sweep_builders(sweep_list): funcs_list = [[ ItvFn(add_vax_intervention, ce), partial(set_param, param='Run_Number', value=x), + CfgFn(sweep_config_func, y) ] for ce in sweep_list # for sweep on sweep_list for x in range(2) # for sweep Run_Number + for y in sweep_config ] builder.add_sweep_definition(sweep_functions, funcs_list) @@ -186,23 +257,56 @@ def run_test(): task.common_assets.add_directory(assets_directory=manifest.assets_input_dir) task.set_sif(manifest.sif) # Create simulation sweep with builder + # start_day_offset = [1] + # # vax_effs = np.linspace(0, 1.0, 3) # 0.0, 0.5, 1.0 + # vax_effs = [0.9] + # # decay_constant = [2000, 3000] + # expected_expiration = [2190, 6935] + # # cov = np.linspace(start=0.5, stop=1.0, num=6) + # cov = [0.5, 0.75, 1] + # sweep_list = [] + # Typhoid_Acute_Infectiousness = [10000, 13000, 16000] + # Typhoid_Exposure_Lambda = [0, 5, 10] + # Typhoid_Environmental_Exposure_Rate = [0.04, 0.28, 0.4, 0.54] + # Typhoid_Contact_Exposure_Rate = [0.009, 0.02, 0.4, 1.0] + # Typhoid_Symptomatic_Fraction = [0.04, 0.05, 0.06] start_day_offset = [1] - vax_effs = np.linspace(0, 1.0, 3) # 0.0, 0.5, 1.0 - decay = [2000, 3000] - cov = np.linspace(start=0.5, stop=1.0, num=6) + vax_effs = [0.9] + expected_expiration = [2190] + cov = [1] sweep_list = [] - combinations = list(itertools.product(start_day_offset, vax_effs, cov, decay)) + Typhoid_Acute_Infectiousness = [13000] + Typhoid_Exposure_Lambda = [5] + Typhoid_Environmental_Exposure_Rate = [0.4] + Typhoid_Contact_Exposure_Rate = [0.009] + min_value = 0.05 + max_value = 0.5 + step = 0.05 + my_array = np.linspace(min_value, max_value, num=int((max_value - min_value) / step) + 1) + + Typhoid_Symptomatic_Fraction = my_array + sweep_config = [] + combinations_config = list( + itertools.product(Typhoid_Acute_Infectiousness, Typhoid_Exposure_Lambda, Typhoid_Environmental_Exposure_Rate, + Typhoid_Contact_Exposure_Rate, Typhoid_Symptomatic_Fraction)) + + for c in combinations_config: + sweep_config.append({'Typhoid_Acute_Infectiousness': c[0], 'Typhoid_Exposure_Lambda': c[1], + 'Typhoid_Environmental_Exposure_Rate': c[2], 'Typhoid_Contact_Exposure_Rate': c[3], + 'Typhoid_Symptomatic_Fraction': c[4]}) + + combinations = list(itertools.product(start_day_offset, vax_effs, cov, expected_expiration)) for c in combinations: - sweep_list.append({'start_day_offset': c[0], 'efficacy': c[1], 'coverage': c[2], 'decay_constant': c[3]}) - builders = get_sweep_builders(sweep_list) + sweep_list.append({'start_day_offset': c[0], 'efficacy': c[1], 'coverage': c[2], 'expected_expiration': c[3]}) + builders = get_sweep_builders(sweep_list, sweep_config) # create TemplatedSimulations from task and builders ts = TemplatedSimulations(base_task=task, builders=builders) # create experiment from TemplatedSimulations - experiment = Experiment.from_template(ts, name="test_vax_sweep") + experiment = Experiment.from_template(ts, name="test_vax_sweep_configs") # The last step is to call run() on the ExperimentManager to run the simulations. experiment.run(wait_until_done=True, platform=platform) - # exp_id = '87d7d4eb-3f6a-ee11-92fc-f0921c167864' - # experiment = platform.get_item(exp_id, item_type=ItemType.EXPERIMENT) + #exp_id = 'f6bd69fb-ec79-ee11-92fd-f0921c167864' + #experiment = platform.get_item(exp_id, item_type=ItemType.EXPERIMENT) task.handle_experiment_completion(experiment) # download and plot some stuff. diff --git a/examples/vaccination/example_mutil_sweep_Punjab1.py b/examples/vaccination/example_mutil_sweep_Punjab1.py new file mode 100644 index 0000000..29bdc5f --- /dev/null +++ b/examples/vaccination/example_mutil_sweep_Punjab1.py @@ -0,0 +1,345 @@ +#!/usr/bin/env python +import itertools + +import numpy as np # just for linspace +from functools import partial + +# idmtools ... +from idmtools.builders import SimulationBuilder +from idmtools.core import ItemType +from idmtools.core.platform_factory import Platform +from idmtools.entities.experiment import Experiment +from idmtools.entities.templated_simulation import TemplatedSimulations + +# emodpy +from emodpy.emod_task import EMODTask + +import emod_api.interventions.common as comm + +import manifest + +from emodpy_typhoid.utility.sweeping import ItvFn, set_param, sweep_functions, CfgFn + +BASE_YEAR = 1990 +SIMULATION_DURATION_IN_YEARS = 40 +CAMP_START_YEAR = 2021.169 +FWD_CAMP_START_YEAR = 2024.25 + + +def update_sim_bic(simulation, value): + simulation.task.config.parameters.Base_Infectivity_Constant = value * 0.1 + return {"Base_Infectivity": value} + + +def year_to_days(year): + return ((year - BASE_YEAR) * 365) + + +def set_param_fn(config): + config.parameters.Simulation_Type = "TYPHOID_SIM" + config.parameters.Simulation_Duration = SIMULATION_DURATION_IN_YEARS * 365.0 + config.parameters.Base_Individual_Sample_Rate = 1 + + config.parameters.Base_Year = BASE_YEAR + config.parameters.Inset_Chart_Reporting_Start_Year = 1990 + config.parameters.Inset_Chart_Reporting_Stop_Year = 2040 + config.parameters.Enable_Demographics_Reporting = 0 + # config.parameters.Enable_Property_Output = 1 + config.parameters.Report_Event_Recorder_Events = ["VaccineDistributed"] + config.parameters["Listed_Events"] = ["VaccineDistributed"] # old school + + config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 1990 + config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2040 + config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" + config.parameters.Typhoid_3year_Susceptible_Fraction = 0 + config.parameters.Typhoid_6month_Susceptible_Fraction = 0 + config.parameters.Typhoid_6year_Susceptible_Fraction = 0 + config.parameters.Typhoid_Acute_Infectiousness = 13435 + config.parameters.Typhoid_Carrier_Probability = 0.108 + config.parameters.Typhoid_Carrier_Removal_Year = 2500 + config.parameters.Typhoid_Chronic_Relative_Infectiousness = 0.241 + config.parameters.Typhoid_Contact_Exposure_Rate = 0.06918859049226553 + config.parameters.Typhoid_Environmental_Exposure_Rate = 0.06169346985005757 + config.parameters.Typhoid_Environmental_Cutoff_Days = 157.20690133538764 + config.parameters.Typhoid_Environmental_Peak_Start = 355.0579483941714 + config.parameters.Typhoid_Environmental_Ramp_Down_Duration = 112.30224910440123 + config.parameters.Typhoid_Environmental_Ramp_Up_Duration = 39.540475369174146 + config.parameters.Typhoid_Exposure_Lambda = 7.0 + config.parameters.Typhoid_Prepatent_Relative_Infectiousness = 0.5 + config.parameters.Typhoid_Protection_Per_Infection = 0.98 + config.parameters.Typhoid_Subclinical_Relative_Infectiousness = 1 + config.parameters.Typhoid_Symptomatic_Fraction = 0.07 + # config.parameters.x_Birth = 1.2 + + # when using 2018 binary + import emodpy_typhoid.config as config_utils + config_utils.cleanup_for_2018_mode(config) + return config + + +def build_camp(): + """ + Build a campaign input file for the DTK using emod_api. + Right now this function creates the file and returns the filename. If calling code just needs an asset that's fine. + """ + # import emod_api.campaign as camp + # + # print(f"Telling emod-api to use {manifest.schema_file} as schema.") + # camp.set_schema(manifest.schema_file) + # import emod_api.interventions.outbreak as ob + # for x in range(10): + # event = ob.new_intervention(camp, timestep=1 + x, cases=1) + # camp.add(event) + # return camp + import emod_api.campaign as camp + + print(f"Telling emod-api to use {manifest.schema_file} as schema.") + camp.set_schema(manifest.schema_file) + import emod_api.interventions.outbreak as ob + ob.seed(camp, Start_Day=1, Coverage=0.5, Honor_Immunity=False) + ob.seed(camp, Start_Day=365, Coverage=0.005, Tot_Rep=10, Rep_Interval=30, Honor_Immunity=False) + return camp + + +def build_demog(): + """ + Build a demographics input file for the DTK using emod_api. + """ + import emodpy_typhoid.demographics.TyphoidDemographics as Demographics # OK to call into emod-api + + demog = Demographics.from_template_node(lat=0, lon=0, pop=10000, name=1, forced_id=1) + # We're getting all our demographics from a static file overlay. + + """ + # This doesn't work right now but still want to leave in example of what we want to be able to do soon. + demog.AddAgeDependentTransmission( + Age_Bin_Edges_In_Years = [0, 5, 20, 60, -1], + TransmissionMatrix = [ + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0] + ] + ) + """ + return demog + + +# def add_vax_intervention(campaign, values): +# 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) +# ria = tv.new_routine_immunization(campaign, +# efficacy=values['efficacy'], +# decay_constant=0, +# expected_expiration=values['expected_expiration'], +# start_day=year_to_days(CAMP_START_YEAR) + values['start_day_offset'], +# coverage=values['coverage'] +# ) +# +# notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") +# campaign.add(ria) +# # +# tv_iv = tv.new_vax(campaign, +# efficacy=values['efficacy'], +# decay_constant=0, +# constant_period=0, +# expected_expiration = values['expected_expiration'] +# ) +# one_time_campaign = comm.ScheduledCampaignEvent(campaign, +# Start_Day=year_to_days(CAMP_START_YEAR) + values['start_day_offset'], +# Intervention_List=[tv_iv, notification_iv], +# Demographic_Coverage=values['coverage'], +# Target_Age_Min=0.75, +# Target_Age_Max=15 +# ) +# campaign.add(one_time_campaign) +# return {"start_day": values['start_day_offset'], 'efficacy': values['efficacy'], 'coverage': values['coverage'], +# 'expected_expiration': values['expected_expiration']} +def add_vax_intervention(campaign, values, min_age=0.75, max_age=15, binary_immunity=True): + """ + Add 1 or both vaccine interventions: + 1) 'campaign' intervention is a one-time vax to an age-banded segment of the population. + 2) 'ria' intervention is a vax given to infants at 9-months. + + Args: + campaign: Central campaign builder object. + values: Dictionary that helps with sweeping, includes 'coverage', 'efficacy', 'decay_constant', and 'start_day_offset'. + min_age: Minimum age in years for 'campaign'. Can be 0. + max_age: Maximum age in years for 'campaign'. Can be 125. + binary_immunity: Vax efficacy can wane continuosly (False) or drop to 0 all at once (True). + """ + + 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'] + + if binary_immunity: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + expected_expiration=values['expected_expiration'], + constant_period=0) + else: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + decay_constant=values['decay_constant'], + constant_period=0) + + def add_historical_vax( camp, ria_coverage=0.75, camp_coverage=0.75, efficacy=0.8, expiration=365 * 6 ): + import emodpy_typhoid.interventions.typhoid_vaccine as tv + + ria = tv.new_routine_immunization(camp, + efficacy=efficacy, + constant_period=0, + expected_expiration=expiration, + #decay_constant=values['decay_constant'], + 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'], + constant_period=0) + + notification_iv = comm.BroadcastEvent(camp, "VaccineDistributed") + camp.add(ria) + + one_time_campaign = comm.ScheduledCampaignEvent(camp, + Start_Day=year_to_days(CAMP_START_YEAR), + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=camp_coverage, + Target_Age_Min=0.75, + Target_Age_Max=15 + ) + camp.add(one_time_campaign) + + # add_historical_vax( camp ) + add_historical_vax(campaign, ria_coverage=0.75, camp_coverage=camp_coverage, efficacy=values['efficacy'], + expiration=365 * 6) + + notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") + one_time_campaign = comm.ScheduledCampaignEvent(campaign, + Start_Day=year_to_days(FWD_CAMP_START_YEAR), + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=camp_coverage, + Target_Age_Min=min_age, + Target_Age_Max=max_age + ) + campaign.add(one_time_campaign) + return { + "start_day": values['start_day_offset'], + 'efficacy': values['efficacy'], + 'coverage_camp': camp_coverage, + 'expected_expiration': values['expected_expiration'] + } + + +def sweep_config_func(config, values): + config.parameters.Typhoid_Acute_Infectiousness = values['Typhoid_Acute_Infectiousness'] + config.parameters.Typhoid_Exposure_Lambda = values['Typhoid_Exposure_Lambda'] + config.parameters.Typhoid_Environmental_Exposure_Rate = values['Typhoid_Environmental_Exposure_Rate'] + config.parameters.Typhoid_Contact_Exposure_Rate = values['Typhoid_Contact_Exposure_Rate'] + config.parameters.Typhoid_Symptomatic_Fraction = values['Typhoid_Symptomatic_Fraction'] + return {'Typhoid_Acute_Infectiousness': values['Typhoid_Acute_Infectiousness'], + 'Typhoid_Exposure_Lambda': values['Typhoid_Exposure_Lambda'], + 'Typhoid_Environmental_Exposure_Rate': values['Typhoid_Environmental_Exposure_Rate'], + 'Typhoid_Contact_Exposure_Rate': values['Typhoid_Contact_Exposure_Rate'], + 'Typhoid_Symptomatic_Fraction': values['Typhoid_Symptomatic_Fraction']} + + +def get_sweep_builders(sweep_list, sweep_config): + """ + Build simulation builders. + Args: + kwargs: User inputs may overwrite the entries in the block. + + Returns: + lis of Simulation builders + """ + builder = SimulationBuilder() + funcs_list = [[ + ItvFn(add_vax_intervention, ce), + partial(set_param, param='Run_Number', value=x), + CfgFn(sweep_config_func, y) + ] + for ce in sweep_list # for sweep on sweep_list + for x in range(1) # for sweep Run_Number + for y in sweep_config + ] + + builder.add_sweep_definition(sweep_functions, funcs_list) + + return [builder] + + +def run_test(): + # Create a platform + # Show how to dynamically set priority and node_group + platform = Platform("SLURM", node_group="idm_48cores", priority="Highest") + + task = EMODTask.from_default2(config_path="config.json", eradication_path=manifest.eradication_path, + campaign_builder=build_camp, demog_builder=None, schema_path=manifest.schema_file, + param_custom_cb=set_param_fn, ep4_custom_cb=None) + # normally we don't force-set parameters at this point + task.config.parameters.Demographics_Filenames = ["TestDemographics_pak_updated.json"] + task.config.parameters.Death_Rate_Dependence = "NONDISEASE_MORTALITY_BY_YEAR_AND_AGE_FOR_EACH_GENDER" + task.config.parameters.Birth_Rate_Dependence = "INDIVIDUAL_PREGNANCIES_BY_AGE_AND_YEAR" + task.common_assets.add_directory(assets_directory=manifest.assets_input_dir) + task.set_sif(manifest.sif) + # Create simulation sweep with builder + start_day_offset = [1] + vax_effs = [0.9] + expected_expiration = [2*365, 4*365, 6*365] + cov = [0.75] + sweep_list = [] + Typhoid_Acute_Infectiousness = [13000] + Typhoid_Exposure_Lambda = [5] + Typhoid_Environmental_Exposure_Rate = [0.4] + Typhoid_Contact_Exposure_Rate = [0.009] + min_value = 0.05 + max_value = 0.5 + step = 0.05 + my_array = np.linspace(min_value, max_value, num=int((max_value - min_value) / step) + 1) + + Typhoid_Symptomatic_Fraction = [0.06] + sweep_config = [] + combinations_config = list( + itertools.product(Typhoid_Acute_Infectiousness, Typhoid_Exposure_Lambda, Typhoid_Environmental_Exposure_Rate, + Typhoid_Contact_Exposure_Rate, Typhoid_Symptomatic_Fraction)) + + for c in combinations_config: + sweep_config.append({'Typhoid_Acute_Infectiousness': c[0], 'Typhoid_Exposure_Lambda': c[1], + 'Typhoid_Environmental_Exposure_Rate': c[2], 'Typhoid_Contact_Exposure_Rate': c[3], + 'Typhoid_Symptomatic_Fraction': c[4]}) + + combinations = list(itertools.product(start_day_offset, vax_effs, cov, expected_expiration)) + for c in combinations: + sweep_list.append({'start_day_offset': c[0], 'efficacy': c[1], 'coverage': c[2], 'expected_expiration': c[3]}) + builders = get_sweep_builders(sweep_list, sweep_config) + # create TemplatedSimulations from task and builders + ts = TemplatedSimulations(base_task=task, builders=builders) + # create experiment from TemplatedSimulations + experiment = Experiment.from_template(ts, name="test_vax_sweep_configs_Punjab_case1") + # The last step is to call run() on the ExperimentManager to run the simulations. + experiment.run(wait_until_done=True, platform=platform) + # exp_id = '87d7d4eb-3f6a-ee11-92fc-f0921c167864' + # experiment = platform.get_item(exp_id, item_type=ItemType.EXPERIMENT) + task.handle_experiment_completion(experiment) + + # download and plot some stuff. + EMODTask.get_file_from_comps(experiment.uid, ["InsetChart.json", "ReportTyphoidByAgeAndGender.csv"]) + task.cache_experiment_metadata_in_sql(experiment.uid) + import matplotlib + matplotlib.use("TkAgg") + import emod_api.channelreports.plot_icj_means as plotter + chan_data = plotter.collect(str(experiment.uid), "Infected", tag="efficacy=SWEEP") + plotter.display(chan_data, False, "Infected", str(experiment.uid)) + + +if __name__ == "__main__": + import emod_typhoid.bootstrap as dtk + + dtk.setup(manifest.model_dl_dir) + + run_test() diff --git a/examples/vaccination/example_mutil_sweep_Punjab3.py b/examples/vaccination/example_mutil_sweep_Punjab3.py new file mode 100644 index 0000000..f96fb3f --- /dev/null +++ b/examples/vaccination/example_mutil_sweep_Punjab3.py @@ -0,0 +1,345 @@ +#!/usr/bin/env python +import itertools + +import numpy as np # just for linspace +from functools import partial + +# idmtools ... +from idmtools.builders import SimulationBuilder +from idmtools.core import ItemType +from idmtools.core.platform_factory import Platform +from idmtools.entities.experiment import Experiment +from idmtools.entities.templated_simulation import TemplatedSimulations + +# emodpy +from emodpy.emod_task import EMODTask + +import emod_api.interventions.common as comm + +import manifest + +from emodpy_typhoid.utility.sweeping import ItvFn, set_param, sweep_functions, CfgFn + +BASE_YEAR = 1990 +SIMULATION_DURATION_IN_YEARS = 40 +CAMP_START_YEAR = 2021.169 +FWD_CAMP_START_YEAR = 2024.25 + + +def update_sim_bic(simulation, value): + simulation.task.config.parameters.Base_Infectivity_Constant = value * 0.1 + return {"Base_Infectivity": value} + + +def year_to_days(year): + return ((year - BASE_YEAR) * 365) + + +def set_param_fn(config): + config.parameters.Simulation_Type = "TYPHOID_SIM" + config.parameters.Simulation_Duration = SIMULATION_DURATION_IN_YEARS * 365.0 + config.parameters.Base_Individual_Sample_Rate = 1 + + config.parameters.Base_Year = BASE_YEAR + config.parameters.Inset_Chart_Reporting_Start_Year = 1990 + config.parameters.Inset_Chart_Reporting_Stop_Year = 2040 + config.parameters.Enable_Demographics_Reporting = 0 + # config.parameters.Enable_Property_Output = 1 + config.parameters.Report_Event_Recorder_Events = ["VaccineDistributed"] + config.parameters["Listed_Events"] = ["VaccineDistributed"] # old school + + config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 1990 + config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2040 + config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" + config.parameters.Typhoid_3year_Susceptible_Fraction = 0 + config.parameters.Typhoid_6month_Susceptible_Fraction = 0 + config.parameters.Typhoid_6year_Susceptible_Fraction = 0 + config.parameters.Typhoid_Acute_Infectiousness = 13435 + config.parameters.Typhoid_Carrier_Probability = 0.108 + config.parameters.Typhoid_Carrier_Removal_Year = 2500 + config.parameters.Typhoid_Chronic_Relative_Infectiousness = 0.241 + config.parameters.Typhoid_Contact_Exposure_Rate = 0.06918859049226553 + config.parameters.Typhoid_Environmental_Exposure_Rate = 0.06169346985005757 + config.parameters.Typhoid_Environmental_Cutoff_Days = 157.20690133538764 + config.parameters.Typhoid_Environmental_Peak_Start = 355.0579483941714 + config.parameters.Typhoid_Environmental_Ramp_Down_Duration = 112.30224910440123 + config.parameters.Typhoid_Environmental_Ramp_Up_Duration = 39.540475369174146 + config.parameters.Typhoid_Exposure_Lambda = 7.0 + config.parameters.Typhoid_Prepatent_Relative_Infectiousness = 0.5 + config.parameters.Typhoid_Protection_Per_Infection = 0.98 + config.parameters.Typhoid_Subclinical_Relative_Infectiousness = 1 + config.parameters.Typhoid_Symptomatic_Fraction = 0.07 + # config.parameters.x_Birth = 1.2 + + # when using 2018 binary + import emodpy_typhoid.config as config_utils + config_utils.cleanup_for_2018_mode(config) + return config + + +def build_camp(): + """ + Build a campaign input file for the DTK using emod_api. + Right now this function creates the file and returns the filename. If calling code just needs an asset that's fine. + """ + # import emod_api.campaign as camp + # + # print(f"Telling emod-api to use {manifest.schema_file} as schema.") + # camp.set_schema(manifest.schema_file) + # import emod_api.interventions.outbreak as ob + # for x in range(10): + # event = ob.new_intervention(camp, timestep=1 + x, cases=1) + # camp.add(event) + # return camp + import emod_api.campaign as camp + + print(f"Telling emod-api to use {manifest.schema_file} as schema.") + camp.set_schema(manifest.schema_file) + import emod_api.interventions.outbreak as ob + ob.seed(camp, Start_Day=1, Coverage=0.5, Honor_Immunity=False) + ob.seed(camp, Start_Day=365, Coverage=0.005, Tot_Rep=10, Rep_Interval=30, Honor_Immunity=False) + return camp + + +def build_demog(): + """ + Build a demographics input file for the DTK using emod_api. + """ + import emodpy_typhoid.demographics.TyphoidDemographics as Demographics # OK to call into emod-api + + demog = Demographics.from_template_node(lat=0, lon=0, pop=10000, name=1, forced_id=1) + # We're getting all our demographics from a static file overlay. + + """ + # This doesn't work right now but still want to leave in example of what we want to be able to do soon. + demog.AddAgeDependentTransmission( + Age_Bin_Edges_In_Years = [0, 5, 20, 60, -1], + TransmissionMatrix = [ + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0] + ] + ) + """ + return demog + + +# def add_vax_intervention(campaign, values): +# 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) +# ria = tv.new_routine_immunization(campaign, +# efficacy=values['efficacy'], +# decay_constant=0, +# expected_expiration=values['expected_expiration'], +# start_day=year_to_days(CAMP_START_YEAR) + values['start_day_offset'], +# coverage=values['coverage'] +# ) +# +# notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") +# campaign.add(ria) +# # +# tv_iv = tv.new_vax(campaign, +# efficacy=values['efficacy'], +# decay_constant=0, +# constant_period=0, +# expected_expiration = values['expected_expiration'] +# ) +# one_time_campaign = comm.ScheduledCampaignEvent(campaign, +# Start_Day=year_to_days(CAMP_START_YEAR) + values['start_day_offset'], +# Intervention_List=[tv_iv, notification_iv], +# Demographic_Coverage=values['coverage'], +# Target_Age_Min=0.75, +# Target_Age_Max=15 +# ) +# campaign.add(one_time_campaign) +# return {"start_day": values['start_day_offset'], 'efficacy': values['efficacy'], 'coverage': values['coverage'], +# 'expected_expiration': values['expected_expiration']} +def add_vax_intervention(campaign, values, min_age=0.75, max_age=15, binary_immunity=True): + """ + Add 1 or both vaccine interventions: + 1) 'campaign' intervention is a one-time vax to an age-banded segment of the population. + 2) 'ria' intervention is a vax given to infants at 9-months. + + Args: + campaign: Central campaign builder object. + values: Dictionary that helps with sweeping, includes 'coverage', 'efficacy', 'decay_constant', and 'start_day_offset'. + min_age: Minimum age in years for 'campaign'. Can be 0. + max_age: Maximum age in years for 'campaign'. Can be 125. + binary_immunity: Vax efficacy can wane continuosly (False) or drop to 0 all at once (True). + """ + + 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'] + + if binary_immunity: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + expected_expiration=values['expected_expiration'], + constant_period=0) + else: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + decay_constant=values['decay_constant'], + constant_period=0) + + def add_historical_vax(camp, ria_coverage=0.75, camp_coverage=0.75, efficacy=0.8, expiration=365 * 6): + import emodpy_typhoid.interventions.typhoid_vaccine as tv + + ria = tv.new_routine_immunization(camp, + efficacy=efficacy, + constant_period=0, + expected_expiration=expiration, + # decay_constant=values['decay_constant'], + 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'], + constant_period=0) + + notification_iv = comm.BroadcastEvent(camp, "VaccineDistributed") + camp.add(ria) + + one_time_campaign = comm.ScheduledCampaignEvent(camp, + Start_Day=year_to_days(CAMP_START_YEAR), + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=camp_coverage, + Target_Age_Min=0.75, + Target_Age_Max=15 + ) + camp.add(one_time_campaign) + + # add_historical_vax( camp ) + add_historical_vax(campaign, ria_coverage=0.75, camp_coverage=camp_coverage, efficacy=values['efficacy'], + expiration=365 * 6) + + notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") + one_time_campaign = comm.ScheduledCampaignEvent(campaign, + Start_Day=year_to_days(FWD_CAMP_START_YEAR), + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=camp_coverage, + Target_Age_Min=min_age, + Target_Age_Max=125 + ) + campaign.add(one_time_campaign) + return { + "start_day": values['start_day_offset'], + 'efficacy': values['efficacy'], + 'coverage_camp': camp_coverage, + 'expected_expiration': values['expected_expiration'] + } + + +def sweep_config_func(config, values): + config.parameters.Typhoid_Acute_Infectiousness = values['Typhoid_Acute_Infectiousness'] + config.parameters.Typhoid_Exposure_Lambda = values['Typhoid_Exposure_Lambda'] + config.parameters.Typhoid_Environmental_Exposure_Rate = values['Typhoid_Environmental_Exposure_Rate'] + config.parameters.Typhoid_Contact_Exposure_Rate = values['Typhoid_Contact_Exposure_Rate'] + config.parameters.Typhoid_Symptomatic_Fraction = values['Typhoid_Symptomatic_Fraction'] + return {'Typhoid_Acute_Infectiousness': values['Typhoid_Acute_Infectiousness'], + 'Typhoid_Exposure_Lambda': values['Typhoid_Exposure_Lambda'], + 'Typhoid_Environmental_Exposure_Rate': values['Typhoid_Environmental_Exposure_Rate'], + 'Typhoid_Contact_Exposure_Rate': values['Typhoid_Contact_Exposure_Rate'], + 'Typhoid_Symptomatic_Fraction': values['Typhoid_Symptomatic_Fraction']} + + +def get_sweep_builders(sweep_list, sweep_config): + """ + Build simulation builders. + Args: + kwargs: User inputs may overwrite the entries in the block. + + Returns: + lis of Simulation builders + """ + builder = SimulationBuilder() + funcs_list = [[ + ItvFn(add_vax_intervention, ce), + partial(set_param, param='Run_Number', value=x), + CfgFn(sweep_config_func, y) + ] + for ce in sweep_list # for sweep on sweep_list + for x in range(1) # for sweep Run_Number + for y in sweep_config + ] + + builder.add_sweep_definition(sweep_functions, funcs_list) + + return [builder] + + +def run_test(): + # Create a platform + # Show how to dynamically set priority and node_group + platform = Platform("SLURM", node_group="idm_48cores", priority="Highest") + + task = EMODTask.from_default2(config_path="config.json", eradication_path=manifest.eradication_path, + campaign_builder=build_camp, demog_builder=None, schema_path=manifest.schema_file, + param_custom_cb=set_param_fn, ep4_custom_cb=None) + # normally we don't force-set parameters at this point + task.config.parameters.Demographics_Filenames = ["TestDemographics_pak_updated.json"] + task.config.parameters.Death_Rate_Dependence = "NONDISEASE_MORTALITY_BY_YEAR_AND_AGE_FOR_EACH_GENDER" + task.config.parameters.Birth_Rate_Dependence = "INDIVIDUAL_PREGNANCIES_BY_AGE_AND_YEAR" + task.common_assets.add_directory(assets_directory=manifest.assets_input_dir) + task.set_sif(manifest.sif) + # Create simulation sweep with builder + start_day_offset = [1] + vax_effs = [0.9] + expected_expiration = [2*365, 4*365, 6*365] + cov = [0.75] + sweep_list = [] + Typhoid_Acute_Infectiousness = [13000] + Typhoid_Exposure_Lambda = [5] + Typhoid_Environmental_Exposure_Rate = [0.4] + Typhoid_Contact_Exposure_Rate = [0.009] + min_value = 0.05 + max_value = 0.5 + step = 0.05 + my_array = np.linspace(min_value, max_value, num=int((max_value - min_value) / step) + 1) + + Typhoid_Symptomatic_Fraction = [0.06] + sweep_config = [] + combinations_config = list( + itertools.product(Typhoid_Acute_Infectiousness, Typhoid_Exposure_Lambda, Typhoid_Environmental_Exposure_Rate, + Typhoid_Contact_Exposure_Rate, Typhoid_Symptomatic_Fraction)) + + for c in combinations_config: + sweep_config.append({'Typhoid_Acute_Infectiousness': c[0], 'Typhoid_Exposure_Lambda': c[1], + 'Typhoid_Environmental_Exposure_Rate': c[2], 'Typhoid_Contact_Exposure_Rate': c[3], + 'Typhoid_Symptomatic_Fraction': c[4]}) + + combinations = list(itertools.product(start_day_offset, vax_effs, cov, expected_expiration)) + for c in combinations: + sweep_list.append({'start_day_offset': c[0], 'efficacy': c[1], 'coverage': c[2], 'expected_expiration': c[3]}) + builders = get_sweep_builders(sweep_list, sweep_config) + # create TemplatedSimulations from task and builders + ts = TemplatedSimulations(base_task=task, builders=builders) + # create experiment from TemplatedSimulations + experiment = Experiment.from_template(ts, name="test_vax_sweep_configs_Punjab_case3") + # The last step is to call run() on the ExperimentManager to run the simulations. + experiment.run(wait_until_done=True, platform=platform) + # exp_id = '87d7d4eb-3f6a-ee11-92fc-f0921c167864' + # experiment = platform.get_item(exp_id, item_type=ItemType.EXPERIMENT) + task.handle_experiment_completion(experiment) + + # download and plot some stuff. + EMODTask.get_file_from_comps(experiment.uid, ["InsetChart.json", "ReportTyphoidByAgeAndGender.csv"]) + task.cache_experiment_metadata_in_sql(experiment.uid) + import matplotlib + matplotlib.use("TkAgg") + import emod_api.channelreports.plot_icj_means as plotter + chan_data = plotter.collect(str(experiment.uid), "Infected", tag="efficacy=SWEEP") + plotter.display(chan_data, False, "Infected", str(experiment.uid)) + + +if __name__ == "__main__": + import emod_typhoid.bootstrap as dtk + + dtk.setup(manifest.model_dl_dir) + + run_test() diff --git a/examples/vaccination/example_mutil_sweep_Sindh1.py b/examples/vaccination/example_mutil_sweep_Sindh1.py new file mode 100644 index 0000000..42075c5 --- /dev/null +++ b/examples/vaccination/example_mutil_sweep_Sindh1.py @@ -0,0 +1,343 @@ +#!/usr/bin/env python +import itertools + +import numpy as np # just for linspace +from functools import partial + +# idmtools ... +from idmtools.builders import SimulationBuilder +from idmtools.core import ItemType +from idmtools.core.platform_factory import Platform +from idmtools.entities.experiment import Experiment +from idmtools.entities.templated_simulation import TemplatedSimulations + +# emodpy +from emodpy.emod_task import EMODTask + +import emod_api.interventions.common as comm + +import manifest + +from emodpy_typhoid.utility.sweeping import ItvFn, set_param, sweep_functions, CfgFn + +BASE_YEAR = 1990 +SIMULATION_DURATION_IN_YEARS = 40 +CAMP_START_YEAR = 2019.9178 +FWD_CAMP_START_YEAR = 2024.25 + + +def update_sim_bic(simulation, value): + simulation.task.config.parameters.Base_Infectivity_Constant = value * 0.1 + return {"Base_Infectivity": value} + + +def year_to_days(year): + return ((year - BASE_YEAR) * 365) + + +def set_param_fn(config): + config.parameters.Simulation_Type = "TYPHOID_SIM" + config.parameters.Simulation_Duration = SIMULATION_DURATION_IN_YEARS * 365.0 + config.parameters.Base_Individual_Sample_Rate = 1 + + config.parameters.Base_Year = BASE_YEAR + config.parameters.Inset_Chart_Reporting_Start_Year = 1990 + config.parameters.Inset_Chart_Reporting_Stop_Year = 2040 + config.parameters.Enable_Demographics_Reporting = 0 + # config.parameters.Enable_Property_Output = 1 + config.parameters.Report_Event_Recorder_Events = ["VaccineDistributed"] + config.parameters["Listed_Events"] = ["VaccineDistributed"] # old school + + config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 1990 + config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2040 + config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" + config.parameters.Typhoid_3year_Susceptible_Fraction = 0 + config.parameters.Typhoid_6month_Susceptible_Fraction = 0 + config.parameters.Typhoid_6year_Susceptible_Fraction = 0 + config.parameters.Typhoid_Acute_Infectiousness = 13435 + config.parameters.Typhoid_Carrier_Probability = 0.108 + config.parameters.Typhoid_Carrier_Removal_Year = 2500 + config.parameters.Typhoid_Chronic_Relative_Infectiousness = 0.241 + config.parameters.Typhoid_Contact_Exposure_Rate = 0.06918859049226553 + config.parameters.Typhoid_Environmental_Exposure_Rate = 0.06169346985005757 + config.parameters.Typhoid_Environmental_Cutoff_Days = 157.20690133538764 + config.parameters.Typhoid_Environmental_Peak_Start = 355.0579483941714 + config.parameters.Typhoid_Environmental_Ramp_Down_Duration = 112.30224910440123 + config.parameters.Typhoid_Environmental_Ramp_Up_Duration = 39.540475369174146 + config.parameters.Typhoid_Exposure_Lambda = 7.0 + config.parameters.Typhoid_Prepatent_Relative_Infectiousness = 0.5 + config.parameters.Typhoid_Protection_Per_Infection = 0.98 + config.parameters.Typhoid_Subclinical_Relative_Infectiousness = 1 + config.parameters.Typhoid_Symptomatic_Fraction = 0.07 + # config.parameters.x_Birth = 1.2 + + # when using 2018 binary + import emodpy_typhoid.config as config_utils + config_utils.cleanup_for_2018_mode(config) + return config + + +def build_camp(): + """ + Build a campaign input file for the DTK using emod_api. + Right now this function creates the file and returns the filename. If calling code just needs an asset that's fine. + """ + # import emod_api.campaign as camp + # + # print(f"Telling emod-api to use {manifest.schema_file} as schema.") + # camp.set_schema(manifest.schema_file) + # import emod_api.interventions.outbreak as ob + # for x in range(10): + # event = ob.new_intervention(camp, timestep=1 + x, cases=1) + # camp.add(event) + # return camp + import emod_api.campaign as camp + + print(f"Telling emod-api to use {manifest.schema_file} as schema.") + camp.set_schema(manifest.schema_file) + import emod_api.interventions.outbreak as ob + ob.seed(camp, Start_Day=1, Coverage=0.5, Honor_Immunity=False) + ob.seed(camp, Start_Day=365, Coverage=0.005, Tot_Rep=10, Rep_Interval=30, Honor_Immunity=False) + return camp + + +def build_demog(): + """ + Build a demographics input file for the DTK using emod_api. + """ + import emodpy_typhoid.demographics.TyphoidDemographics as Demographics # OK to call into emod-api + + demog = Demographics.from_template_node(lat=0, lon=0, pop=10000, name=1, forced_id=1) + # We're getting all our demographics from a static file overlay. + + """ + # This doesn't work right now but still want to leave in example of what we want to be able to do soon. + demog.AddAgeDependentTransmission( + Age_Bin_Edges_In_Years = [0, 5, 20, 60, -1], + TransmissionMatrix = [ + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0] + ] + ) + """ + return demog + + +# def add_vax_intervention(campaign, values): +# 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) +# ria = tv.new_routine_immunization(campaign, +# efficacy=values['efficacy'], +# decay_constant=0, +# expected_expiration=values['expected_expiration'], +# start_day=year_to_days(CAMP_START_YEAR) + values['start_day_offset'], +# coverage=values['coverage'] +# ) +# +# notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") +# campaign.add(ria) +# # +# tv_iv = tv.new_vax(campaign, +# efficacy=values['efficacy'], +# decay_constant=0, +# constant_period=0, +# expected_expiration = values['expected_expiration'] +# ) +# one_time_campaign = comm.ScheduledCampaignEvent(campaign, +# Start_Day=year_to_days(CAMP_START_YEAR) + values['start_day_offset'], +# Intervention_List=[tv_iv, notification_iv], +# Demographic_Coverage=values['coverage'], +# Target_Age_Min=0.75, +# Target_Age_Max=15 +# ) +# campaign.add(one_time_campaign) +# return {"start_day": values['start_day_offset'], 'efficacy': values['efficacy'], 'coverage': values['coverage'], +# 'expected_expiration': values['expected_expiration']} +def add_vax_intervention(campaign, values, min_age=0.75, max_age=15, binary_immunity=True): + """ + Add 1 or both vaccine interventions: + 1) 'campaign' intervention is a one-time vax to an age-banded segment of the population. + 2) 'ria' intervention is a vax given to infants at 9-months. + + Args: + campaign: Central campaign builder object. + values: Dictionary that helps with sweeping, includes 'coverage', 'efficacy', 'decay_constant', and 'start_day_offset'. + min_age: Minimum age in years for 'campaign'. Can be 0. + max_age: Maximum age in years for 'campaign'. Can be 125. + binary_immunity: Vax efficacy can wane continuosly (False) or drop to 0 all at once (True). + """ + + 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'] + + if binary_immunity: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + expected_expiration=values['expected_expiration'], + constant_period=0) + else: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + decay_constant=values['decay_constant'], + constant_period=0) + + def add_historical_vax( camp, ria_coverage=0.75, camp_coverage=0.75, efficacy=0.8, expiration=365 * 6 ): + import emodpy_typhoid.interventions.typhoid_vaccine as tv + + ria = tv.new_routine_immunization(camp, + efficacy=efficacy, + constant_period=0, + expected_expiration=expiration, + #decay_constant=values['decay_constant'], + 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'], + constant_period=0) + + notification_iv = comm.BroadcastEvent(camp, "VaccineDistributed") + camp.add(ria) + + one_time_campaign = comm.ScheduledCampaignEvent(camp, + Start_Day=year_to_days(CAMP_START_YEAR), + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=camp_coverage, + Target_Age_Min=0.75, + Target_Age_Max=15 + ) + camp.add(one_time_campaign) + + add_historical_vax(campaign, ria_coverage=0.75, camp_coverage=camp_coverage, efficacy=values['efficacy'], + expiration=365 * 6) + notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") + one_time_campaign = comm.ScheduledCampaignEvent(campaign, + Start_Day=year_to_days(FWD_CAMP_START_YEAR), + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=camp_coverage, + Target_Age_Min=min_age, + Target_Age_Max=max_age + ) + campaign.add(one_time_campaign) + return { + "start_day": values['start_day_offset'], + 'efficacy': values['efficacy'], + 'coverage_camp': camp_coverage, + 'expected_expiration': values['expected_expiration'] + } + + +def sweep_config_func(config, values): + config.parameters.Typhoid_Acute_Infectiousness = values['Typhoid_Acute_Infectiousness'] + config.parameters.Typhoid_Exposure_Lambda = values['Typhoid_Exposure_Lambda'] + config.parameters.Typhoid_Environmental_Exposure_Rate = values['Typhoid_Environmental_Exposure_Rate'] + config.parameters.Typhoid_Contact_Exposure_Rate = values['Typhoid_Contact_Exposure_Rate'] + config.parameters.Typhoid_Symptomatic_Fraction = values['Typhoid_Symptomatic_Fraction'] + return {'Typhoid_Acute_Infectiousness': values['Typhoid_Acute_Infectiousness'], + 'Typhoid_Exposure_Lambda': values['Typhoid_Exposure_Lambda'], + 'Typhoid_Environmental_Exposure_Rate': values['Typhoid_Environmental_Exposure_Rate'], + 'Typhoid_Contact_Exposure_Rate': values['Typhoid_Contact_Exposure_Rate'], + 'Typhoid_Symptomatic_Fraction': values['Typhoid_Symptomatic_Fraction']} + + +def get_sweep_builders(sweep_list, sweep_config): + """ + Build simulation builders. + Args: + kwargs: User inputs may overwrite the entries in the block. + + Returns: + lis of Simulation builders + """ + builder = SimulationBuilder() + funcs_list = [[ + ItvFn(add_vax_intervention, ce), + partial(set_param, param='Run_Number', value=x), + CfgFn(sweep_config_func, y) + ] + for ce in sweep_list # for sweep on sweep_list + for x in range(1) # for sweep Run_Number + for y in sweep_config + ] + + builder.add_sweep_definition(sweep_functions, funcs_list) + + return [builder] + + +def run_test(): + # Create a platform + # Show how to dynamically set priority and node_group + platform = Platform("SLURM", node_group="idm_48cores", priority="Highest") + + task = EMODTask.from_default2(config_path="config.json", eradication_path=manifest.eradication_path, + campaign_builder=build_camp, demog_builder=None, schema_path=manifest.schema_file, + param_custom_cb=set_param_fn, ep4_custom_cb=None) + # normally we don't force-set parameters at this point + task.config.parameters.Demographics_Filenames = ["TestDemographics_pak_updated.json"] + task.config.parameters.Death_Rate_Dependence = "NONDISEASE_MORTALITY_BY_YEAR_AND_AGE_FOR_EACH_GENDER" + task.config.parameters.Birth_Rate_Dependence = "INDIVIDUAL_PREGNANCIES_BY_AGE_AND_YEAR" + task.common_assets.add_directory(assets_directory=manifest.assets_input_dir) + task.set_sif(manifest.sif) + # Create simulation sweep with builder + start_day_offset = [1] + vax_effs = [0.9] + expected_expiration = [2*365, 4*365, 6*365] + cov = [0.75] + sweep_list = [] + Typhoid_Acute_Infectiousness = [16000] + Typhoid_Exposure_Lambda = [10] + Typhoid_Environmental_Exposure_Rate = [0.54] + Typhoid_Contact_Exposure_Rate = [1] + min_value = 0.05 + max_value = 0.5 + step = 0.05 + my_array = np.linspace(min_value, max_value, num=int((max_value - min_value) / step) + 1) + + Typhoid_Symptomatic_Fraction = [0.06] + sweep_config = [] + combinations_config = list( + itertools.product(Typhoid_Acute_Infectiousness, Typhoid_Exposure_Lambda, Typhoid_Environmental_Exposure_Rate, + Typhoid_Contact_Exposure_Rate, Typhoid_Symptomatic_Fraction)) + + for c in combinations_config: + sweep_config.append({'Typhoid_Acute_Infectiousness': c[0], 'Typhoid_Exposure_Lambda': c[1], + 'Typhoid_Environmental_Exposure_Rate': c[2], 'Typhoid_Contact_Exposure_Rate': c[3], + 'Typhoid_Symptomatic_Fraction': c[4]}) + + combinations = list(itertools.product(start_day_offset, vax_effs, cov, expected_expiration)) + for c in combinations: + sweep_list.append({'start_day_offset': c[0], 'efficacy': c[1], 'coverage': c[2], 'expected_expiration': c[3]}) + builders = get_sweep_builders(sweep_list, sweep_config) + # create TemplatedSimulations from task and builders + ts = TemplatedSimulations(base_task=task, builders=builders) + # create experiment from TemplatedSimulations + experiment = Experiment.from_template(ts, name="test_vax_sweep_configs_Sindh_case1") + # The last step is to call run() on the ExperimentManager to run the simulations. + experiment.run(wait_until_done=True, platform=platform) + # exp_id = '87d7d4eb-3f6a-ee11-92fc-f0921c167864' + # experiment = platform.get_item(exp_id, item_type=ItemType.EXPERIMENT) + task.handle_experiment_completion(experiment) + + # download and plot some stuff. + EMODTask.get_file_from_comps(experiment.uid, ["InsetChart.json", "ReportTyphoidByAgeAndGender.csv"]) + task.cache_experiment_metadata_in_sql(experiment.uid) + import matplotlib + matplotlib.use("TkAgg") + import emod_api.channelreports.plot_icj_means as plotter + chan_data = plotter.collect(str(experiment.uid), "Infected", tag="efficacy=SWEEP") + plotter.display(chan_data, False, "Infected", str(experiment.uid)) + + +if __name__ == "__main__": + import emod_typhoid.bootstrap as dtk + + dtk.setup(manifest.model_dl_dir) + + run_test() diff --git a/examples/vaccination/example_mutil_sweep_Sindh3.py b/examples/vaccination/example_mutil_sweep_Sindh3.py new file mode 100644 index 0000000..a06bf5e --- /dev/null +++ b/examples/vaccination/example_mutil_sweep_Sindh3.py @@ -0,0 +1,344 @@ +#!/usr/bin/env python +import itertools + +import numpy as np # just for linspace +from functools import partial + +# idmtools ... +from idmtools.builders import SimulationBuilder +from idmtools.core import ItemType +from idmtools.core.platform_factory import Platform +from idmtools.entities.experiment import Experiment +from idmtools.entities.templated_simulation import TemplatedSimulations + +# emodpy +from emodpy.emod_task import EMODTask + +import emod_api.interventions.common as comm + +import manifest + +from emodpy_typhoid.utility.sweeping import ItvFn, set_param, sweep_functions, CfgFn + +BASE_YEAR = 1990 +SIMULATION_DURATION_IN_YEARS = 40 +CAMP_START_YEAR = 2019.9178 +FWD_CAMP_START_YEAR = 2024.25 + + +def update_sim_bic(simulation, value): + simulation.task.config.parameters.Base_Infectivity_Constant = value * 0.1 + return {"Base_Infectivity": value} + + +def year_to_days(year): + return ((year - BASE_YEAR) * 365) + + +def set_param_fn(config): + config.parameters.Simulation_Type = "TYPHOID_SIM" + config.parameters.Simulation_Duration = SIMULATION_DURATION_IN_YEARS * 365.0 + config.parameters.Base_Individual_Sample_Rate = 1 + + config.parameters.Base_Year = BASE_YEAR + config.parameters.Inset_Chart_Reporting_Start_Year = 1990 + config.parameters.Inset_Chart_Reporting_Stop_Year = 2040 + config.parameters.Enable_Demographics_Reporting = 0 + # config.parameters.Enable_Property_Output = 1 + config.parameters.Report_Event_Recorder_Events = ["VaccineDistributed"] + config.parameters["Listed_Events"] = ["VaccineDistributed"] # old school + + config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 1990 + config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2040 + config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" + config.parameters.Typhoid_3year_Susceptible_Fraction = 0 + config.parameters.Typhoid_6month_Susceptible_Fraction = 0 + config.parameters.Typhoid_6year_Susceptible_Fraction = 0 + config.parameters.Typhoid_Acute_Infectiousness = 13435 + config.parameters.Typhoid_Carrier_Probability = 0.108 + config.parameters.Typhoid_Carrier_Removal_Year = 2500 + config.parameters.Typhoid_Chronic_Relative_Infectiousness = 0.241 + config.parameters.Typhoid_Contact_Exposure_Rate = 0.06918859049226553 + config.parameters.Typhoid_Environmental_Exposure_Rate = 0.06169346985005757 + config.parameters.Typhoid_Environmental_Cutoff_Days = 157.20690133538764 + config.parameters.Typhoid_Environmental_Peak_Start = 355.0579483941714 + config.parameters.Typhoid_Environmental_Ramp_Down_Duration = 112.30224910440123 + config.parameters.Typhoid_Environmental_Ramp_Up_Duration = 39.540475369174146 + config.parameters.Typhoid_Exposure_Lambda = 7.0 + config.parameters.Typhoid_Prepatent_Relative_Infectiousness = 0.5 + config.parameters.Typhoid_Protection_Per_Infection = 0.98 + config.parameters.Typhoid_Subclinical_Relative_Infectiousness = 1 + config.parameters.Typhoid_Symptomatic_Fraction = 0.07 + # config.parameters.x_Birth = 1.2 + + # when using 2018 binary + import emodpy_typhoid.config as config_utils + config_utils.cleanup_for_2018_mode(config) + return config + + +def build_camp(): + """ + Build a campaign input file for the DTK using emod_api. + Right now this function creates the file and returns the filename. If calling code just needs an asset that's fine. + """ + # import emod_api.campaign as camp + # + # print(f"Telling emod-api to use {manifest.schema_file} as schema.") + # camp.set_schema(manifest.schema_file) + # import emod_api.interventions.outbreak as ob + # for x in range(10): + # event = ob.new_intervention(camp, timestep=1 + x, cases=1) + # camp.add(event) + # return camp + import emod_api.campaign as camp + + print(f"Telling emod-api to use {manifest.schema_file} as schema.") + camp.set_schema(manifest.schema_file) + import emod_api.interventions.outbreak as ob + ob.seed(camp, Start_Day=1, Coverage=0.5, Honor_Immunity=False) + ob.seed(camp, Start_Day=365, Coverage=0.005, Tot_Rep=10, Rep_Interval=30, Honor_Immunity=False) + return camp + + +def build_demog(): + """ + Build a demographics input file for the DTK using emod_api. + """ + import emodpy_typhoid.demographics.TyphoidDemographics as Demographics # OK to call into emod-api + + demog = Demographics.from_template_node(lat=0, lon=0, pop=10000, name=1, forced_id=1) + # We're getting all our demographics from a static file overlay. + + """ + # This doesn't work right now but still want to leave in example of what we want to be able to do soon. + demog.AddAgeDependentTransmission( + Age_Bin_Edges_In_Years = [0, 5, 20, 60, -1], + TransmissionMatrix = [ + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0] + ] + ) + """ + return demog + + +# def add_vax_intervention(campaign, values): +# 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) +# ria = tv.new_routine_immunization(campaign, +# efficacy=values['efficacy'], +# decay_constant=0, +# expected_expiration=values['expected_expiration'], +# start_day=year_to_days(CAMP_START_YEAR) + values['start_day_offset'], +# coverage=values['coverage'] +# ) +# +# notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") +# campaign.add(ria) +# # +# tv_iv = tv.new_vax(campaign, +# efficacy=values['efficacy'], +# decay_constant=0, +# constant_period=0, +# expected_expiration = values['expected_expiration'] +# ) +# one_time_campaign = comm.ScheduledCampaignEvent(campaign, +# Start_Day=year_to_days(CAMP_START_YEAR) + values['start_day_offset'], +# Intervention_List=[tv_iv, notification_iv], +# Demographic_Coverage=values['coverage'], +# Target_Age_Min=0.75, +# Target_Age_Max=15 +# ) +# campaign.add(one_time_campaign) +# return {"start_day": values['start_day_offset'], 'efficacy': values['efficacy'], 'coverage': values['coverage'], +# 'expected_expiration': values['expected_expiration']} +def add_vax_intervention(campaign, values, min_age=0.75, max_age=15, binary_immunity=True): + """ + Add 1 or both vaccine interventions: + 1) 'campaign' intervention is a one-time vax to an age-banded segment of the population. + 2) 'ria' intervention is a vax given to infants at 9-months. + + Args: + campaign: Central campaign builder object. + values: Dictionary that helps with sweeping, includes 'coverage', 'efficacy', 'decay_constant', and 'start_day_offset'. + min_age: Minimum age in years for 'campaign'. Can be 0. + max_age: Maximum age in years for 'campaign'. Can be 125. + binary_immunity: Vax efficacy can wane continuosly (False) or drop to 0 all at once (True). + """ + + 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'] + + if binary_immunity: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + expected_expiration=values['expected_expiration'], + constant_period=0) + else: + tv_iv = tv.new_vax(campaign, + efficacy=values['efficacy'], + decay_constant=values['decay_constant'], + constant_period=0) + + def add_historical_vax( camp, ria_coverage=0.75, camp_coverage=0.75, efficacy=0.8, expiration=365 * 6 ): + import emodpy_typhoid.interventions.typhoid_vaccine as tv + + ria = tv.new_routine_immunization(camp, + efficacy=efficacy, + constant_period=0, + expected_expiration=expiration, + #decay_constant=values['decay_constant'], + 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'], + constant_period=0) + + notification_iv = comm.BroadcastEvent(camp, "VaccineDistributed") + camp.add(ria) + + one_time_campaign = comm.ScheduledCampaignEvent(camp, + Start_Day=year_to_days(CAMP_START_YEAR), + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=camp_coverage, + Target_Age_Min=0.75, + Target_Age_Max=15 + ) + camp.add(one_time_campaign) + + add_historical_vax(campaign, ria_coverage=0.75, camp_coverage=camp_coverage, efficacy=values['efficacy'], + expiration=365 * 6) + + notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") + one_time_campaign = comm.ScheduledCampaignEvent(campaign, + Start_Day=year_to_days(FWD_CAMP_START_YEAR), + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=camp_coverage, + Target_Age_Min=min_age, + Target_Age_Max=125 + ) + campaign.add(one_time_campaign) + return { + "start_day": values['start_day_offset'], + 'efficacy': values['efficacy'], + 'coverage_camp': camp_coverage, + 'expected_expiration': values['expected_expiration'] + } + + +def sweep_config_func(config, values): + config.parameters.Typhoid_Acute_Infectiousness = values['Typhoid_Acute_Infectiousness'] + config.parameters.Typhoid_Exposure_Lambda = values['Typhoid_Exposure_Lambda'] + config.parameters.Typhoid_Environmental_Exposure_Rate = values['Typhoid_Environmental_Exposure_Rate'] + config.parameters.Typhoid_Contact_Exposure_Rate = values['Typhoid_Contact_Exposure_Rate'] + config.parameters.Typhoid_Symptomatic_Fraction = values['Typhoid_Symptomatic_Fraction'] + return {'Typhoid_Acute_Infectiousness': values['Typhoid_Acute_Infectiousness'], + 'Typhoid_Exposure_Lambda': values['Typhoid_Exposure_Lambda'], + 'Typhoid_Environmental_Exposure_Rate': values['Typhoid_Environmental_Exposure_Rate'], + 'Typhoid_Contact_Exposure_Rate': values['Typhoid_Contact_Exposure_Rate'], + 'Typhoid_Symptomatic_Fraction': values['Typhoid_Symptomatic_Fraction']} + + +def get_sweep_builders(sweep_list, sweep_config): + """ + Build simulation builders. + Args: + kwargs: User inputs may overwrite the entries in the block. + + Returns: + lis of Simulation builders + """ + builder = SimulationBuilder() + funcs_list = [[ + ItvFn(add_vax_intervention, ce), + partial(set_param, param='Run_Number', value=x), + CfgFn(sweep_config_func, y) + ] + for ce in sweep_list # for sweep on sweep_list + for x in range(1) # for sweep Run_Number + for y in sweep_config + ] + + builder.add_sweep_definition(sweep_functions, funcs_list) + + return [builder] + + +def run_test(): + # Create a platform + # Show how to dynamically set priority and node_group + platform = Platform("SLURM", node_group="idm_48cores", priority="Highest") + + task = EMODTask.from_default2(config_path="config.json", eradication_path=manifest.eradication_path, + campaign_builder=build_camp, demog_builder=None, schema_path=manifest.schema_file, + param_custom_cb=set_param_fn, ep4_custom_cb=None) + # normally we don't force-set parameters at this point + task.config.parameters.Demographics_Filenames = ["TestDemographics_pak_updated.json"] + task.config.parameters.Death_Rate_Dependence = "NONDISEASE_MORTALITY_BY_YEAR_AND_AGE_FOR_EACH_GENDER" + task.config.parameters.Birth_Rate_Dependence = "INDIVIDUAL_PREGNANCIES_BY_AGE_AND_YEAR" + task.common_assets.add_directory(assets_directory=manifest.assets_input_dir) + task.set_sif(manifest.sif) + # Create simulation sweep with builder + start_day_offset = [1] + vax_effs = [0.9] + expected_expiration = [2*365, 4*365, 6*365] + cov = [0.75] + sweep_list = [] + Typhoid_Acute_Infectiousness = [16000] + Typhoid_Exposure_Lambda = [10] + Typhoid_Environmental_Exposure_Rate = [0.54] + Typhoid_Contact_Exposure_Rate = [1] + min_value = 0.05 + max_value = 0.5 + step = 0.05 + my_array = np.linspace(min_value, max_value, num=int((max_value - min_value) / step) + 1) + + Typhoid_Symptomatic_Fraction = [0.06] + sweep_config = [] + combinations_config = list( + itertools.product(Typhoid_Acute_Infectiousness, Typhoid_Exposure_Lambda, Typhoid_Environmental_Exposure_Rate, + Typhoid_Contact_Exposure_Rate, Typhoid_Symptomatic_Fraction)) + + for c in combinations_config: + sweep_config.append({'Typhoid_Acute_Infectiousness': c[0], 'Typhoid_Exposure_Lambda': c[1], + 'Typhoid_Environmental_Exposure_Rate': c[2], 'Typhoid_Contact_Exposure_Rate': c[3], + 'Typhoid_Symptomatic_Fraction': c[4]}) + + combinations = list(itertools.product(start_day_offset, vax_effs, cov, expected_expiration)) + for c in combinations: + sweep_list.append({'start_day_offset': c[0], 'efficacy': c[1], 'coverage': c[2], 'expected_expiration': c[3]}) + builders = get_sweep_builders(sweep_list, sweep_config) + # create TemplatedSimulations from task and builders + ts = TemplatedSimulations(base_task=task, builders=builders) + # create experiment from TemplatedSimulations + experiment = Experiment.from_template(ts, name="test_vax_sweep_configs_Sindh_case3") + # The last step is to call run() on the ExperimentManager to run the simulations. + experiment.run(wait_until_done=True, platform=platform) + # exp_id = '87d7d4eb-3f6a-ee11-92fc-f0921c167864' + # experiment = platform.get_item(exp_id, item_type=ItemType.EXPERIMENT) + task.handle_experiment_completion(experiment) + + # download and plot some stuff. + EMODTask.get_file_from_comps(experiment.uid, ["InsetChart.json", "ReportTyphoidByAgeAndGender.csv"]) + task.cache_experiment_metadata_in_sql(experiment.uid) + import matplotlib + matplotlib.use("TkAgg") + import emod_api.channelreports.plot_icj_means as plotter + chan_data = plotter.collect(str(experiment.uid), "Infected", tag="efficacy=SWEEP") + plotter.display(chan_data, False, "Infected", str(experiment.uid)) + + +if __name__ == "__main__": + import emod_typhoid.bootstrap as dtk + + dtk.setup(manifest.model_dl_dir) + + run_test() diff --git a/examples/vaccination/requirements.txt b/examples/vaccination/requirements.txt index f6ada09..5b99404 100644 --- a/examples/vaccination/requirements.txt +++ b/examples/vaccination/requirements.txt @@ -1,4 +1,4 @@ -emodpy-typhoid==0.0.5.dev2 +emodpy-typhoid==0.0.5 emodpy==1.22.0.dev3 emod-api==1.31.0.dev0 emod-typhoid==0.0.4.dev2 diff --git a/examples/vax_efficacy_sweep/requirements.txt b/examples/vax_efficacy_sweep/requirements.txt index a75e372..73d4c93 100644 --- a/examples/vax_efficacy_sweep/requirements.txt +++ b/examples/vax_efficacy_sweep/requirements.txt @@ -1,5 +1,5 @@ -i https://packages.idmod.org/api/pypi/pypi-production/simple emodpy-typhoid==0.0.6.dev0 emodpy==1.22.0.dev3 -emod-api==1.31.0.dev0 -emod-typhoid==0.0.4.dev2 +emod-api==1.31.0.dev1 +emod-typhoid==0.0.4.dev5 diff --git a/requirements.txt b/requirements.txt index 25bfa20..3946a35 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ -emodpy~=1.16 -emod-typhoid +emodpy==1.22.0.dev3 +emod-api==1.31.0.dev1 +emod-typhoid==0.0.5 diff --git a/requirements_2018.txt b/requirements_2018.txt deleted file mode 100644 index dbcc932..0000000 --- a/requirements_2018.txt +++ /dev/null @@ -1,3 +0,0 @@ -emodpy==1.22.0.dev3 -emod-api==1.31.0.dev1 -emod-typhoid==0.0.4.dev3 diff --git a/requirements_future.txt b/requirements_future.txt new file mode 100644 index 0000000..25bfa20 --- /dev/null +++ b/requirements_future.txt @@ -0,0 +1,2 @@ +emodpy~=1.16 +emod-typhoid diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..f1c2d6e --- /dev/null +++ b/tests/README.md @@ -0,0 +1,27 @@ +# emodpy-typhoid tests + +## How to run these tests after you have emodpy-typhoid installed in a virtual environment. + +### prerequisites: +Active your virtual environment + +Install pytest: `pip install pytest` + +Install idm-test: `pip install idm-test --index-url=https://packages.idmod.org/api/pypi/pypi-production/simple` + +Login to comps2: `python .dev_scripts/create_auth_token_args.py -u youremail@idmod.org -p password` + +### Run unit tests: +`cd tests/unitests` + +`py.test -sv --junitxml=reports/test_results.xml` + +### Run integration tests: +`cd tests/workflow_tests` + +`py.test -sv --junitxml=reports/test_results.xml` + +### Run sft tests: +`cd tests\sft_tests` + +`python run_all_sft_tests.py` \ No newline at end of file diff --git a/tests/sft_tests/.gitignore b/tests/sft_tests/.gitignore new file mode 100644 index 0000000..3bb1c10 --- /dev/null +++ b/tests/sft_tests/.gitignore @@ -0,0 +1,4 @@ +**/COMPS_ID +**/sif/ +**/test_results.xml +**/demographics.json \ No newline at end of file diff --git a/tests/sft_tests/Assets/TestDemographics_pak_updated.json b/tests/sft_tests/Assets/TestDemographics_pak_updated.json new file mode 100644 index 0000000..588bfdf --- /dev/null +++ b/tests/sft_tests/Assets/TestDemographics_pak_updated.json @@ -0,0 +1,9716 @@ +{ + "Metadata": { + "DateCreated": "Sun Sep 25 23:19:55 2011", + "Tool": "not_convertdemog.py", + "Author": "jgauld", + "IdReference": "Gridded world grump2.5arcmin", + "NodeCount": 1 + }, + "Nodes": [ + { + "NodeID": 1, + "NodeAttributes": { + "Latitude": -8.5, + "Longitude": 36.5, + "Altitude": 0, + "Airport": 0, + "Region": 1, + "Seaport": 0, + "InitialPopulation": 16000, + "BirthRate": 0 + }, + "IndividualAttributes": { + "AgeDistributionFlag": 3, + "AgeDistribution1": 0.0001, + "AgeDistribution2": 0, + "PrevalenceDistributionFlag": 0, + "PrevalenceDistribution1": 0, + "PrevalenceDistribution2": 0, + "ImmunityDistributionFlag": 0, + "ImmunityDistribution1": 1, + "ImmunityDistribution2": 0, + "RiskDistributionFlag": 0, + "RiskDistribution1": 1, + "RiskDistribution2": 0, + "MigrationHeterogeneityDistributionFlag": 0, + "MigrationHeterogeneityDistribution1": 1, + "MigrationHeterogeneityDistribution2": 0, + "AgeDistribution": { + "NumDistributionAxes": 0, + "ResultUnits": "years", + "ResultScaleFactor": 365, + "ResultValues": [ + 0, + 5, + 10, + 15, + 20, + 25, + 30, + 35, + 40, + 45, + 50, + 55, + 60, + 65, + 70, + 75, + 80, + 85, + 90, + 95, + 100, + 120 + ], + "DistributionValues": [ + 0, + 0.1264, + 0.2502, + 0.3698, + 0.4808, + 0.5786, + 0.6615, + 0.7355, + 0.7981, + 0.8451, + 0.8803, + 0.9108, + 0.938, + 0.9594, + 0.9754, + 0.9868, + 0.994, + 0.9979, + 0.9995, + 0.9999, + 1, + 1 + ] + }, + "MortalityDistributionMale": { + "NumDistributionAxes": 2, + "AxisNames": [ + "age", + "year" + ], + "AxisUnits": [ + "years", + "simulation year" + ], + "AxisScaleFactors": [ + 365, + 1 + ], + "NumPopulationGroups": [ + 44, + 72 + ], + "PopulationGroups": [ + [ + 0, + 0.999, + 1, + 4.999, + 5, + 9.999, + 10, + 14.999, + 15, + 19.999, + 20, + 24.999, + 25, + 29.999, + 30, + 34.999, + 35, + 39.999, + 40, + 44.999, + 45, + 49.999, + 50, + 54.999, + 55, + 59.999, + 60, + 64.999, + 65, + 69.999, + 70, + 74.999, + 75, + 79.999, + 80, + 84.999, + 85, + 89.999, + 90, + 94.999, + 95, + 99.999, + 100, + 100 + ], + [ + 1950, + 1951, + 1952, + 1953, + 1954, + 1955, + 1956, + 1957, + 1958, + 1959, + 1960, + 1961, + 1962, + 1963, + 1964, + 1965, + 1966, + 1967, + 1968, + 1969, + 1970, + 1971, + 1972, + 1973, + 1974, + 1975, + 1976, + 1977, + 1978, + 1979, + 1980, + 1981, + 1982, + 1983, + 1984, + 1985, + 1986, + 1987, + 1988, + 1989, + 1990, + 1991, + 1992, + 1993, + 1994, + 1995, + 1996, + 1997, + 1998, + 1999, + 2000, + 2001, + 2002, + 2003, + 2004, + 2005, + 2006, + 2007, + 2008, + 2009, + 2010, + 2011, + 2012, + 2013, + 2014, + 2015, + 2016, + 2017, + 2018, + 2019, + 2020, + 2021 + ] + ], + "ResultUnits": "Mortality rate in units of 1/year", + "ResultScaleFactor": 0.0027, + "ResultValues": [ + [ + 0.3263, + 0.3106, + 0.2962, + 0.283, + 0.2705, + 0.2587, + 0.2478, + 0.2374, + 0.2278, + 0.2188, + 0.2105, + 0.2025, + 0.1953, + 0.1884, + 0.1823, + 0.1776, + 0.1721, + 0.168, + 0.1644, + 0.1611, + 0.158, + 0.1624, + 0.1527, + 0.1505, + 0.1484, + 0.1464, + 0.1447, + 0.143, + 0.1414, + 0.1397, + 0.1381, + 0.1365, + 0.1349, + 0.1332, + 0.1313, + 0.1294, + 0.1274, + 0.1254, + 0.1233, + 0.1211, + 0.1188, + 0.1166, + 0.1144, + 0.1118, + 0.1092, + 0.1066, + 0.104, + 0.1014, + 0.0989, + 0.0963, + 0.094, + 0.0919, + 0.09, + 0.0882, + 0.0867, + 0.0864, + 0.084, + 0.0826, + 0.0812, + 0.0798, + 0.0783, + 0.0764, + 0.0746, + 0.0729, + 0.071, + 0.0691, + 0.0672, + 0.0654, + 0.0636, + 0.0618, + 0.06, + 0.0583 + ], + [ + 0.3263, + 0.3106, + 0.2962, + 0.283, + 0.2705, + 0.2587, + 0.2478, + 0.2374, + 0.2278, + 0.2188, + 0.2105, + 0.2025, + 0.1953, + 0.1884, + 0.1823, + 0.1776, + 0.1721, + 0.168, + 0.1644, + 0.1611, + 0.158, + 0.1624, + 0.1527, + 0.1505, + 0.1484, + 0.1464, + 0.1447, + 0.143, + 0.1414, + 0.1397, + 0.1381, + 0.1365, + 0.1349, + 0.1332, + 0.1313, + 0.1294, + 0.1274, + 0.1254, + 0.1233, + 0.1211, + 0.1188, + 0.1166, + 0.1144, + 0.1118, + 0.1092, + 0.1066, + 0.104, + 0.1014, + 0.0989, + 0.0963, + 0.094, + 0.0919, + 0.09, + 0.0882, + 0.0867, + 0.0864, + 0.084, + 0.0826, + 0.0812, + 0.0798, + 0.0783, + 0.0764, + 0.0746, + 0.0729, + 0.071, + 0.0691, + 0.0672, + 0.0654, + 0.0636, + 0.0618, + 0.06, + 0.0583 + ], + [ + 0.0453, + 0.0417, + 0.0388, + 0.0361, + 0.0336, + 0.0313, + 0.0293, + 0.0274, + 0.0258, + 0.0242, + 0.0228, + 0.0215, + 0.0203, + 0.0193, + 0.0184, + 0.0179, + 0.0168, + 0.0162, + 0.0156, + 0.0152, + 0.0147, + 0.0174, + 0.014, + 0.0137, + 0.0134, + 0.0131, + 0.0129, + 0.0126, + 0.0124, + 0.0122, + 0.012, + 0.0118, + 0.0116, + 0.0113, + 0.0111, + 0.0108, + 0.0106, + 0.0103, + 0.01, + 0.0098, + 0.0095, + 0.0092, + 0.009, + 0.0087, + 0.0083, + 0.008, + 0.0077, + 0.0074, + 0.0071, + 0.0068, + 0.0066, + 0.0064, + 0.0062, + 0.006, + 0.0058, + 0.0062, + 0.0055, + 0.0054, + 0.0052, + 0.0051, + 0.005, + 0.0048, + 0.0046, + 0.0045, + 0.0043, + 0.0041, + 0.0039, + 0.0038, + 0.0036, + 0.0035, + 0.0033, + 0.0032 + ], + [ + 0.0453, + 0.0417, + 0.0388, + 0.0361, + 0.0336, + 0.0313, + 0.0293, + 0.0274, + 0.0258, + 0.0242, + 0.0228, + 0.0215, + 0.0203, + 0.0193, + 0.0184, + 0.0179, + 0.0168, + 0.0162, + 0.0156, + 0.0152, + 0.0147, + 0.0174, + 0.014, + 0.0137, + 0.0134, + 0.0131, + 0.0129, + 0.0126, + 0.0124, + 0.0122, + 0.012, + 0.0118, + 0.0116, + 0.0113, + 0.0111, + 0.0108, + 0.0106, + 0.0103, + 0.01, + 0.0098, + 0.0095, + 0.0092, + 0.009, + 0.0087, + 0.0083, + 0.008, + 0.0077, + 0.0074, + 0.0071, + 0.0068, + 0.0066, + 0.0064, + 0.0062, + 0.006, + 0.0058, + 0.0062, + 0.0055, + 0.0054, + 0.0052, + 0.0051, + 0.005, + 0.0048, + 0.0046, + 0.0045, + 0.0043, + 0.0041, + 0.0039, + 0.0038, + 0.0036, + 0.0035, + 0.0033, + 0.0032 + ], + [ + 0.0046, + 0.0044, + 0.0043, + 0.0041, + 0.004, + 0.0039, + 0.0038, + 0.0036, + 0.0035, + 0.0034, + 0.0033, + 0.0031, + 0.003, + 0.0028, + 0.0027, + 0.0028, + 0.0022, + 0.002, + 0.0018, + 0.0017, + 0.0015, + 0.0026, + 0.0015, + 0.0015, + 0.0016, + 0.0015, + 0.0015, + 0.0016, + 0.0015, + 0.0015, + 0.0014, + 0.0014, + 0.0014, + 0.0013, + 0.0012, + 0.0012, + 0.0011, + 0.0012, + 0.0013, + 0.0013, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0014, + 0.0013, + 0.0014, + 0.0013, + 0.0013, + 0.0012, + 0.0012, + 0.0011, + 0.0012, + 0.0012, + 0.0015, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0011, + 0.0011, + 0.0011, + 0.001, + 0.001, + 0.001, + 0.001 + ], + [ + 0.0046, + 0.0044, + 0.0043, + 0.0041, + 0.004, + 0.0039, + 0.0038, + 0.0036, + 0.0035, + 0.0034, + 0.0033, + 0.0031, + 0.003, + 0.0028, + 0.0027, + 0.0028, + 0.0022, + 0.002, + 0.0018, + 0.0017, + 0.0015, + 0.0026, + 0.0015, + 0.0015, + 0.0016, + 0.0015, + 0.0015, + 0.0016, + 0.0015, + 0.0015, + 0.0014, + 0.0014, + 0.0014, + 0.0013, + 0.0012, + 0.0012, + 0.0011, + 0.0012, + 0.0013, + 0.0013, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0014, + 0.0013, + 0.0014, + 0.0013, + 0.0013, + 0.0012, + 0.0012, + 0.0011, + 0.0012, + 0.0012, + 0.0015, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0012, + 0.0011, + 0.0011, + 0.0011, + 0.001, + 0.001, + 0.001, + 0.001 + ], + [ + 0.0031, + 0.0029, + 0.0028, + 0.0028, + 0.0027, + 0.0026, + 0.0026, + 0.0024, + 0.0024, + 0.0023, + 0.0022, + 0.0022, + 0.0021, + 0.002, + 0.0019, + 0.0019, + 0.0016, + 0.0014, + 0.0013, + 0.0012, + 0.001, + 0.0018, + 0.001, + 0.0011, + 0.0011, + 0.001, + 0.0011, + 0.0011, + 0.0011, + 0.001, + 0.001, + 0.001, + 0.0009, + 0.0009, + 0.0009, + 0.0008, + 0.0008, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0008, + 0.0008, + 0.0009, + 0.0009, + 0.001, + 0.001, + 0.001, + 0.001, + 0.0009, + 0.0009, + 0.0009, + 0.0008, + 0.0008, + 0.0009, + 0.0012, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0008, + 0.0008, + 0.0008, + 0.0008, + 0.0008, + 0.0008, + 0.0008 + ], + [ + 0.0031, + 0.0029, + 0.0028, + 0.0028, + 0.0027, + 0.0026, + 0.0026, + 0.0024, + 0.0024, + 0.0023, + 0.0022, + 0.0022, + 0.0021, + 0.002, + 0.0019, + 0.0019, + 0.0016, + 0.0014, + 0.0013, + 0.0012, + 0.001, + 0.0018, + 0.001, + 0.0011, + 0.0011, + 0.001, + 0.0011, + 0.0011, + 0.0011, + 0.001, + 0.001, + 0.001, + 0.0009, + 0.0009, + 0.0009, + 0.0008, + 0.0008, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0008, + 0.0008, + 0.0009, + 0.0009, + 0.001, + 0.001, + 0.001, + 0.001, + 0.0009, + 0.0009, + 0.0009, + 0.0008, + 0.0008, + 0.0009, + 0.0012, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0009, + 0.0008, + 0.0008, + 0.0008, + 0.0008, + 0.0008, + 0.0008, + 0.0008 + ], + [ + 0.0045, + 0.0044, + 0.0042, + 0.0041, + 0.0041, + 0.0039, + 0.0038, + 0.0037, + 0.0036, + 0.0035, + 0.0034, + 0.0033, + 0.0032, + 0.0031, + 0.0029, + 0.0028, + 0.0025, + 0.0023, + 0.0021, + 0.0019, + 0.0018, + 0.0041, + 0.0018, + 0.0018, + 0.0019, + 0.0018, + 0.0018, + 0.0019, + 0.0018, + 0.0018, + 0.0017, + 0.0017, + 0.0016, + 0.0016, + 0.0015, + 0.0014, + 0.0014, + 0.0015, + 0.0016, + 0.0016, + 0.0016, + 0.0015, + 0.0015, + 0.0016, + 0.0016, + 0.0017, + 0.0017, + 0.0017, + 0.0017, + 0.0016, + 0.0015, + 0.0015, + 0.0015, + 0.0015, + 0.0015, + 0.0018, + 0.0015, + 0.0016, + 0.0016, + 0.0016, + 0.0016, + 0.0016, + 0.0016, + 0.0015, + 0.0016, + 0.0015, + 0.0015, + 0.0014, + 0.0014, + 0.0014, + 0.0012, + 0.0013 + ], + [ + 0.0045, + 0.0044, + 0.0042, + 0.0041, + 0.0041, + 0.0039, + 0.0038, + 0.0037, + 0.0036, + 0.0035, + 0.0034, + 0.0033, + 0.0032, + 0.0031, + 0.0029, + 0.0028, + 0.0025, + 0.0023, + 0.0021, + 0.0019, + 0.0018, + 0.0041, + 0.0018, + 0.0018, + 0.0019, + 0.0018, + 0.0018, + 0.0019, + 0.0018, + 0.0018, + 0.0017, + 0.0017, + 0.0016, + 0.0016, + 0.0015, + 0.0014, + 0.0014, + 0.0015, + 0.0016, + 0.0016, + 0.0016, + 0.0015, + 0.0015, + 0.0016, + 0.0016, + 0.0017, + 0.0017, + 0.0017, + 0.0017, + 0.0016, + 0.0015, + 0.0015, + 0.0015, + 0.0015, + 0.0015, + 0.0018, + 0.0015, + 0.0016, + 0.0016, + 0.0016, + 0.0016, + 0.0016, + 0.0016, + 0.0015, + 0.0016, + 0.0015, + 0.0015, + 0.0014, + 0.0014, + 0.0014, + 0.0012, + 0.0013 + ], + [ + 0.0065, + 0.0062, + 0.0061, + 0.0059, + 0.0058, + 0.0056, + 0.0055, + 0.0053, + 0.0052, + 0.005, + 0.0049, + 0.0047, + 0.0045, + 0.0044, + 0.0041, + 0.0039, + 0.0035, + 0.0032, + 0.003, + 0.0028, + 0.0025, + 0.0072, + 0.0025, + 0.0026, + 0.0027, + 0.0027, + 0.0027, + 0.0028, + 0.0026, + 0.0025, + 0.0024, + 0.0024, + 0.0023, + 0.0022, + 0.0022, + 0.002, + 0.002, + 0.0022, + 0.0022, + 0.0022, + 0.0022, + 0.0021, + 0.0021, + 0.0022, + 0.0023, + 0.0025, + 0.0024, + 0.0024, + 0.0024, + 0.0023, + 0.0022, + 0.0022, + 0.0021, + 0.0021, + 0.0022, + 0.0024, + 0.0022, + 0.0023, + 0.0024, + 0.0024, + 0.0023, + 0.0023, + 0.0023, + 0.0022, + 0.0022, + 0.0022, + 0.0021, + 0.002, + 0.002, + 0.002, + 0.0017, + 0.0018 + ], + [ + 0.0065, + 0.0062, + 0.0061, + 0.0059, + 0.0058, + 0.0056, + 0.0055, + 0.0053, + 0.0052, + 0.005, + 0.0049, + 0.0047, + 0.0045, + 0.0044, + 0.0041, + 0.0039, + 0.0035, + 0.0032, + 0.003, + 0.0028, + 0.0025, + 0.0072, + 0.0025, + 0.0026, + 0.0027, + 0.0027, + 0.0027, + 0.0028, + 0.0026, + 0.0025, + 0.0024, + 0.0024, + 0.0023, + 0.0022, + 0.0022, + 0.002, + 0.002, + 0.0022, + 0.0022, + 0.0022, + 0.0022, + 0.0021, + 0.0021, + 0.0022, + 0.0023, + 0.0025, + 0.0024, + 0.0024, + 0.0024, + 0.0023, + 0.0022, + 0.0022, + 0.0021, + 0.0021, + 0.0022, + 0.0024, + 0.0022, + 0.0023, + 0.0024, + 0.0024, + 0.0023, + 0.0023, + 0.0023, + 0.0022, + 0.0022, + 0.0022, + 0.0021, + 0.002, + 0.002, + 0.002, + 0.0017, + 0.0018 + ], + [ + 0.0071, + 0.0068, + 0.0067, + 0.0064, + 0.0064, + 0.0061, + 0.006, + 0.0058, + 0.0056, + 0.0054, + 0.0053, + 0.0051, + 0.0049, + 0.0047, + 0.0044, + 0.0042, + 0.0037, + 0.0034, + 0.0031, + 0.0028, + 0.0026, + 0.0074, + 0.0026, + 0.0026, + 0.0028, + 0.0028, + 0.0028, + 0.0028, + 0.0026, + 0.0026, + 0.0025, + 0.0024, + 0.0024, + 0.0023, + 0.0022, + 0.0021, + 0.002, + 0.0022, + 0.0023, + 0.0023, + 0.0023, + 0.0022, + 0.0022, + 0.0022, + 0.0023, + 0.0025, + 0.0024, + 0.0025, + 0.0024, + 0.0023, + 0.0022, + 0.0022, + 0.0021, + 0.0022, + 0.0022, + 0.0024, + 0.0022, + 0.0024, + 0.0025, + 0.0024, + 0.0024, + 0.0023, + 0.0023, + 0.0023, + 0.0023, + 0.0022, + 0.0022, + 0.0021, + 0.0021, + 0.002, + 0.0017, + 0.0018 + ], + [ + 0.0071, + 0.0068, + 0.0067, + 0.0064, + 0.0064, + 0.0061, + 0.006, + 0.0058, + 0.0056, + 0.0054, + 0.0053, + 0.0051, + 0.0049, + 0.0047, + 0.0044, + 0.0042, + 0.0037, + 0.0034, + 0.0031, + 0.0028, + 0.0026, + 0.0074, + 0.0026, + 0.0026, + 0.0028, + 0.0028, + 0.0028, + 0.0028, + 0.0026, + 0.0026, + 0.0025, + 0.0024, + 0.0024, + 0.0023, + 0.0022, + 0.0021, + 0.002, + 0.0022, + 0.0023, + 0.0023, + 0.0023, + 0.0022, + 0.0022, + 0.0022, + 0.0023, + 0.0025, + 0.0024, + 0.0025, + 0.0024, + 0.0023, + 0.0022, + 0.0022, + 0.0021, + 0.0022, + 0.0022, + 0.0024, + 0.0022, + 0.0024, + 0.0025, + 0.0024, + 0.0024, + 0.0023, + 0.0023, + 0.0023, + 0.0023, + 0.0022, + 0.0022, + 0.0021, + 0.0021, + 0.002, + 0.0017, + 0.0018 + ], + [ + 0.0081, + 0.0078, + 0.0076, + 0.0074, + 0.0073, + 0.007, + 0.0068, + 0.0066, + 0.0064, + 0.0062, + 0.006, + 0.0058, + 0.0056, + 0.0053, + 0.005, + 0.0047, + 0.0042, + 0.0038, + 0.0035, + 0.0032, + 0.0029, + 0.0071, + 0.0029, + 0.003, + 0.0031, + 0.0031, + 0.0031, + 0.0032, + 0.003, + 0.0029, + 0.0028, + 0.0027, + 0.0027, + 0.0025, + 0.0025, + 0.0023, + 0.0023, + 0.0025, + 0.0026, + 0.0026, + 0.0025, + 0.0024, + 0.0024, + 0.0025, + 0.0026, + 0.0028, + 0.0027, + 0.0028, + 0.0027, + 0.0026, + 0.0025, + 0.0024, + 0.0024, + 0.0024, + 0.0024, + 0.0027, + 0.0025, + 0.0026, + 0.0027, + 0.0027, + 0.0026, + 0.0026, + 0.0026, + 0.0025, + 0.0025, + 0.0024, + 0.0024, + 0.0023, + 0.0023, + 0.0022, + 0.002, + 0.0021 + ], + [ + 0.0081, + 0.0078, + 0.0076, + 0.0074, + 0.0073, + 0.007, + 0.0068, + 0.0066, + 0.0064, + 0.0062, + 0.006, + 0.0058, + 0.0056, + 0.0053, + 0.005, + 0.0047, + 0.0042, + 0.0038, + 0.0035, + 0.0032, + 0.0029, + 0.0071, + 0.0029, + 0.003, + 0.0031, + 0.0031, + 0.0031, + 0.0032, + 0.003, + 0.0029, + 0.0028, + 0.0027, + 0.0027, + 0.0025, + 0.0025, + 0.0023, + 0.0023, + 0.0025, + 0.0026, + 0.0026, + 0.0025, + 0.0024, + 0.0024, + 0.0025, + 0.0026, + 0.0028, + 0.0027, + 0.0028, + 0.0027, + 0.0026, + 0.0025, + 0.0024, + 0.0024, + 0.0024, + 0.0024, + 0.0027, + 0.0025, + 0.0026, + 0.0027, + 0.0027, + 0.0026, + 0.0026, + 0.0026, + 0.0025, + 0.0025, + 0.0024, + 0.0024, + 0.0023, + 0.0023, + 0.0022, + 0.002, + 0.0021 + ], + [ + 0.0097, + 0.0094, + 0.0092, + 0.0088, + 0.0087, + 0.0084, + 0.0082, + 0.0079, + 0.0077, + 0.0075, + 0.0073, + 0.007, + 0.0068, + 0.0065, + 0.0061, + 0.0058, + 0.0052, + 0.0048, + 0.0044, + 0.004, + 0.0037, + 0.0078, + 0.0037, + 0.0038, + 0.0039, + 0.0038, + 0.0038, + 0.004, + 0.0037, + 0.0037, + 0.0036, + 0.0035, + 0.0034, + 0.0032, + 0.0032, + 0.003, + 0.0029, + 0.0032, + 0.0033, + 0.0033, + 0.0032, + 0.0031, + 0.0031, + 0.0032, + 0.0033, + 0.0036, + 0.0035, + 0.0036, + 0.0035, + 0.0033, + 0.0032, + 0.0032, + 0.0031, + 0.0031, + 0.0032, + 0.0034, + 0.0032, + 0.0033, + 0.0034, + 0.0034, + 0.0033, + 0.0033, + 0.0033, + 0.0032, + 0.0032, + 0.0031, + 0.0031, + 0.003, + 0.003, + 0.0028, + 0.0028, + 0.0029 + ], + [ + 0.0097, + 0.0094, + 0.0092, + 0.0088, + 0.0087, + 0.0084, + 0.0082, + 0.0079, + 0.0077, + 0.0075, + 0.0073, + 0.007, + 0.0068, + 0.0065, + 0.0061, + 0.0058, + 0.0052, + 0.0048, + 0.0044, + 0.004, + 0.0037, + 0.0078, + 0.0037, + 0.0038, + 0.0039, + 0.0038, + 0.0038, + 0.004, + 0.0037, + 0.0037, + 0.0036, + 0.0035, + 0.0034, + 0.0032, + 0.0032, + 0.003, + 0.0029, + 0.0032, + 0.0033, + 0.0033, + 0.0032, + 0.0031, + 0.0031, + 0.0032, + 0.0033, + 0.0036, + 0.0035, + 0.0036, + 0.0035, + 0.0033, + 0.0032, + 0.0032, + 0.0031, + 0.0031, + 0.0032, + 0.0034, + 0.0032, + 0.0033, + 0.0034, + 0.0034, + 0.0033, + 0.0033, + 0.0033, + 0.0032, + 0.0032, + 0.0031, + 0.0031, + 0.003, + 0.003, + 0.0028, + 0.0028, + 0.0029 + ], + [ + 0.0121, + 0.0117, + 0.0114, + 0.0111, + 0.0109, + 0.0106, + 0.0104, + 0.01, + 0.0098, + 0.0096, + 0.0093, + 0.009, + 0.0087, + 0.0084, + 0.008, + 0.0076, + 0.0069, + 0.0064, + 0.0059, + 0.0055, + 0.0051, + 0.009, + 0.0051, + 0.0051, + 0.0053, + 0.0052, + 0.0052, + 0.0054, + 0.0051, + 0.0051, + 0.0049, + 0.0048, + 0.0047, + 0.0046, + 0.0045, + 0.0042, + 0.0041, + 0.0045, + 0.0046, + 0.0046, + 0.0045, + 0.0044, + 0.0044, + 0.0045, + 0.0046, + 0.0049, + 0.0048, + 0.005, + 0.0048, + 0.0046, + 0.0044, + 0.0044, + 0.0043, + 0.0044, + 0.0044, + 0.0048, + 0.0044, + 0.0046, + 0.0047, + 0.0047, + 0.0046, + 0.0046, + 0.0046, + 0.0045, + 0.0045, + 0.0044, + 0.0043, + 0.0042, + 0.0042, + 0.0041, + 0.0042, + 0.0044 + ], + [ + 0.0121, + 0.0117, + 0.0114, + 0.0111, + 0.0109, + 0.0106, + 0.0104, + 0.01, + 0.0098, + 0.0096, + 0.0093, + 0.009, + 0.0087, + 0.0084, + 0.008, + 0.0076, + 0.0069, + 0.0064, + 0.0059, + 0.0055, + 0.0051, + 0.009, + 0.0051, + 0.0051, + 0.0053, + 0.0052, + 0.0052, + 0.0054, + 0.0051, + 0.0051, + 0.0049, + 0.0048, + 0.0047, + 0.0046, + 0.0045, + 0.0042, + 0.0041, + 0.0045, + 0.0046, + 0.0046, + 0.0045, + 0.0044, + 0.0044, + 0.0045, + 0.0046, + 0.0049, + 0.0048, + 0.005, + 0.0048, + 0.0046, + 0.0044, + 0.0044, + 0.0043, + 0.0044, + 0.0044, + 0.0048, + 0.0044, + 0.0046, + 0.0047, + 0.0047, + 0.0046, + 0.0046, + 0.0046, + 0.0045, + 0.0045, + 0.0044, + 0.0043, + 0.0042, + 0.0042, + 0.0041, + 0.0042, + 0.0044 + ], + [ + 0.015, + 0.0145, + 0.0142, + 0.0139, + 0.0138, + 0.0134, + 0.0132, + 0.0128, + 0.0126, + 0.0123, + 0.012, + 0.0117, + 0.0114, + 0.0111, + 0.0106, + 0.0102, + 0.0094, + 0.0089, + 0.0083, + 0.0079, + 0.0074, + 0.0115, + 0.0074, + 0.0075, + 0.0076, + 0.0075, + 0.0076, + 0.0077, + 0.0075, + 0.0074, + 0.0072, + 0.0071, + 0.007, + 0.0068, + 0.0067, + 0.0064, + 0.0063, + 0.0067, + 0.0068, + 0.0068, + 0.0068, + 0.0066, + 0.0066, + 0.0068, + 0.0068, + 0.0072, + 0.0071, + 0.0072, + 0.0071, + 0.0069, + 0.0067, + 0.0067, + 0.0066, + 0.0066, + 0.0067, + 0.007, + 0.0067, + 0.0069, + 0.007, + 0.0069, + 0.0068, + 0.0068, + 0.0068, + 0.0067, + 0.0067, + 0.0066, + 0.0066, + 0.0064, + 0.0064, + 0.0062, + 0.0067, + 0.0069 + ], + [ + 0.015, + 0.0145, + 0.0142, + 0.0139, + 0.0138, + 0.0134, + 0.0132, + 0.0128, + 0.0126, + 0.0123, + 0.012, + 0.0117, + 0.0114, + 0.0111, + 0.0106, + 0.0102, + 0.0094, + 0.0089, + 0.0083, + 0.0079, + 0.0074, + 0.0115, + 0.0074, + 0.0075, + 0.0076, + 0.0075, + 0.0076, + 0.0077, + 0.0075, + 0.0074, + 0.0072, + 0.0071, + 0.007, + 0.0068, + 0.0067, + 0.0064, + 0.0063, + 0.0067, + 0.0068, + 0.0068, + 0.0068, + 0.0066, + 0.0066, + 0.0068, + 0.0068, + 0.0072, + 0.0071, + 0.0072, + 0.0071, + 0.0069, + 0.0067, + 0.0067, + 0.0066, + 0.0066, + 0.0067, + 0.007, + 0.0067, + 0.0069, + 0.007, + 0.0069, + 0.0068, + 0.0068, + 0.0068, + 0.0067, + 0.0067, + 0.0066, + 0.0066, + 0.0064, + 0.0064, + 0.0062, + 0.0067, + 0.0069 + ], + [ + 0.0201, + 0.0195, + 0.0192, + 0.0188, + 0.0186, + 0.0182, + 0.0179, + 0.0175, + 0.0172, + 0.0169, + 0.0166, + 0.0162, + 0.0159, + 0.0155, + 0.015, + 0.0145, + 0.0136, + 0.0129, + 0.0122, + 0.0117, + 0.0111, + 0.0156, + 0.0112, + 0.0112, + 0.0114, + 0.0113, + 0.0113, + 0.0115, + 0.0112, + 0.0112, + 0.0109, + 0.0108, + 0.0107, + 0.0104, + 0.0103, + 0.0099, + 0.0098, + 0.0103, + 0.0104, + 0.0104, + 0.0104, + 0.0102, + 0.0101, + 0.0104, + 0.0104, + 0.0109, + 0.0108, + 0.011, + 0.0108, + 0.0105, + 0.0103, + 0.0102, + 0.0101, + 0.0101, + 0.0102, + 0.0107, + 0.0103, + 0.0105, + 0.0106, + 0.0106, + 0.0105, + 0.0104, + 0.0104, + 0.0103, + 0.0103, + 0.0101, + 0.0101, + 0.0099, + 0.0099, + 0.0097, + 0.0107, + 0.011 + ], + [ + 0.0201, + 0.0195, + 0.0192, + 0.0188, + 0.0186, + 0.0182, + 0.0179, + 0.0175, + 0.0172, + 0.0169, + 0.0166, + 0.0162, + 0.0159, + 0.0155, + 0.015, + 0.0145, + 0.0136, + 0.0129, + 0.0122, + 0.0117, + 0.0111, + 0.0156, + 0.0112, + 0.0112, + 0.0114, + 0.0113, + 0.0113, + 0.0115, + 0.0112, + 0.0112, + 0.0109, + 0.0108, + 0.0107, + 0.0104, + 0.0103, + 0.0099, + 0.0098, + 0.0103, + 0.0104, + 0.0104, + 0.0104, + 0.0102, + 0.0101, + 0.0104, + 0.0104, + 0.0109, + 0.0108, + 0.011, + 0.0108, + 0.0105, + 0.0103, + 0.0102, + 0.0101, + 0.0101, + 0.0102, + 0.0107, + 0.0103, + 0.0105, + 0.0106, + 0.0106, + 0.0105, + 0.0104, + 0.0104, + 0.0103, + 0.0103, + 0.0101, + 0.0101, + 0.0099, + 0.0099, + 0.0097, + 0.0107, + 0.011 + ], + [ + 0.027, + 0.0262, + 0.0259, + 0.0254, + 0.0253, + 0.0248, + 0.0245, + 0.024, + 0.0237, + 0.0234, + 0.0231, + 0.0227, + 0.0223, + 0.0219, + 0.0214, + 0.0208, + 0.0198, + 0.019, + 0.0183, + 0.0177, + 0.017, + 0.0223, + 0.0171, + 0.0171, + 0.0173, + 0.0172, + 0.0172, + 0.0174, + 0.0171, + 0.0171, + 0.0168, + 0.0166, + 0.0165, + 0.0162, + 0.016, + 0.0156, + 0.0155, + 0.0161, + 0.0162, + 0.0162, + 0.0162, + 0.0159, + 0.0158, + 0.0161, + 0.0162, + 0.0167, + 0.0166, + 0.0168, + 0.0167, + 0.0163, + 0.016, + 0.016, + 0.0158, + 0.0158, + 0.016, + 0.0165, + 0.016, + 0.0163, + 0.0164, + 0.0164, + 0.0162, + 0.0162, + 0.0162, + 0.016, + 0.016, + 0.0158, + 0.0158, + 0.0156, + 0.0156, + 0.0153, + 0.0173, + 0.0176 + ], + [ + 0.027, + 0.0262, + 0.0259, + 0.0254, + 0.0253, + 0.0248, + 0.0245, + 0.024, + 0.0237, + 0.0234, + 0.0231, + 0.0227, + 0.0223, + 0.0219, + 0.0214, + 0.0208, + 0.0198, + 0.019, + 0.0183, + 0.0177, + 0.017, + 0.0223, + 0.0171, + 0.0171, + 0.0173, + 0.0172, + 0.0172, + 0.0174, + 0.0171, + 0.0171, + 0.0168, + 0.0166, + 0.0165, + 0.0162, + 0.016, + 0.0156, + 0.0155, + 0.0161, + 0.0162, + 0.0162, + 0.0162, + 0.0159, + 0.0158, + 0.0161, + 0.0162, + 0.0167, + 0.0166, + 0.0168, + 0.0167, + 0.0163, + 0.016, + 0.016, + 0.0158, + 0.0158, + 0.016, + 0.0165, + 0.016, + 0.0163, + 0.0164, + 0.0164, + 0.0162, + 0.0162, + 0.0162, + 0.016, + 0.016, + 0.0158, + 0.0158, + 0.0156, + 0.0156, + 0.0153, + 0.0173, + 0.0176 + ], + [ + 0.0387, + 0.0378, + 0.0373, + 0.0368, + 0.0365, + 0.0359, + 0.0355, + 0.0349, + 0.0346, + 0.0341, + 0.0337, + 0.0332, + 0.0328, + 0.0324, + 0.0316, + 0.0309, + 0.0297, + 0.0286, + 0.0276, + 0.0269, + 0.0261, + 0.0322, + 0.0261, + 0.0262, + 0.0264, + 0.0262, + 0.0263, + 0.0265, + 0.0262, + 0.0261, + 0.0258, + 0.0256, + 0.0254, + 0.025, + 0.0248, + 0.0242, + 0.024, + 0.0248, + 0.0251, + 0.025, + 0.025, + 0.0246, + 0.0245, + 0.0249, + 0.0251, + 0.0257, + 0.0255, + 0.0258, + 0.0256, + 0.0251, + 0.0248, + 0.0247, + 0.0245, + 0.0245, + 0.0247, + 0.0254, + 0.0248, + 0.0251, + 0.0252, + 0.0252, + 0.025, + 0.025, + 0.025, + 0.0248, + 0.0248, + 0.0245, + 0.0245, + 0.0242, + 0.0242, + 0.0238, + 0.0273, + 0.028 + ], + [ + 0.0387, + 0.0378, + 0.0373, + 0.0368, + 0.0365, + 0.0359, + 0.0355, + 0.0349, + 0.0346, + 0.0341, + 0.0337, + 0.0332, + 0.0328, + 0.0324, + 0.0316, + 0.0309, + 0.0297, + 0.0286, + 0.0276, + 0.0269, + 0.0261, + 0.0322, + 0.0261, + 0.0262, + 0.0264, + 0.0262, + 0.0263, + 0.0265, + 0.0262, + 0.0261, + 0.0258, + 0.0256, + 0.0254, + 0.025, + 0.0248, + 0.0242, + 0.024, + 0.0248, + 0.0251, + 0.025, + 0.025, + 0.0246, + 0.0245, + 0.0249, + 0.0251, + 0.0257, + 0.0255, + 0.0258, + 0.0256, + 0.0251, + 0.0248, + 0.0247, + 0.0245, + 0.0245, + 0.0247, + 0.0254, + 0.0248, + 0.0251, + 0.0252, + 0.0252, + 0.025, + 0.025, + 0.025, + 0.0248, + 0.0248, + 0.0245, + 0.0245, + 0.0242, + 0.0242, + 0.0238, + 0.0273, + 0.028 + ], + [ + 0.0552, + 0.054, + 0.0535, + 0.0528, + 0.0525, + 0.0517, + 0.0513, + 0.0506, + 0.0501, + 0.0495, + 0.0492, + 0.0486, + 0.048, + 0.0476, + 0.0467, + 0.0459, + 0.0444, + 0.0431, + 0.0419, + 0.041, + 0.04, + 0.0476, + 0.04, + 0.0401, + 0.0404, + 0.0402, + 0.0402, + 0.0405, + 0.0401, + 0.04, + 0.0396, + 0.0393, + 0.0391, + 0.0386, + 0.0384, + 0.0377, + 0.0374, + 0.0385, + 0.0387, + 0.0387, + 0.0386, + 0.0382, + 0.0381, + 0.0385, + 0.0387, + 0.0395, + 0.0393, + 0.0396, + 0.0394, + 0.0388, + 0.0384, + 0.0383, + 0.038, + 0.0381, + 0.0383, + 0.0391, + 0.0384, + 0.0388, + 0.0389, + 0.0389, + 0.0387, + 0.0386, + 0.0387, + 0.0384, + 0.0384, + 0.038, + 0.038, + 0.0377, + 0.0376, + 0.0371, + 0.0428, + 0.0441 + ], + [ + 0.0552, + 0.054, + 0.0535, + 0.0528, + 0.0525, + 0.0517, + 0.0513, + 0.0506, + 0.0501, + 0.0495, + 0.0492, + 0.0486, + 0.048, + 0.0476, + 0.0467, + 0.0459, + 0.0444, + 0.0431, + 0.0419, + 0.041, + 0.04, + 0.0476, + 0.04, + 0.0401, + 0.0404, + 0.0402, + 0.0402, + 0.0405, + 0.0401, + 0.04, + 0.0396, + 0.0393, + 0.0391, + 0.0386, + 0.0384, + 0.0377, + 0.0374, + 0.0385, + 0.0387, + 0.0387, + 0.0386, + 0.0382, + 0.0381, + 0.0385, + 0.0387, + 0.0395, + 0.0393, + 0.0396, + 0.0394, + 0.0388, + 0.0384, + 0.0383, + 0.038, + 0.0381, + 0.0383, + 0.0391, + 0.0384, + 0.0388, + 0.0389, + 0.0389, + 0.0387, + 0.0386, + 0.0387, + 0.0384, + 0.0384, + 0.038, + 0.038, + 0.0377, + 0.0376, + 0.0371, + 0.0428, + 0.0441 + ], + [ + 0.0815, + 0.0799, + 0.0793, + 0.0784, + 0.0779, + 0.077, + 0.0764, + 0.0755, + 0.0749, + 0.0742, + 0.0737, + 0.0731, + 0.0724, + 0.0719, + 0.0708, + 0.0698, + 0.0679, + 0.0662, + 0.0648, + 0.0636, + 0.0624, + 0.0718, + 0.0624, + 0.0625, + 0.0628, + 0.0626, + 0.0627, + 0.063, + 0.0625, + 0.0624, + 0.0619, + 0.0615, + 0.0613, + 0.0607, + 0.0604, + 0.0594, + 0.059, + 0.0604, + 0.0608, + 0.0608, + 0.0606, + 0.0601, + 0.0599, + 0.0606, + 0.0608, + 0.0618, + 0.0615, + 0.062, + 0.0616, + 0.0609, + 0.0603, + 0.0603, + 0.0599, + 0.0599, + 0.0603, + 0.0611, + 0.0603, + 0.0608, + 0.061, + 0.061, + 0.0607, + 0.0607, + 0.0607, + 0.0603, + 0.0603, + 0.0598, + 0.0598, + 0.0594, + 0.0593, + 0.0587, + 0.0664, + 0.0684 + ], + [ + 0.0815, + 0.0799, + 0.0793, + 0.0784, + 0.0779, + 0.077, + 0.0764, + 0.0755, + 0.0749, + 0.0742, + 0.0737, + 0.0731, + 0.0724, + 0.0719, + 0.0708, + 0.0698, + 0.0679, + 0.0662, + 0.0648, + 0.0636, + 0.0624, + 0.0718, + 0.0624, + 0.0625, + 0.0628, + 0.0626, + 0.0627, + 0.063, + 0.0625, + 0.0624, + 0.0619, + 0.0615, + 0.0613, + 0.0607, + 0.0604, + 0.0594, + 0.059, + 0.0604, + 0.0608, + 0.0608, + 0.0606, + 0.0601, + 0.0599, + 0.0606, + 0.0608, + 0.0618, + 0.0615, + 0.062, + 0.0616, + 0.0609, + 0.0603, + 0.0603, + 0.0599, + 0.0599, + 0.0603, + 0.0611, + 0.0603, + 0.0608, + 0.061, + 0.061, + 0.0607, + 0.0607, + 0.0607, + 0.0603, + 0.0603, + 0.0598, + 0.0598, + 0.0594, + 0.0593, + 0.0587, + 0.0664, + 0.0684 + ], + [ + 0.1233, + 0.121, + 0.1203, + 0.1191, + 0.1184, + 0.1171, + 0.1164, + 0.1152, + 0.1144, + 0.1135, + 0.1128, + 0.112, + 0.1113, + 0.1107, + 0.1093, + 0.1079, + 0.1054, + 0.1032, + 0.1012, + 0.0997, + 0.0981, + 0.1097, + 0.0982, + 0.0982, + 0.0987, + 0.0983, + 0.0985, + 0.099, + 0.0983, + 0.0982, + 0.0974, + 0.097, + 0.0966, + 0.0959, + 0.0955, + 0.0942, + 0.0937, + 0.0956, + 0.096, + 0.096, + 0.0958, + 0.095, + 0.0948, + 0.0957, + 0.096, + 0.0973, + 0.0969, + 0.0976, + 0.0971, + 0.0961, + 0.0953, + 0.0953, + 0.0948, + 0.0948, + 0.0953, + 0.0965, + 0.0954, + 0.096, + 0.0963, + 0.0963, + 0.0959, + 0.0959, + 0.0959, + 0.0954, + 0.0954, + 0.0947, + 0.0947, + 0.0942, + 0.0941, + 0.0932, + 0.1023, + 0.105 + ], + [ + 0.1233, + 0.121, + 0.1203, + 0.1191, + 0.1184, + 0.1171, + 0.1164, + 0.1152, + 0.1144, + 0.1135, + 0.1128, + 0.112, + 0.1113, + 0.1107, + 0.1093, + 0.1079, + 0.1054, + 0.1032, + 0.1012, + 0.0997, + 0.0981, + 0.1097, + 0.0982, + 0.0982, + 0.0987, + 0.0983, + 0.0985, + 0.099, + 0.0983, + 0.0982, + 0.0974, + 0.097, + 0.0966, + 0.0959, + 0.0955, + 0.0942, + 0.0937, + 0.0956, + 0.096, + 0.096, + 0.0958, + 0.095, + 0.0948, + 0.0957, + 0.096, + 0.0973, + 0.0969, + 0.0976, + 0.0971, + 0.0961, + 0.0953, + 0.0953, + 0.0948, + 0.0948, + 0.0953, + 0.0965, + 0.0954, + 0.096, + 0.0963, + 0.0963, + 0.0959, + 0.0959, + 0.0959, + 0.0954, + 0.0954, + 0.0947, + 0.0947, + 0.0942, + 0.0941, + 0.0932, + 0.1023, + 0.105 + ], + [ + 0.1828, + 0.18, + 0.1792, + 0.1777, + 0.1768, + 0.1752, + 0.1742, + 0.1728, + 0.172, + 0.1708, + 0.1701, + 0.1693, + 0.1686, + 0.1681, + 0.1665, + 0.165, + 0.1621, + 0.1594, + 0.1572, + 0.1555, + 0.1536, + 0.1654, + 0.1537, + 0.1538, + 0.1543, + 0.1539, + 0.154, + 0.1546, + 0.1539, + 0.1537, + 0.1529, + 0.1524, + 0.152, + 0.1511, + 0.1507, + 0.1492, + 0.1485, + 0.1508, + 0.1513, + 0.1512, + 0.151, + 0.1502, + 0.1499, + 0.1509, + 0.1512, + 0.1527, + 0.1523, + 0.153, + 0.1525, + 0.1513, + 0.1505, + 0.1505, + 0.1498, + 0.1499, + 0.1505, + 0.1516, + 0.1506, + 0.1513, + 0.1516, + 0.1516, + 0.1512, + 0.1511, + 0.1512, + 0.1505, + 0.1505, + 0.1497, + 0.1497, + 0.1492, + 0.149, + 0.148, + 0.1637, + 0.1682 + ], + [ + 0.1828, + 0.18, + 0.1792, + 0.1777, + 0.1768, + 0.1752, + 0.1742, + 0.1728, + 0.172, + 0.1708, + 0.1701, + 0.1693, + 0.1686, + 0.1681, + 0.1665, + 0.165, + 0.1621, + 0.1594, + 0.1572, + 0.1555, + 0.1536, + 0.1654, + 0.1537, + 0.1538, + 0.1543, + 0.1539, + 0.154, + 0.1546, + 0.1539, + 0.1537, + 0.1529, + 0.1524, + 0.152, + 0.1511, + 0.1507, + 0.1492, + 0.1485, + 0.1508, + 0.1513, + 0.1512, + 0.151, + 0.1502, + 0.1499, + 0.1509, + 0.1512, + 0.1527, + 0.1523, + 0.153, + 0.1525, + 0.1513, + 0.1505, + 0.1505, + 0.1498, + 0.1499, + 0.1505, + 0.1516, + 0.1506, + 0.1513, + 0.1516, + 0.1516, + 0.1512, + 0.1511, + 0.1512, + 0.1505, + 0.1505, + 0.1497, + 0.1497, + 0.1492, + 0.149, + 0.148, + 0.1637, + 0.1682 + ], + [ + 0.2683, + 0.2648, + 0.2641, + 0.2623, + 0.2612, + 0.2593, + 0.2582, + 0.2566, + 0.2557, + 0.2543, + 0.2536, + 0.253, + 0.2525, + 0.2525, + 0.2509, + 0.2492, + 0.2461, + 0.2431, + 0.2408, + 0.239, + 0.2371, + 0.2487, + 0.2372, + 0.2372, + 0.2377, + 0.2374, + 0.2375, + 0.2381, + 0.2374, + 0.2372, + 0.2363, + 0.2358, + 0.2354, + 0.2345, + 0.2341, + 0.2325, + 0.2316, + 0.2342, + 0.2347, + 0.2347, + 0.2344, + 0.2335, + 0.2331, + 0.2343, + 0.2347, + 0.2362, + 0.2358, + 0.2365, + 0.236, + 0.2347, + 0.2338, + 0.2339, + 0.233, + 0.2332, + 0.2339, + 0.2348, + 0.234, + 0.2348, + 0.235, + 0.235, + 0.2346, + 0.2345, + 0.2346, + 0.2338, + 0.2338, + 0.2329, + 0.2329, + 0.2324, + 0.2323, + 0.2311, + 0.2641, + 0.2679 + ], + [ + 0.2683, + 0.2648, + 0.2641, + 0.2623, + 0.2612, + 0.2593, + 0.2582, + 0.2566, + 0.2557, + 0.2543, + 0.2536, + 0.253, + 0.2525, + 0.2525, + 0.2509, + 0.2492, + 0.2461, + 0.2431, + 0.2408, + 0.239, + 0.2371, + 0.2487, + 0.2372, + 0.2372, + 0.2377, + 0.2374, + 0.2375, + 0.2381, + 0.2374, + 0.2372, + 0.2363, + 0.2358, + 0.2354, + 0.2345, + 0.2341, + 0.2325, + 0.2316, + 0.2342, + 0.2347, + 0.2347, + 0.2344, + 0.2335, + 0.2331, + 0.2343, + 0.2347, + 0.2362, + 0.2358, + 0.2365, + 0.236, + 0.2347, + 0.2338, + 0.2339, + 0.233, + 0.2332, + 0.2339, + 0.2348, + 0.234, + 0.2348, + 0.235, + 0.235, + 0.2346, + 0.2345, + 0.2346, + 0.2338, + 0.2338, + 0.2329, + 0.2329, + 0.2324, + 0.2323, + 0.2311, + 0.2641, + 0.2679 + ], + [ + 0.3938, + 0.3899, + 0.3891, + 0.3871, + 0.3857, + 0.3835, + 0.3822, + 0.3808, + 0.3799, + 0.3784, + 0.3779, + 0.3778, + 0.3779, + 0.3786, + 0.3774, + 0.3759, + 0.373, + 0.37, + 0.368, + 0.3665, + 0.3649, + 0.3764, + 0.365, + 0.3651, + 0.3655, + 0.3652, + 0.3653, + 0.3658, + 0.3651, + 0.365, + 0.3643, + 0.3639, + 0.3636, + 0.3629, + 0.3625, + 0.361, + 0.3601, + 0.3626, + 0.363, + 0.363, + 0.3628, + 0.362, + 0.3614, + 0.3627, + 0.363, + 0.3642, + 0.3639, + 0.3645, + 0.3641, + 0.3628, + 0.362, + 0.3624, + 0.3614, + 0.3615, + 0.3624, + 0.3631, + 0.3625, + 0.3631, + 0.3633, + 0.3633, + 0.363, + 0.3629, + 0.363, + 0.3621, + 0.3621, + 0.3613, + 0.3613, + 0.361, + 0.3609, + 0.3596, + 0.3947, + 0.3982 + ], + [ + 0.3938, + 0.3899, + 0.3891, + 0.3871, + 0.3857, + 0.3835, + 0.3822, + 0.3808, + 0.3799, + 0.3784, + 0.3779, + 0.3778, + 0.3779, + 0.3786, + 0.3774, + 0.3759, + 0.373, + 0.37, + 0.368, + 0.3665, + 0.3649, + 0.3764, + 0.365, + 0.3651, + 0.3655, + 0.3652, + 0.3653, + 0.3658, + 0.3651, + 0.365, + 0.3643, + 0.3639, + 0.3636, + 0.3629, + 0.3625, + 0.361, + 0.3601, + 0.3626, + 0.363, + 0.363, + 0.3628, + 0.362, + 0.3614, + 0.3627, + 0.363, + 0.3642, + 0.3639, + 0.3645, + 0.3641, + 0.3628, + 0.362, + 0.3624, + 0.3614, + 0.3615, + 0.3624, + 0.3631, + 0.3625, + 0.3631, + 0.3633, + 0.3633, + 0.363, + 0.3629, + 0.363, + 0.3621, + 0.3621, + 0.3613, + 0.3613, + 0.361, + 0.3609, + 0.3596, + 0.3947, + 0.3982 + ], + [ + 0.5253, + 0.52, + 0.5204, + 0.5183, + 0.5165, + 0.5144, + 0.513, + 0.5118, + 0.5111, + 0.5095, + 0.5096, + 0.5104, + 0.5115, + 0.5137, + 0.5132, + 0.5124, + 0.5103, + 0.5076, + 0.5064, + 0.5056, + 0.5049, + 0.5163, + 0.5049, + 0.505, + 0.5053, + 0.5051, + 0.5052, + 0.5054, + 0.505, + 0.5049, + 0.5046, + 0.5044, + 0.5043, + 0.504, + 0.5038, + 0.5028, + 0.5018, + 0.5038, + 0.504, + 0.504, + 0.5039, + 0.5035, + 0.5027, + 0.5039, + 0.504, + 0.5046, + 0.5044, + 0.5047, + 0.5046, + 0.5034, + 0.5031, + 0.5037, + 0.5027, + 0.5028, + 0.5037, + 0.5044, + 0.5038, + 0.5042, + 0.5043, + 0.5043, + 0.5042, + 0.504, + 0.5041, + 0.5032, + 0.5032, + 0.5027, + 0.5027, + 0.5028, + 0.5028, + 0.5016, + 0.5368, + 0.5404 + ], + [ + 0.5253, + 0.52, + 0.5204, + 0.5183, + 0.5165, + 0.5144, + 0.513, + 0.5118, + 0.5111, + 0.5095, + 0.5096, + 0.5104, + 0.5115, + 0.5137, + 0.5132, + 0.5124, + 0.5103, + 0.5076, + 0.5064, + 0.5056, + 0.5049, + 0.5163, + 0.5049, + 0.505, + 0.5053, + 0.5051, + 0.5052, + 0.5054, + 0.505, + 0.5049, + 0.5046, + 0.5044, + 0.5043, + 0.504, + 0.5038, + 0.5028, + 0.5018, + 0.5038, + 0.504, + 0.504, + 0.5039, + 0.5035, + 0.5027, + 0.5039, + 0.504, + 0.5046, + 0.5044, + 0.5047, + 0.5046, + 0.5034, + 0.5031, + 0.5037, + 0.5027, + 0.5028, + 0.5037, + 0.5044, + 0.5038, + 0.5042, + 0.5043, + 0.5043, + 0.5042, + 0.504, + 0.5041, + 0.5032, + 0.5032, + 0.5027, + 0.5027, + 0.5028, + 0.5028, + 0.5016, + 0.5368, + 0.5404 + ], + [ + 0.6598, + 0.656, + 0.6557, + 0.6539, + 0.6522, + 0.6503, + 0.6491, + 0.6487, + 0.6486, + 0.6474, + 0.6482, + 0.6498, + 0.652, + 0.6552, + 0.6558, + 0.656, + 0.6552, + 0.6537, + 0.6538, + 0.654, + 0.6544, + 0.6655, + 0.6544, + 0.6545, + 0.6547, + 0.6544, + 0.6545, + 0.6544, + 0.6544, + 0.6544, + 0.6545, + 0.6548, + 0.6548, + 0.655, + 0.6552, + 0.655, + 0.654, + 0.6552, + 0.6551, + 0.655, + 0.6551, + 0.6551, + 0.6543, + 0.6551, + 0.6551, + 0.6549, + 0.6548, + 0.6548, + 0.6548, + 0.6542, + 0.6544, + 0.6553, + 0.6544, + 0.6544, + 0.6553, + 0.6559, + 0.6553, + 0.6552, + 0.6553, + 0.6553, + 0.6555, + 0.6553, + 0.6554, + 0.6546, + 0.6546, + 0.6544, + 0.6544, + 0.6552, + 0.6551, + 0.6542, + 0.6889, + 0.6924 + ], + [ + 0.6598, + 0.656, + 0.6557, + 0.6539, + 0.6522, + 0.6503, + 0.6491, + 0.6487, + 0.6486, + 0.6474, + 0.6482, + 0.6498, + 0.652, + 0.6552, + 0.6558, + 0.656, + 0.6552, + 0.6537, + 0.6538, + 0.654, + 0.6544, + 0.6655, + 0.6544, + 0.6545, + 0.6547, + 0.6544, + 0.6545, + 0.6544, + 0.6544, + 0.6544, + 0.6545, + 0.6548, + 0.6548, + 0.655, + 0.6552, + 0.655, + 0.654, + 0.6552, + 0.6551, + 0.655, + 0.6551, + 0.6551, + 0.6543, + 0.6551, + 0.6551, + 0.6549, + 0.6548, + 0.6548, + 0.6548, + 0.6542, + 0.6544, + 0.6553, + 0.6544, + 0.6544, + 0.6553, + 0.6559, + 0.6553, + 0.6552, + 0.6553, + 0.6553, + 0.6555, + 0.6553, + 0.6554, + 0.6546, + 0.6546, + 0.6544, + 0.6544, + 0.6552, + 0.6551, + 0.6542, + 0.6889, + 0.6924 + ] + ] + }, + "MortalityDistributionFemale": { + "NumDistributionAxes": 2, + "AxisNames": [ + "age", + "year" + ], + "AxisUnits": [ + "years", + "simulation year" + ], + "AxisScaleFactors": [ + 365, + 1 + ], + "NumPopulationGroups": [ + 44, + 72 + ], + "PopulationGroups": [ + [ + 0, + 0.999, + 1, + 4.999, + 5, + 9.999, + 10, + 14.999, + 15, + 19.999, + 20, + 24.999, + 25, + 29.999, + 30, + 34.999, + 35, + 39.999, + 40, + 44.999, + 45, + 49.999, + 50, + 54.999, + 55, + 59.999, + 60, + 64.999, + 65, + 69.999, + 70, + 74.999, + 75, + 79.999, + 80, + 84.999, + 85, + 89.999, + 90, + 94.999, + 95, + 99.999, + 100, + 100 + ], + [ + 1950, + 1951, + 1952, + 1953, + 1954, + 1955, + 1956, + 1957, + 1958, + 1959, + 1960, + 1961, + 1962, + 1963, + 1964, + 1965, + 1966, + 1967, + 1968, + 1969, + 1970, + 1971, + 1972, + 1973, + 1974, + 1975, + 1976, + 1977, + 1978, + 1979, + 1980, + 1981, + 1982, + 1983, + 1984, + 1985, + 1986, + 1987, + 1988, + 1989, + 1990, + 1991, + 1992, + 1993, + 1994, + 1995, + 1996, + 1997, + 1998, + 1999, + 2000, + 2001, + 2002, + 2003, + 2004, + 2005, + 2006, + 2007, + 2008, + 2009, + 2010, + 2011, + 2012, + 2013, + 2014, + 2015, + 2016, + 2017, + 2018, + 2019, + 2020, + 2021 + ] + ], + "ResultUnits": "Mortality rate in units of 1/year", + "ResultScaleFactor": 0.0027, + "ResultValues": [ + [ + 0.313, + 0.2981, + 0.2849, + 0.2722, + 0.2609, + 0.2499, + 0.2398, + 0.2301, + 0.2212, + 0.2131, + 0.2054, + 0.1981, + 0.1913, + 0.185, + 0.1791, + 0.1746, + 0.1692, + 0.1652, + 0.1618, + 0.1588, + 0.1561, + 0.16, + 0.1511, + 0.1489, + 0.1466, + 0.1443, + 0.1424, + 0.1407, + 0.1387, + 0.1368, + 0.1349, + 0.1329, + 0.1309, + 0.1289, + 0.1267, + 0.1243, + 0.122, + 0.1197, + 0.1176, + 0.1154, + 0.1133, + 0.1111, + 0.1089, + 0.1063, + 0.1039, + 0.1013, + 0.0985, + 0.0956, + 0.0929, + 0.09, + 0.0874, + 0.0851, + 0.0829, + 0.0811, + 0.0793, + 0.0786, + 0.0762, + 0.0747, + 0.0732, + 0.0718, + 0.0703, + 0.0686, + 0.0667, + 0.0649, + 0.063, + 0.0612, + 0.0594, + 0.0576, + 0.0559, + 0.0542, + 0.0524, + 0.0507 + ], + [ + 0.313, + 0.2981, + 0.2849, + 0.2722, + 0.2609, + 0.2499, + 0.2398, + 0.2301, + 0.2212, + 0.2131, + 0.2054, + 0.1981, + 0.1913, + 0.185, + 0.1791, + 0.1746, + 0.1692, + 0.1652, + 0.1618, + 0.1588, + 0.1561, + 0.16, + 0.1511, + 0.1489, + 0.1466, + 0.1443, + 0.1424, + 0.1407, + 0.1387, + 0.1368, + 0.1349, + 0.1329, + 0.1309, + 0.1289, + 0.1267, + 0.1243, + 0.122, + 0.1197, + 0.1176, + 0.1154, + 0.1133, + 0.1111, + 0.1089, + 0.1063, + 0.1039, + 0.1013, + 0.0985, + 0.0956, + 0.0929, + 0.09, + 0.0874, + 0.0851, + 0.0829, + 0.0811, + 0.0793, + 0.0786, + 0.0762, + 0.0747, + 0.0732, + 0.0718, + 0.0703, + 0.0686, + 0.0667, + 0.0649, + 0.063, + 0.0612, + 0.0594, + 0.0576, + 0.0559, + 0.0542, + 0.0524, + 0.0507 + ], + [ + 0.0425, + 0.0394, + 0.0366, + 0.034, + 0.0319, + 0.0297, + 0.0279, + 0.0263, + 0.0248, + 0.0234, + 0.0221, + 0.0209, + 0.0197, + 0.0186, + 0.018, + 0.0175, + 0.0165, + 0.0159, + 0.0154, + 0.015, + 0.0146, + 0.0173, + 0.0139, + 0.0136, + 0.0133, + 0.0129, + 0.0126, + 0.0124, + 0.0122, + 0.0119, + 0.0117, + 0.0114, + 0.0112, + 0.0109, + 0.0106, + 0.0103, + 0.01, + 0.0097, + 0.0094, + 0.0092, + 0.0089, + 0.0086, + 0.0084, + 0.0081, + 0.0078, + 0.0075, + 0.0071, + 0.0068, + 0.0065, + 0.0062, + 0.0059, + 0.0057, + 0.0055, + 0.0053, + 0.0051, + 0.0054, + 0.0048, + 0.0046, + 0.0045, + 0.0044, + 0.0043, + 0.0041, + 0.0039, + 0.0038, + 0.0036, + 0.0034, + 0.0033, + 0.0031, + 0.003, + 0.0029, + 0.0027, + 0.0026 + ], + [ + 0.0425, + 0.0394, + 0.0366, + 0.034, + 0.0319, + 0.0297, + 0.0279, + 0.0263, + 0.0248, + 0.0234, + 0.0221, + 0.0209, + 0.0197, + 0.0186, + 0.018, + 0.0175, + 0.0165, + 0.0159, + 0.0154, + 0.015, + 0.0146, + 0.0173, + 0.0139, + 0.0136, + 0.0133, + 0.0129, + 0.0126, + 0.0124, + 0.0122, + 0.0119, + 0.0117, + 0.0114, + 0.0112, + 0.0109, + 0.0106, + 0.0103, + 0.01, + 0.0097, + 0.0094, + 0.0092, + 0.0089, + 0.0086, + 0.0084, + 0.0081, + 0.0078, + 0.0075, + 0.0071, + 0.0068, + 0.0065, + 0.0062, + 0.0059, + 0.0057, + 0.0055, + 0.0053, + 0.0051, + 0.0054, + 0.0048, + 0.0046, + 0.0045, + 0.0044, + 0.0043, + 0.0041, + 0.0039, + 0.0038, + 0.0036, + 0.0034, + 0.0033, + 0.0031, + 0.003, + 0.0029, + 0.0027, + 0.0026 + ], + [ + 0.0078, + 0.0075, + 0.0073, + 0.007, + 0.0069, + 0.0066, + 0.0064, + 0.0063, + 0.0061, + 0.0059, + 0.0057, + 0.0055, + 0.0053, + 0.0049, + 0.0045, + 0.0042, + 0.0035, + 0.0031, + 0.0028, + 0.0024, + 0.002, + 0.0032, + 0.002, + 0.0019, + 0.002, + 0.0018, + 0.0018, + 0.0017, + 0.0016, + 0.0014, + 0.0013, + 0.0011, + 0.001, + 0.0009, + 0.0008, + 0.001, + 0.0011, + 0.0013, + 0.0012, + 0.0012, + 0.001, + 0.0011, + 0.0013, + 0.0015, + 0.0015, + 0.0016, + 0.0015, + 0.0015, + 0.0013, + 0.0012, + 0.0012, + 0.0011, + 0.0012, + 0.0011, + 0.001, + 0.0014, + 0.0008, + 0.0008, + 0.0007, + 0.0007, + 0.0007, + 0.0007, + 0.0007, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0005, + 0.0005 + ], + [ + 0.0078, + 0.0075, + 0.0073, + 0.007, + 0.0069, + 0.0066, + 0.0064, + 0.0063, + 0.0061, + 0.0059, + 0.0057, + 0.0055, + 0.0053, + 0.0049, + 0.0045, + 0.0042, + 0.0035, + 0.0031, + 0.0028, + 0.0024, + 0.002, + 0.0032, + 0.002, + 0.0019, + 0.002, + 0.0018, + 0.0018, + 0.0017, + 0.0016, + 0.0014, + 0.0013, + 0.0011, + 0.001, + 0.0009, + 0.0008, + 0.001, + 0.0011, + 0.0013, + 0.0012, + 0.0012, + 0.001, + 0.0011, + 0.0013, + 0.0015, + 0.0015, + 0.0016, + 0.0015, + 0.0015, + 0.0013, + 0.0012, + 0.0012, + 0.0011, + 0.0012, + 0.0011, + 0.001, + 0.0014, + 0.0008, + 0.0008, + 0.0007, + 0.0007, + 0.0007, + 0.0007, + 0.0007, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0005, + 0.0005 + ], + [ + 0.0062, + 0.006, + 0.0058, + 0.0056, + 0.0054, + 0.0052, + 0.0051, + 0.0049, + 0.0048, + 0.0046, + 0.0045, + 0.0043, + 0.0042, + 0.0038, + 0.0036, + 0.0033, + 0.0028, + 0.0025, + 0.0022, + 0.002, + 0.0017, + 0.0022, + 0.0017, + 0.0016, + 0.0017, + 0.0015, + 0.0015, + 0.0014, + 0.0013, + 0.0012, + 0.0011, + 0.001, + 0.0009, + 0.0008, + 0.0008, + 0.0009, + 0.001, + 0.0011, + 0.001, + 0.001, + 0.0009, + 0.001, + 0.0011, + 0.0012, + 0.0012, + 0.0014, + 0.0012, + 0.0012, + 0.0011, + 0.001, + 0.001, + 0.0009, + 0.001, + 0.0009, + 0.0009, + 0.0012, + 0.0007, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0005, + 0.0005, + 0.0005, + 0.0005, + 0.0005, + 0.0004, + 0.0004 + ], + [ + 0.0062, + 0.006, + 0.0058, + 0.0056, + 0.0054, + 0.0052, + 0.0051, + 0.0049, + 0.0048, + 0.0046, + 0.0045, + 0.0043, + 0.0042, + 0.0038, + 0.0036, + 0.0033, + 0.0028, + 0.0025, + 0.0022, + 0.002, + 0.0017, + 0.0022, + 0.0017, + 0.0016, + 0.0017, + 0.0015, + 0.0015, + 0.0014, + 0.0013, + 0.0012, + 0.0011, + 0.001, + 0.0009, + 0.0008, + 0.0008, + 0.0009, + 0.001, + 0.0011, + 0.001, + 0.001, + 0.0009, + 0.001, + 0.0011, + 0.0012, + 0.0012, + 0.0014, + 0.0012, + 0.0012, + 0.0011, + 0.001, + 0.001, + 0.0009, + 0.001, + 0.0009, + 0.0009, + 0.0012, + 0.0007, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0006, + 0.0005, + 0.0005, + 0.0005, + 0.0005, + 0.0005, + 0.0004, + 0.0004 + ], + [ + 0.0081, + 0.0078, + 0.0076, + 0.0074, + 0.0072, + 0.0069, + 0.0068, + 0.0066, + 0.0064, + 0.0062, + 0.006, + 0.0058, + 0.0056, + 0.0052, + 0.0048, + 0.0044, + 0.004, + 0.0035, + 0.0032, + 0.0028, + 0.0024, + 0.0028, + 0.0024, + 0.0023, + 0.0023, + 0.0022, + 0.0021, + 0.002, + 0.0019, + 0.0017, + 0.0016, + 0.0014, + 0.0013, + 0.0012, + 0.0011, + 0.0013, + 0.0014, + 0.0017, + 0.0015, + 0.0015, + 0.0013, + 0.0014, + 0.0016, + 0.0018, + 0.0018, + 0.002, + 0.0018, + 0.0018, + 0.0017, + 0.0015, + 0.0015, + 0.0014, + 0.0015, + 0.0014, + 0.0013, + 0.0016, + 0.0011, + 0.001, + 0.0009, + 0.001, + 0.0009, + 0.0009, + 0.0009, + 0.0008, + 0.0009, + 0.0008, + 0.0008, + 0.0008, + 0.0008, + 0.0008, + 0.0006, + 0.0007 + ], + [ + 0.0081, + 0.0078, + 0.0076, + 0.0074, + 0.0072, + 0.0069, + 0.0068, + 0.0066, + 0.0064, + 0.0062, + 0.006, + 0.0058, + 0.0056, + 0.0052, + 0.0048, + 0.0044, + 0.004, + 0.0035, + 0.0032, + 0.0028, + 0.0024, + 0.0028, + 0.0024, + 0.0023, + 0.0023, + 0.0022, + 0.0021, + 0.002, + 0.0019, + 0.0017, + 0.0016, + 0.0014, + 0.0013, + 0.0012, + 0.0011, + 0.0013, + 0.0014, + 0.0017, + 0.0015, + 0.0015, + 0.0013, + 0.0014, + 0.0016, + 0.0018, + 0.0018, + 0.002, + 0.0018, + 0.0018, + 0.0017, + 0.0015, + 0.0015, + 0.0014, + 0.0015, + 0.0014, + 0.0013, + 0.0016, + 0.0011, + 0.001, + 0.0009, + 0.001, + 0.0009, + 0.0009, + 0.0009, + 0.0008, + 0.0009, + 0.0008, + 0.0008, + 0.0008, + 0.0008, + 0.0008, + 0.0006, + 0.0007 + ], + [ + 0.0102, + 0.0099, + 0.0096, + 0.0093, + 0.009, + 0.0087, + 0.0085, + 0.0083, + 0.0081, + 0.0078, + 0.0076, + 0.0074, + 0.0071, + 0.0066, + 0.0062, + 0.0057, + 0.0051, + 0.0046, + 0.0041, + 0.0037, + 0.0032, + 0.0036, + 0.0031, + 0.003, + 0.0031, + 0.0029, + 0.0028, + 0.0027, + 0.0026, + 0.0023, + 0.0022, + 0.002, + 0.0018, + 0.0016, + 0.0015, + 0.0018, + 0.002, + 0.0022, + 0.0021, + 0.002, + 0.0018, + 0.0019, + 0.0022, + 0.0025, + 0.0024, + 0.0026, + 0.0025, + 0.0024, + 0.0022, + 0.0021, + 0.002, + 0.0019, + 0.002, + 0.0019, + 0.0018, + 0.002, + 0.0015, + 0.0014, + 0.0013, + 0.0013, + 0.0013, + 0.0012, + 0.0013, + 0.0012, + 0.0012, + 0.0011, + 0.0011, + 0.001, + 0.001, + 0.0011, + 0.0009, + 0.0009 + ], + [ + 0.0102, + 0.0099, + 0.0096, + 0.0093, + 0.009, + 0.0087, + 0.0085, + 0.0083, + 0.0081, + 0.0078, + 0.0076, + 0.0074, + 0.0071, + 0.0066, + 0.0062, + 0.0057, + 0.0051, + 0.0046, + 0.0041, + 0.0037, + 0.0032, + 0.0036, + 0.0031, + 0.003, + 0.0031, + 0.0029, + 0.0028, + 0.0027, + 0.0026, + 0.0023, + 0.0022, + 0.002, + 0.0018, + 0.0016, + 0.0015, + 0.0018, + 0.002, + 0.0022, + 0.0021, + 0.002, + 0.0018, + 0.0019, + 0.0022, + 0.0025, + 0.0024, + 0.0026, + 0.0025, + 0.0024, + 0.0022, + 0.0021, + 0.002, + 0.0019, + 0.002, + 0.0019, + 0.0018, + 0.002, + 0.0015, + 0.0014, + 0.0013, + 0.0013, + 0.0013, + 0.0012, + 0.0013, + 0.0012, + 0.0012, + 0.0011, + 0.0011, + 0.001, + 0.001, + 0.0011, + 0.0009, + 0.0009 + ], + [ + 0.0115, + 0.0111, + 0.0107, + 0.0104, + 0.0102, + 0.0098, + 0.0096, + 0.0093, + 0.0091, + 0.0088, + 0.0085, + 0.0083, + 0.008, + 0.0074, + 0.007, + 0.0064, + 0.0058, + 0.0052, + 0.0047, + 0.0043, + 0.0037, + 0.0042, + 0.0036, + 0.0035, + 0.0035, + 0.0033, + 0.0032, + 0.0032, + 0.003, + 0.0027, + 0.0025, + 0.0023, + 0.0022, + 0.0019, + 0.0018, + 0.0022, + 0.0023, + 0.0026, + 0.0024, + 0.0023, + 0.0021, + 0.0022, + 0.0026, + 0.0029, + 0.0028, + 0.0031, + 0.0029, + 0.0028, + 0.0026, + 0.0024, + 0.0024, + 0.0023, + 0.0023, + 0.0022, + 0.0021, + 0.0023, + 0.0018, + 0.0016, + 0.0016, + 0.0016, + 0.0015, + 0.0015, + 0.0015, + 0.0014, + 0.0014, + 0.0014, + 0.0014, + 0.0013, + 0.0013, + 0.0013, + 0.0011, + 0.0011 + ], + [ + 0.0115, + 0.0111, + 0.0107, + 0.0104, + 0.0102, + 0.0098, + 0.0096, + 0.0093, + 0.0091, + 0.0088, + 0.0085, + 0.0083, + 0.008, + 0.0074, + 0.007, + 0.0064, + 0.0058, + 0.0052, + 0.0047, + 0.0043, + 0.0037, + 0.0042, + 0.0036, + 0.0035, + 0.0035, + 0.0033, + 0.0032, + 0.0032, + 0.003, + 0.0027, + 0.0025, + 0.0023, + 0.0022, + 0.0019, + 0.0018, + 0.0022, + 0.0023, + 0.0026, + 0.0024, + 0.0023, + 0.0021, + 0.0022, + 0.0026, + 0.0029, + 0.0028, + 0.0031, + 0.0029, + 0.0028, + 0.0026, + 0.0024, + 0.0024, + 0.0023, + 0.0023, + 0.0022, + 0.0021, + 0.0023, + 0.0018, + 0.0016, + 0.0016, + 0.0016, + 0.0015, + 0.0015, + 0.0015, + 0.0014, + 0.0014, + 0.0014, + 0.0014, + 0.0013, + 0.0013, + 0.0013, + 0.0011, + 0.0011 + ], + [ + 0.013, + 0.0126, + 0.0122, + 0.0118, + 0.0115, + 0.0111, + 0.0109, + 0.0106, + 0.0103, + 0.01, + 0.0097, + 0.0094, + 0.0091, + 0.0084, + 0.0079, + 0.0073, + 0.0066, + 0.006, + 0.0054, + 0.0049, + 0.0042, + 0.0048, + 0.0041, + 0.004, + 0.0041, + 0.0039, + 0.0038, + 0.0037, + 0.0034, + 0.0032, + 0.003, + 0.0027, + 0.0025, + 0.0023, + 0.0022, + 0.0025, + 0.0027, + 0.0031, + 0.0028, + 0.0028, + 0.0025, + 0.0026, + 0.003, + 0.0033, + 0.0033, + 0.0036, + 0.0033, + 0.0032, + 0.0031, + 0.0029, + 0.0028, + 0.0027, + 0.0027, + 0.0026, + 0.0025, + 0.0027, + 0.0021, + 0.002, + 0.0019, + 0.0019, + 0.0018, + 0.0018, + 0.0018, + 0.0017, + 0.0018, + 0.0017, + 0.0017, + 0.0016, + 0.0016, + 0.0016, + 0.0014, + 0.0014 + ], + [ + 0.013, + 0.0126, + 0.0122, + 0.0118, + 0.0115, + 0.0111, + 0.0109, + 0.0106, + 0.0103, + 0.01, + 0.0097, + 0.0094, + 0.0091, + 0.0084, + 0.0079, + 0.0073, + 0.0066, + 0.006, + 0.0054, + 0.0049, + 0.0042, + 0.0048, + 0.0041, + 0.004, + 0.0041, + 0.0039, + 0.0038, + 0.0037, + 0.0034, + 0.0032, + 0.003, + 0.0027, + 0.0025, + 0.0023, + 0.0022, + 0.0025, + 0.0027, + 0.0031, + 0.0028, + 0.0028, + 0.0025, + 0.0026, + 0.003, + 0.0033, + 0.0033, + 0.0036, + 0.0033, + 0.0032, + 0.0031, + 0.0029, + 0.0028, + 0.0027, + 0.0027, + 0.0026, + 0.0025, + 0.0027, + 0.0021, + 0.002, + 0.0019, + 0.0019, + 0.0018, + 0.0018, + 0.0018, + 0.0017, + 0.0018, + 0.0017, + 0.0017, + 0.0016, + 0.0016, + 0.0016, + 0.0014, + 0.0014 + ], + [ + 0.0144, + 0.0139, + 0.0135, + 0.0131, + 0.0127, + 0.0123, + 0.012, + 0.0118, + 0.0114, + 0.0111, + 0.0107, + 0.0104, + 0.0102, + 0.0094, + 0.0088, + 0.0082, + 0.0074, + 0.0068, + 0.0062, + 0.0056, + 0.005, + 0.0056, + 0.0048, + 0.0047, + 0.0048, + 0.0046, + 0.0044, + 0.0043, + 0.0041, + 0.0038, + 0.0036, + 0.0033, + 0.0031, + 0.0029, + 0.0027, + 0.0031, + 0.0033, + 0.0037, + 0.0034, + 0.0034, + 0.0031, + 0.0032, + 0.0036, + 0.004, + 0.004, + 0.0042, + 0.004, + 0.0039, + 0.0037, + 0.0035, + 0.0034, + 0.0033, + 0.0033, + 0.0032, + 0.0031, + 0.0032, + 0.0026, + 0.0025, + 0.0024, + 0.0024, + 0.0023, + 0.0023, + 0.0023, + 0.0022, + 0.0023, + 0.0022, + 0.0022, + 0.002, + 0.002, + 0.002, + 0.0019, + 0.002 + ], + [ + 0.0144, + 0.0139, + 0.0135, + 0.0131, + 0.0127, + 0.0123, + 0.012, + 0.0118, + 0.0114, + 0.0111, + 0.0107, + 0.0104, + 0.0102, + 0.0094, + 0.0088, + 0.0082, + 0.0074, + 0.0068, + 0.0062, + 0.0056, + 0.005, + 0.0056, + 0.0048, + 0.0047, + 0.0048, + 0.0046, + 0.0044, + 0.0043, + 0.0041, + 0.0038, + 0.0036, + 0.0033, + 0.0031, + 0.0029, + 0.0027, + 0.0031, + 0.0033, + 0.0037, + 0.0034, + 0.0034, + 0.0031, + 0.0032, + 0.0036, + 0.004, + 0.004, + 0.0042, + 0.004, + 0.0039, + 0.0037, + 0.0035, + 0.0034, + 0.0033, + 0.0033, + 0.0032, + 0.0031, + 0.0032, + 0.0026, + 0.0025, + 0.0024, + 0.0024, + 0.0023, + 0.0023, + 0.0023, + 0.0022, + 0.0023, + 0.0022, + 0.0022, + 0.002, + 0.002, + 0.002, + 0.0019, + 0.002 + ], + [ + 0.0156, + 0.0152, + 0.0147, + 0.0143, + 0.014, + 0.0135, + 0.0132, + 0.0129, + 0.0126, + 0.0122, + 0.0119, + 0.0116, + 0.0113, + 0.0105, + 0.0099, + 0.0092, + 0.0084, + 0.0078, + 0.0072, + 0.0067, + 0.0059, + 0.0069, + 0.0058, + 0.0057, + 0.0058, + 0.0055, + 0.0054, + 0.0053, + 0.0051, + 0.0048, + 0.0045, + 0.0042, + 0.004, + 0.0038, + 0.0036, + 0.004, + 0.0042, + 0.0046, + 0.0044, + 0.0043, + 0.004, + 0.0042, + 0.0046, + 0.0049, + 0.0049, + 0.0052, + 0.0049, + 0.0048, + 0.0046, + 0.0044, + 0.0043, + 0.0042, + 0.0042, + 0.0042, + 0.004, + 0.0042, + 0.0035, + 0.0034, + 0.0032, + 0.0033, + 0.0032, + 0.0032, + 0.0032, + 0.0031, + 0.0031, + 0.003, + 0.003, + 0.0028, + 0.0028, + 0.0028, + 0.0028, + 0.0029 + ], + [ + 0.0156, + 0.0152, + 0.0147, + 0.0143, + 0.014, + 0.0135, + 0.0132, + 0.0129, + 0.0126, + 0.0122, + 0.0119, + 0.0116, + 0.0113, + 0.0105, + 0.0099, + 0.0092, + 0.0084, + 0.0078, + 0.0072, + 0.0067, + 0.0059, + 0.0069, + 0.0058, + 0.0057, + 0.0058, + 0.0055, + 0.0054, + 0.0053, + 0.0051, + 0.0048, + 0.0045, + 0.0042, + 0.004, + 0.0038, + 0.0036, + 0.004, + 0.0042, + 0.0046, + 0.0044, + 0.0043, + 0.004, + 0.0042, + 0.0046, + 0.0049, + 0.0049, + 0.0052, + 0.0049, + 0.0048, + 0.0046, + 0.0044, + 0.0043, + 0.0042, + 0.0042, + 0.0042, + 0.004, + 0.0042, + 0.0035, + 0.0034, + 0.0032, + 0.0033, + 0.0032, + 0.0032, + 0.0032, + 0.0031, + 0.0031, + 0.003, + 0.003, + 0.0028, + 0.0028, + 0.0028, + 0.0028, + 0.0029 + ], + [ + 0.0172, + 0.0168, + 0.0163, + 0.0159, + 0.0155, + 0.0151, + 0.0148, + 0.0145, + 0.0142, + 0.0138, + 0.0135, + 0.0132, + 0.0128, + 0.0121, + 0.0115, + 0.0108, + 0.01, + 0.0094, + 0.0088, + 0.0083, + 0.0075, + 0.0089, + 0.0074, + 0.0073, + 0.0074, + 0.0071, + 0.007, + 0.0069, + 0.0066, + 0.0063, + 0.0061, + 0.0058, + 0.0056, + 0.0053, + 0.0051, + 0.0056, + 0.0058, + 0.0062, + 0.0059, + 0.0058, + 0.0056, + 0.0057, + 0.0062, + 0.0065, + 0.0065, + 0.0068, + 0.0065, + 0.0064, + 0.0062, + 0.006, + 0.0059, + 0.0058, + 0.0058, + 0.0057, + 0.0056, + 0.0057, + 0.005, + 0.0049, + 0.0047, + 0.0048, + 0.0046, + 0.0046, + 0.0046, + 0.0045, + 0.0045, + 0.0044, + 0.0044, + 0.0042, + 0.0042, + 0.0042, + 0.0044, + 0.0045 + ], + [ + 0.0172, + 0.0168, + 0.0163, + 0.0159, + 0.0155, + 0.0151, + 0.0148, + 0.0145, + 0.0142, + 0.0138, + 0.0135, + 0.0132, + 0.0128, + 0.0121, + 0.0115, + 0.0108, + 0.01, + 0.0094, + 0.0088, + 0.0083, + 0.0075, + 0.0089, + 0.0074, + 0.0073, + 0.0074, + 0.0071, + 0.007, + 0.0069, + 0.0066, + 0.0063, + 0.0061, + 0.0058, + 0.0056, + 0.0053, + 0.0051, + 0.0056, + 0.0058, + 0.0062, + 0.0059, + 0.0058, + 0.0056, + 0.0057, + 0.0062, + 0.0065, + 0.0065, + 0.0068, + 0.0065, + 0.0064, + 0.0062, + 0.006, + 0.0059, + 0.0058, + 0.0058, + 0.0057, + 0.0056, + 0.0057, + 0.005, + 0.0049, + 0.0047, + 0.0048, + 0.0046, + 0.0046, + 0.0046, + 0.0045, + 0.0045, + 0.0044, + 0.0044, + 0.0042, + 0.0042, + 0.0042, + 0.0044, + 0.0045 + ], + [ + 0.0226, + 0.022, + 0.0214, + 0.0208, + 0.0204, + 0.0198, + 0.0195, + 0.0192, + 0.0187, + 0.0183, + 0.0178, + 0.0175, + 0.017, + 0.0161, + 0.0154, + 0.0145, + 0.0135, + 0.0128, + 0.012, + 0.0114, + 0.0105, + 0.0121, + 0.0104, + 0.0102, + 0.0103, + 0.01, + 0.0098, + 0.0097, + 0.0094, + 0.009, + 0.0087, + 0.0083, + 0.0081, + 0.0077, + 0.0074, + 0.008, + 0.0083, + 0.0088, + 0.0085, + 0.0084, + 0.008, + 0.0082, + 0.0088, + 0.0092, + 0.0092, + 0.0095, + 0.0092, + 0.0091, + 0.0088, + 0.0085, + 0.0084, + 0.0083, + 0.0083, + 0.0082, + 0.008, + 0.0082, + 0.0074, + 0.0071, + 0.007, + 0.007, + 0.0069, + 0.0068, + 0.0069, + 0.0067, + 0.0067, + 0.0065, + 0.0065, + 0.0063, + 0.0063, + 0.0063, + 0.0067, + 0.007 + ], + [ + 0.0226, + 0.022, + 0.0214, + 0.0208, + 0.0204, + 0.0198, + 0.0195, + 0.0192, + 0.0187, + 0.0183, + 0.0178, + 0.0175, + 0.017, + 0.0161, + 0.0154, + 0.0145, + 0.0135, + 0.0128, + 0.012, + 0.0114, + 0.0105, + 0.0121, + 0.0104, + 0.0102, + 0.0103, + 0.01, + 0.0098, + 0.0097, + 0.0094, + 0.009, + 0.0087, + 0.0083, + 0.0081, + 0.0077, + 0.0074, + 0.008, + 0.0083, + 0.0088, + 0.0085, + 0.0084, + 0.008, + 0.0082, + 0.0088, + 0.0092, + 0.0092, + 0.0095, + 0.0092, + 0.0091, + 0.0088, + 0.0085, + 0.0084, + 0.0083, + 0.0083, + 0.0082, + 0.008, + 0.0082, + 0.0074, + 0.0071, + 0.007, + 0.007, + 0.0069, + 0.0068, + 0.0069, + 0.0067, + 0.0067, + 0.0065, + 0.0065, + 0.0063, + 0.0063, + 0.0063, + 0.0067, + 0.007 + ], + [ + 0.0294, + 0.0286, + 0.0279, + 0.0272, + 0.0267, + 0.026, + 0.0256, + 0.0252, + 0.0247, + 0.0241, + 0.0236, + 0.0232, + 0.0226, + 0.0214, + 0.0206, + 0.0196, + 0.0184, + 0.0175, + 0.0166, + 0.0159, + 0.0148, + 0.0168, + 0.0146, + 0.0144, + 0.0145, + 0.0142, + 0.014, + 0.0138, + 0.0134, + 0.013, + 0.0126, + 0.0121, + 0.0118, + 0.0113, + 0.0111, + 0.0118, + 0.0121, + 0.0127, + 0.0124, + 0.0122, + 0.0118, + 0.012, + 0.0127, + 0.0132, + 0.0132, + 0.0136, + 0.0132, + 0.0131, + 0.0127, + 0.0124, + 0.0123, + 0.0121, + 0.0122, + 0.012, + 0.0118, + 0.0119, + 0.0109, + 0.0106, + 0.0104, + 0.0105, + 0.0103, + 0.0103, + 0.0103, + 0.0101, + 0.0101, + 0.0099, + 0.0099, + 0.0096, + 0.0096, + 0.0096, + 0.0105, + 0.0109 + ], + [ + 0.0294, + 0.0286, + 0.0279, + 0.0272, + 0.0267, + 0.026, + 0.0256, + 0.0252, + 0.0247, + 0.0241, + 0.0236, + 0.0232, + 0.0226, + 0.0214, + 0.0206, + 0.0196, + 0.0184, + 0.0175, + 0.0166, + 0.0159, + 0.0148, + 0.0168, + 0.0146, + 0.0144, + 0.0145, + 0.0142, + 0.014, + 0.0138, + 0.0134, + 0.013, + 0.0126, + 0.0121, + 0.0118, + 0.0113, + 0.0111, + 0.0118, + 0.0121, + 0.0127, + 0.0124, + 0.0122, + 0.0118, + 0.012, + 0.0127, + 0.0132, + 0.0132, + 0.0136, + 0.0132, + 0.0131, + 0.0127, + 0.0124, + 0.0123, + 0.0121, + 0.0122, + 0.012, + 0.0118, + 0.0119, + 0.0109, + 0.0106, + 0.0104, + 0.0105, + 0.0103, + 0.0103, + 0.0103, + 0.0101, + 0.0101, + 0.0099, + 0.0099, + 0.0096, + 0.0096, + 0.0096, + 0.0105, + 0.0109 + ], + [ + 0.0436, + 0.0426, + 0.0415, + 0.0405, + 0.0398, + 0.0387, + 0.0382, + 0.0376, + 0.0368, + 0.036, + 0.0353, + 0.0346, + 0.0338, + 0.0321, + 0.031, + 0.0294, + 0.0278, + 0.0265, + 0.0253, + 0.0242, + 0.0227, + 0.0257, + 0.0224, + 0.0221, + 0.0222, + 0.0218, + 0.0215, + 0.0212, + 0.0208, + 0.0201, + 0.0196, + 0.0189, + 0.0185, + 0.0178, + 0.0174, + 0.0184, + 0.0189, + 0.0198, + 0.0192, + 0.019, + 0.0184, + 0.0188, + 0.0197, + 0.0205, + 0.0204, + 0.021, + 0.0205, + 0.0203, + 0.0198, + 0.0193, + 0.0191, + 0.0188, + 0.019, + 0.0188, + 0.0184, + 0.0185, + 0.0172, + 0.0168, + 0.0165, + 0.0166, + 0.0163, + 0.0163, + 0.0163, + 0.016, + 0.0161, + 0.0157, + 0.0157, + 0.0152, + 0.0152, + 0.0153, + 0.0171, + 0.018 + ], + [ + 0.0436, + 0.0426, + 0.0415, + 0.0405, + 0.0398, + 0.0387, + 0.0382, + 0.0376, + 0.0368, + 0.036, + 0.0353, + 0.0346, + 0.0338, + 0.0321, + 0.031, + 0.0294, + 0.0278, + 0.0265, + 0.0253, + 0.0242, + 0.0227, + 0.0257, + 0.0224, + 0.0221, + 0.0222, + 0.0218, + 0.0215, + 0.0212, + 0.0208, + 0.0201, + 0.0196, + 0.0189, + 0.0185, + 0.0178, + 0.0174, + 0.0184, + 0.0189, + 0.0198, + 0.0192, + 0.019, + 0.0184, + 0.0188, + 0.0197, + 0.0205, + 0.0204, + 0.021, + 0.0205, + 0.0203, + 0.0198, + 0.0193, + 0.0191, + 0.0188, + 0.019, + 0.0188, + 0.0184, + 0.0185, + 0.0172, + 0.0168, + 0.0165, + 0.0166, + 0.0163, + 0.0163, + 0.0163, + 0.016, + 0.0161, + 0.0157, + 0.0157, + 0.0152, + 0.0152, + 0.0153, + 0.0171, + 0.018 + ], + [ + 0.0606, + 0.0592, + 0.0578, + 0.0566, + 0.0557, + 0.0544, + 0.0538, + 0.053, + 0.0522, + 0.0512, + 0.0502, + 0.0494, + 0.0485, + 0.0463, + 0.045, + 0.0431, + 0.0412, + 0.0397, + 0.0382, + 0.0369, + 0.035, + 0.0397, + 0.0347, + 0.0343, + 0.0345, + 0.0339, + 0.0336, + 0.0332, + 0.0327, + 0.0318, + 0.0312, + 0.0304, + 0.0299, + 0.029, + 0.0284, + 0.0298, + 0.0304, + 0.0315, + 0.0308, + 0.0306, + 0.0298, + 0.0302, + 0.0314, + 0.0323, + 0.0322, + 0.033, + 0.0323, + 0.0321, + 0.0315, + 0.0309, + 0.0307, + 0.0303, + 0.0305, + 0.0302, + 0.0298, + 0.0298, + 0.0282, + 0.0277, + 0.0273, + 0.0274, + 0.027, + 0.027, + 0.027, + 0.0266, + 0.0267, + 0.0262, + 0.0262, + 0.0256, + 0.0256, + 0.0256, + 0.0289, + 0.0298 + ], + [ + 0.0606, + 0.0592, + 0.0578, + 0.0566, + 0.0557, + 0.0544, + 0.0538, + 0.053, + 0.0522, + 0.0512, + 0.0502, + 0.0494, + 0.0485, + 0.0463, + 0.045, + 0.0431, + 0.0412, + 0.0397, + 0.0382, + 0.0369, + 0.035, + 0.0397, + 0.0347, + 0.0343, + 0.0345, + 0.0339, + 0.0336, + 0.0332, + 0.0327, + 0.0318, + 0.0312, + 0.0304, + 0.0299, + 0.029, + 0.0284, + 0.0298, + 0.0304, + 0.0315, + 0.0308, + 0.0306, + 0.0298, + 0.0302, + 0.0314, + 0.0323, + 0.0322, + 0.033, + 0.0323, + 0.0321, + 0.0315, + 0.0309, + 0.0307, + 0.0303, + 0.0305, + 0.0302, + 0.0298, + 0.0298, + 0.0282, + 0.0277, + 0.0273, + 0.0274, + 0.027, + 0.027, + 0.027, + 0.0266, + 0.0267, + 0.0262, + 0.0262, + 0.0256, + 0.0256, + 0.0256, + 0.0289, + 0.0298 + ], + [ + 0.091, + 0.0891, + 0.0872, + 0.0855, + 0.0842, + 0.0824, + 0.0816, + 0.0807, + 0.0795, + 0.0782, + 0.0769, + 0.0758, + 0.0744, + 0.0714, + 0.0698, + 0.0672, + 0.0648, + 0.0629, + 0.0608, + 0.059, + 0.0566, + 0.0634, + 0.0562, + 0.0557, + 0.0559, + 0.0551, + 0.0547, + 0.0542, + 0.0535, + 0.0524, + 0.0516, + 0.0505, + 0.0498, + 0.0486, + 0.0479, + 0.0497, + 0.0505, + 0.0519, + 0.0511, + 0.0507, + 0.0496, + 0.0502, + 0.0519, + 0.0531, + 0.053, + 0.0539, + 0.053, + 0.0527, + 0.0519, + 0.0511, + 0.0509, + 0.0504, + 0.0506, + 0.0503, + 0.0497, + 0.0497, + 0.0476, + 0.0468, + 0.0462, + 0.0465, + 0.0459, + 0.0458, + 0.0459, + 0.0453, + 0.0454, + 0.0447, + 0.0447, + 0.0438, + 0.0438, + 0.0439, + 0.0482, + 0.0498 + ], + [ + 0.091, + 0.0891, + 0.0872, + 0.0855, + 0.0842, + 0.0824, + 0.0816, + 0.0807, + 0.0795, + 0.0782, + 0.0769, + 0.0758, + 0.0744, + 0.0714, + 0.0698, + 0.0672, + 0.0648, + 0.0629, + 0.0608, + 0.059, + 0.0566, + 0.0634, + 0.0562, + 0.0557, + 0.0559, + 0.0551, + 0.0547, + 0.0542, + 0.0535, + 0.0524, + 0.0516, + 0.0505, + 0.0498, + 0.0486, + 0.0479, + 0.0497, + 0.0505, + 0.0519, + 0.0511, + 0.0507, + 0.0496, + 0.0502, + 0.0519, + 0.0531, + 0.053, + 0.0539, + 0.053, + 0.0527, + 0.0519, + 0.0511, + 0.0509, + 0.0504, + 0.0506, + 0.0503, + 0.0497, + 0.0497, + 0.0476, + 0.0468, + 0.0462, + 0.0465, + 0.0459, + 0.0458, + 0.0459, + 0.0453, + 0.0454, + 0.0447, + 0.0447, + 0.0438, + 0.0438, + 0.0439, + 0.0482, + 0.0498 + ], + [ + 0.1352, + 0.1326, + 0.1301, + 0.1279, + 0.1262, + 0.1237, + 0.1228, + 0.1217, + 0.1202, + 0.1185, + 0.1168, + 0.1153, + 0.1135, + 0.1095, + 0.1077, + 0.1044, + 0.1012, + 0.099, + 0.0963, + 0.0942, + 0.0911, + 0.1002, + 0.0906, + 0.0899, + 0.0901, + 0.0891, + 0.0886, + 0.088, + 0.0871, + 0.0858, + 0.0848, + 0.0834, + 0.0825, + 0.081, + 0.08, + 0.0824, + 0.0834, + 0.0852, + 0.0842, + 0.0837, + 0.0823, + 0.0831, + 0.0852, + 0.0867, + 0.0865, + 0.0876, + 0.0866, + 0.0862, + 0.0852, + 0.0842, + 0.0839, + 0.0833, + 0.0836, + 0.0832, + 0.0824, + 0.0819, + 0.0796, + 0.0786, + 0.0779, + 0.0782, + 0.0774, + 0.0774, + 0.0775, + 0.0765, + 0.0768, + 0.0757, + 0.0758, + 0.0745, + 0.0746, + 0.0746, + 0.0782, + 0.0802 + ], + [ + 0.1352, + 0.1326, + 0.1301, + 0.1279, + 0.1262, + 0.1237, + 0.1228, + 0.1217, + 0.1202, + 0.1185, + 0.1168, + 0.1153, + 0.1135, + 0.1095, + 0.1077, + 0.1044, + 0.1012, + 0.099, + 0.0963, + 0.0942, + 0.0911, + 0.1002, + 0.0906, + 0.0899, + 0.0901, + 0.0891, + 0.0886, + 0.088, + 0.0871, + 0.0858, + 0.0848, + 0.0834, + 0.0825, + 0.081, + 0.08, + 0.0824, + 0.0834, + 0.0852, + 0.0842, + 0.0837, + 0.0823, + 0.0831, + 0.0852, + 0.0867, + 0.0865, + 0.0876, + 0.0866, + 0.0862, + 0.0852, + 0.0842, + 0.0839, + 0.0833, + 0.0836, + 0.0832, + 0.0824, + 0.0819, + 0.0796, + 0.0786, + 0.0779, + 0.0782, + 0.0774, + 0.0774, + 0.0775, + 0.0765, + 0.0768, + 0.0757, + 0.0758, + 0.0745, + 0.0746, + 0.0746, + 0.0782, + 0.0802 + ], + [ + 0.1993, + 0.196, + 0.1926, + 0.1898, + 0.1875, + 0.1843, + 0.1833, + 0.1821, + 0.1803, + 0.1782, + 0.176, + 0.1742, + 0.1718, + 0.1666, + 0.1648, + 0.1608, + 0.1572, + 0.1547, + 0.1515, + 0.1491, + 0.1457, + 0.1548, + 0.1451, + 0.1442, + 0.1444, + 0.1433, + 0.1427, + 0.1421, + 0.1412, + 0.1398, + 0.1387, + 0.1373, + 0.1363, + 0.1345, + 0.1335, + 0.1362, + 0.1373, + 0.1392, + 0.138, + 0.1375, + 0.1361, + 0.1369, + 0.1391, + 0.1407, + 0.1405, + 0.1417, + 0.1406, + 0.1402, + 0.1392, + 0.1381, + 0.1378, + 0.1371, + 0.1374, + 0.137, + 0.1361, + 0.1351, + 0.1329, + 0.1318, + 0.1309, + 0.1312, + 0.1303, + 0.1303, + 0.1304, + 0.1294, + 0.1295, + 0.1284, + 0.1284, + 0.1269, + 0.1269, + 0.127, + 0.1323, + 0.1357 + ], + [ + 0.1993, + 0.196, + 0.1926, + 0.1898, + 0.1875, + 0.1843, + 0.1833, + 0.1821, + 0.1803, + 0.1782, + 0.176, + 0.1742, + 0.1718, + 0.1666, + 0.1648, + 0.1608, + 0.1572, + 0.1547, + 0.1515, + 0.1491, + 0.1457, + 0.1548, + 0.1451, + 0.1442, + 0.1444, + 0.1433, + 0.1427, + 0.1421, + 0.1412, + 0.1398, + 0.1387, + 0.1373, + 0.1363, + 0.1345, + 0.1335, + 0.1362, + 0.1373, + 0.1392, + 0.138, + 0.1375, + 0.1361, + 0.1369, + 0.1391, + 0.1407, + 0.1405, + 0.1417, + 0.1406, + 0.1402, + 0.1392, + 0.1381, + 0.1378, + 0.1371, + 0.1374, + 0.137, + 0.1361, + 0.1351, + 0.1329, + 0.1318, + 0.1309, + 0.1312, + 0.1303, + 0.1303, + 0.1304, + 0.1294, + 0.1295, + 0.1284, + 0.1284, + 0.1269, + 0.1269, + 0.127, + 0.1323, + 0.1357 + ], + [ + 0.2793, + 0.2754, + 0.2713, + 0.2679, + 0.2652, + 0.2613, + 0.2604, + 0.2593, + 0.2575, + 0.2551, + 0.2526, + 0.2506, + 0.2474, + 0.2413, + 0.2401, + 0.2356, + 0.2318, + 0.2296, + 0.226, + 0.2238, + 0.2204, + 0.2294, + 0.2199, + 0.2188, + 0.2191, + 0.2179, + 0.2173, + 0.2166, + 0.2159, + 0.2149, + 0.2137, + 0.2123, + 0.2113, + 0.2095, + 0.2084, + 0.2111, + 0.2123, + 0.2142, + 0.2131, + 0.2125, + 0.211, + 0.212, + 0.2142, + 0.2156, + 0.2154, + 0.2164, + 0.2155, + 0.2151, + 0.2142, + 0.2132, + 0.2128, + 0.2121, + 0.2124, + 0.212, + 0.2111, + 0.2097, + 0.2078, + 0.2066, + 0.2058, + 0.2061, + 0.2051, + 0.2051, + 0.2052, + 0.204, + 0.2042, + 0.2029, + 0.203, + 0.2012, + 0.2013, + 0.2013, + 0.2124, + 0.2189 + ], + [ + 0.2793, + 0.2754, + 0.2713, + 0.2679, + 0.2652, + 0.2613, + 0.2604, + 0.2593, + 0.2575, + 0.2551, + 0.2526, + 0.2506, + 0.2474, + 0.2413, + 0.2401, + 0.2356, + 0.2318, + 0.2296, + 0.226, + 0.2238, + 0.2204, + 0.2294, + 0.2199, + 0.2188, + 0.2191, + 0.2179, + 0.2173, + 0.2166, + 0.2159, + 0.2149, + 0.2137, + 0.2123, + 0.2113, + 0.2095, + 0.2084, + 0.2111, + 0.2123, + 0.2142, + 0.2131, + 0.2125, + 0.211, + 0.212, + 0.2142, + 0.2156, + 0.2154, + 0.2164, + 0.2155, + 0.2151, + 0.2142, + 0.2132, + 0.2128, + 0.2121, + 0.2124, + 0.212, + 0.2111, + 0.2097, + 0.2078, + 0.2066, + 0.2058, + 0.2061, + 0.2051, + 0.2051, + 0.2052, + 0.204, + 0.2042, + 0.2029, + 0.203, + 0.2012, + 0.2013, + 0.2013, + 0.2124, + 0.2189 + ], + [ + 0.3936, + 0.3891, + 0.3842, + 0.3802, + 0.377, + 0.3726, + 0.3721, + 0.3715, + 0.3698, + 0.3673, + 0.3645, + 0.3624, + 0.3585, + 0.3513, + 0.3516, + 0.347, + 0.3434, + 0.3421, + 0.3386, + 0.3369, + 0.3344, + 0.3433, + 0.334, + 0.3329, + 0.3332, + 0.332, + 0.3315, + 0.331, + 0.3309, + 0.3307, + 0.3302, + 0.3297, + 0.3293, + 0.3281, + 0.3273, + 0.3291, + 0.3298, + 0.3303, + 0.3301, + 0.3298, + 0.3291, + 0.3297, + 0.3304, + 0.3311, + 0.3307, + 0.3311, + 0.3308, + 0.3306, + 0.3304, + 0.33, + 0.33, + 0.3296, + 0.3298, + 0.3296, + 0.3292, + 0.3276, + 0.3265, + 0.3256, + 0.325, + 0.3253, + 0.3244, + 0.3244, + 0.3244, + 0.3068, + 0.3235, + 0.3058, + 0.3058, + 0.304, + 0.3041, + 0.3042, + 0.3158, + 0.3227 + ], + [ + 0.3936, + 0.3891, + 0.3842, + 0.3802, + 0.377, + 0.3726, + 0.3721, + 0.3715, + 0.3698, + 0.3673, + 0.3645, + 0.3624, + 0.3585, + 0.3513, + 0.3516, + 0.347, + 0.3434, + 0.3421, + 0.3386, + 0.3369, + 0.3344, + 0.3433, + 0.334, + 0.3329, + 0.3332, + 0.332, + 0.3315, + 0.331, + 0.3309, + 0.3307, + 0.3302, + 0.3297, + 0.3293, + 0.3281, + 0.3273, + 0.3291, + 0.3298, + 0.3303, + 0.3301, + 0.3298, + 0.3291, + 0.3297, + 0.3304, + 0.3311, + 0.3307, + 0.3311, + 0.3308, + 0.3306, + 0.3304, + 0.33, + 0.33, + 0.3296, + 0.3298, + 0.3296, + 0.3292, + 0.3276, + 0.3265, + 0.3256, + 0.325, + 0.3253, + 0.3244, + 0.3244, + 0.3244, + 0.3068, + 0.3235, + 0.3058, + 0.3058, + 0.304, + 0.3041, + 0.3042, + 0.3158, + 0.3227 + ], + [ + 0.5101, + 0.5056, + 0.5001, + 0.496, + 0.4926, + 0.4879, + 0.4883, + 0.4886, + 0.4876, + 0.4854, + 0.4825, + 0.4805, + 0.4758, + 0.4683, + 0.471, + 0.4673, + 0.4643, + 0.4647, + 0.4617, + 0.4613, + 0.4605, + 0.4697, + 0.4603, + 0.4594, + 0.4596, + 0.4587, + 0.4584, + 0.4582, + 0.459, + 0.4603, + 0.4605, + 0.4608, + 0.461, + 0.4607, + 0.4604, + 0.4607, + 0.4609, + 0.4602, + 0.4608, + 0.4606, + 0.4609, + 0.4611, + 0.4604, + 0.4599, + 0.4595, + 0.4588, + 0.4596, + 0.4597, + 0.4604, + 0.4606, + 0.4608, + 0.4609, + 0.4609, + 0.4609, + 0.4608, + 0.4598, + 0.4598, + 0.4595, + 0.4594, + 0.4596, + 0.459, + 0.4589, + 0.4588, + 0.4582, + 0.4583, + 0.4575, + 0.4576, + 0.4561, + 0.4562, + 0.4562, + 0.4681, + 0.475 + ], + [ + 0.5101, + 0.5056, + 0.5001, + 0.496, + 0.4926, + 0.4879, + 0.4883, + 0.4886, + 0.4876, + 0.4854, + 0.4825, + 0.4805, + 0.4758, + 0.4683, + 0.471, + 0.4673, + 0.4643, + 0.4647, + 0.4617, + 0.4613, + 0.4605, + 0.4697, + 0.4603, + 0.4594, + 0.4596, + 0.4587, + 0.4584, + 0.4582, + 0.459, + 0.4603, + 0.4605, + 0.4608, + 0.461, + 0.4607, + 0.4604, + 0.4607, + 0.4609, + 0.4602, + 0.4608, + 0.4606, + 0.4609, + 0.4611, + 0.4604, + 0.4599, + 0.4595, + 0.4588, + 0.4596, + 0.4597, + 0.4604, + 0.4606, + 0.4608, + 0.4609, + 0.4609, + 0.4609, + 0.4608, + 0.4598, + 0.4598, + 0.4595, + 0.4594, + 0.4596, + 0.459, + 0.4589, + 0.4588, + 0.4582, + 0.4583, + 0.4575, + 0.4576, + 0.4561, + 0.4562, + 0.4562, + 0.4681, + 0.475 + ], + [ + 0.6309, + 0.6271, + 0.6217, + 0.6182, + 0.6151, + 0.6111, + 0.6126, + 0.6142, + 0.6145, + 0.613, + 0.6108, + 0.6095, + 0.6051, + 0.5979, + 0.6037, + 0.602, + 0.6005, + 0.6031, + 0.6012, + 0.6024, + 0.604, + 0.6133, + 0.6042, + 0.6035, + 0.6039, + 0.6032, + 0.6034, + 0.6036, + 0.6055, + 0.6085, + 0.6095, + 0.611, + 0.6118, + 0.6125, + 0.613, + 0.6116, + 0.6111, + 0.6089, + 0.6103, + 0.6105, + 0.6118, + 0.6116, + 0.6092, + 0.6073, + 0.6068, + 0.6048, + 0.6068, + 0.6073, + 0.609, + 0.6101, + 0.6106, + 0.6111, + 0.611, + 0.6112, + 0.6117, + 0.6104, + 0.6119, + 0.6121, + 0.6128, + 0.6128, + 0.6128, + 0.6126, + 0.6123, + 0.6127, + 0.6124, + 0.6127, + 0.6128, + 0.612, + 0.6122, + 0.612, + 0.6244, + 0.6314 + ], + [ + 0.6309, + 0.6271, + 0.6217, + 0.6182, + 0.6151, + 0.6111, + 0.6126, + 0.6142, + 0.6145, + 0.613, + 0.6108, + 0.6095, + 0.6051, + 0.5979, + 0.6037, + 0.602, + 0.6005, + 0.6031, + 0.6012, + 0.6024, + 0.604, + 0.6133, + 0.6042, + 0.6035, + 0.6039, + 0.6032, + 0.6034, + 0.6036, + 0.6055, + 0.6085, + 0.6095, + 0.611, + 0.6118, + 0.6125, + 0.613, + 0.6116, + 0.6111, + 0.6089, + 0.6103, + 0.6105, + 0.6118, + 0.6116, + 0.6092, + 0.6073, + 0.6068, + 0.6048, + 0.6068, + 0.6073, + 0.609, + 0.6101, + 0.6106, + 0.6111, + 0.611, + 0.6112, + 0.6117, + 0.6104, + 0.6119, + 0.6121, + 0.6128, + 0.6128, + 0.6128, + 0.6126, + 0.6123, + 0.6127, + 0.6124, + 0.6127, + 0.6128, + 0.612, + 0.6122, + 0.612, + 0.6244, + 0.6314 + ] + ] + }, + "FertilityDistribution": { + "NumDistributionAxes": 2, + "AxisNames": [ + "age", + "year" + ], + "AxisUnits": [ + "years", + "simulation year" + ], + "AxisScaleFactors": [ + 365, + 1 + ], + "NumPopulationGroups": [ + 18, + 144 + ], + "PopulationGroups": [ + [ + 10, + 14.9999, + 15, + 19.9999, + 20, + 24.9999, + 25, + 29.9999, + 30, + 34.9999, + 35, + 39.9999, + 40, + 44.9999, + 45, + 49.9999, + 50, + 54.9999 + ], + [ + 1950, + 1950.999, + 1951, + 1951.999, + 1952, + 1952.999, + 1953, + 1953.999, + 1954, + 1954.999, + 1955, + 1955.999, + 1956, + 1956.999, + 1957, + 1957.999, + 1958, + 1958.999, + 1959, + 1959.999, + 1960, + 1960.999, + 1961, + 1961.999, + 1962, + 1962.999, + 1963, + 1963.999, + 1964, + 1964.999, + 1965, + 1965.999, + 1966, + 1966.999, + 1967, + 1967.999, + 1968, + 1968.999, + 1969, + 1969.999, + 1970, + 1970.999, + 1971, + 1971.999, + 1972, + 1972.999, + 1973, + 1973.999, + 1974, + 1974.999, + 1975, + 1975.999, + 1976, + 1976.999, + 1977, + 1977.999, + 1978, + 1978.999, + 1979, + 1979.999, + 1980, + 1980.999, + 1981, + 1981.999, + 1982, + 1982.999, + 1983, + 1983.999, + 1984, + 1984.999, + 1985, + 1985.999, + 1986, + 1986.999, + 1987, + 1987.999, + 1988, + 1988.999, + 1989, + 1989.999, + 1990, + 1990.999, + 1991, + 1991.999, + 1992, + 1992.999, + 1993, + 1993.999, + 1994, + 1994.999, + 1995, + 1995.999, + 1996, + 1996.999, + 1997, + 1997.999, + 1998, + 1998.999, + 1999, + 1999.999, + 2000, + 2000.999, + 2001, + 2001.999, + 2002, + 2002.999, + 2003, + 2003.999, + 2004, + 2004.999, + 2005, + 2005.999, + 2006, + 2006.999, + 2007, + 2007.999, + 2008, + 2008.999, + 2009, + 2009.999, + 2010, + 2010.999, + 2011, + 2011.999, + 2012, + 2012.999, + 2013, + 2013.999, + 2014, + 2014.999, + 2015, + 2015.999, + 2016, + 2016.999, + 2017, + 2017.999, + 2018, + 2018.999, + 2019, + 2019.999, + 2020, + 2020.999, + 2021, + 2021.999 + ] + ], + "ResultUnits": "annual births per 1000 individuals", + "ResultScaleFactor": 0.0000027397, + "ResultValues": [ + [ + 13.16, + 13.16, + 12.98, + 12.98, + 12.94, + 12.94, + 12.9, + 12.9, + 12.02, + 12.02, + 11.19, + 11.19, + 10.73, + 10.73, + 10.4, + 10.4, + 9.98, + 9.98, + 9.28, + 9.28, + 8.98, + 8.98, + 8.77, + 8.77, + 8.63, + 8.63, + 8.44, + 8.44, + 7.67, + 7.67, + 7.16, + 7.16, + 6.64, + 6.64, + 6.31, + 6.31, + 6.1, + 6.1, + 5.58, + 5.58, + 5.38, + 5.38, + 5.33, + 5.33, + 5.22, + 5.22, + 5.05, + 5.05, + 4.82, + 4.82, + 4.6, + 4.6, + 4.43, + 4.43, + 4.49, + 4.49, + 4.61, + 4.61, + 4.81, + 4.81, + 5.07, + 5.07, + 5.26, + 5.26, + 5.17, + 5.17, + 5.13, + 5.13, + 5.12, + 5.12, + 5.23, + 5.23, + 5.49, + 5.49, + 5.74, + 5.74, + 5.59, + 5.59, + 5.55, + 5.55, + 4.94, + 4.94, + 4.43, + 4.43, + 3.98, + 3.98, + 3.83, + 3.83, + 3.65, + 3.65, + 3.17, + 3.17, + 3.14, + 3.14, + 2.93, + 2.93, + 2.81, + 2.81, + 2.66, + 2.66, + 2.56, + 2.56, + 2.34, + 2.34, + 2.17, + 2.17, + 2.01, + 2.01, + 1.84, + 1.84, + 1.7, + 1.7, + 1.52, + 1.52, + 1.47, + 1.47, + 1.24, + 1.24, + 1.08, + 1.08, + 0.99, + 0.99, + 0.79, + 0.79, + 0.85, + 0.85, + 0.88, + 0.88, + 0.87, + 0.87, + 0.85, + 0.85, + 0.83, + 0.83, + 0.8, + 0.8, + 0.75, + 0.75, + 0.7, + 0.7, + 0.66, + 0.66, + 0.61, + 0.61 + ], + [ + 13.16, + 13.16, + 12.98, + 12.98, + 12.94, + 12.94, + 12.9, + 12.9, + 12.02, + 12.02, + 11.19, + 11.19, + 10.73, + 10.73, + 10.4, + 10.4, + 9.98, + 9.98, + 9.28, + 9.28, + 8.98, + 8.98, + 8.77, + 8.77, + 8.63, + 8.63, + 8.44, + 8.44, + 7.67, + 7.67, + 7.16, + 7.16, + 6.64, + 6.64, + 6.31, + 6.31, + 6.1, + 6.1, + 5.58, + 5.58, + 5.38, + 5.38, + 5.33, + 5.33, + 5.22, + 5.22, + 5.05, + 5.05, + 4.82, + 4.82, + 4.6, + 4.6, + 4.43, + 4.43, + 4.49, + 4.49, + 4.61, + 4.61, + 4.81, + 4.81, + 5.07, + 5.07, + 5.26, + 5.26, + 5.17, + 5.17, + 5.13, + 5.13, + 5.12, + 5.12, + 5.23, + 5.23, + 5.49, + 5.49, + 5.74, + 5.74, + 5.59, + 5.59, + 5.55, + 5.55, + 4.94, + 4.94, + 4.43, + 4.43, + 3.98, + 3.98, + 3.83, + 3.83, + 3.65, + 3.65, + 3.17, + 3.17, + 3.14, + 3.14, + 2.93, + 2.93, + 2.81, + 2.81, + 2.66, + 2.66, + 2.56, + 2.56, + 2.34, + 2.34, + 2.17, + 2.17, + 2.01, + 2.01, + 1.84, + 1.84, + 1.7, + 1.7, + 1.52, + 1.52, + 1.47, + 1.47, + 1.24, + 1.24, + 1.08, + 1.08, + 0.99, + 0.99, + 0.79, + 0.79, + 0.85, + 0.85, + 0.88, + 0.88, + 0.87, + 0.87, + 0.85, + 0.85, + 0.83, + 0.83, + 0.8, + 0.8, + 0.75, + 0.75, + 0.7, + 0.7, + 0.66, + 0.66, + 0.61, + 0.61 + ], + [ + 179.69, + 179.69, + 179.27, + 179.27, + 179.43, + 179.43, + 178.56, + 178.56, + 174.37, + 174.37, + 169.67, + 169.67, + 166.38, + 166.38, + 163.61, + 163.61, + 160.58, + 160.58, + 156.51, + 156.51, + 153.8, + 153.8, + 152.1, + 152.1, + 151.18, + 151.18, + 150.24, + 150.24, + 147.23, + 147.23, + 145, + 145, + 141.64, + 141.64, + 137.84, + 137.84, + 132.83, + 132.83, + 124.23, + 124.23, + 121.58, + 121.58, + 119.96, + 119.96, + 117.98, + 117.98, + 116.55, + 116.55, + 111.93, + 111.93, + 107.52, + 107.52, + 103.23, + 103.23, + 101.88, + 101.88, + 101.47, + 101.47, + 102.2, + 102.2, + 102.94, + 102.94, + 103.36, + 103.36, + 101.86, + 101.86, + 100.72, + 100.72, + 100, + 100, + 101.55, + 101.55, + 103.82, + 103.82, + 105.75, + 105.75, + 104.9, + 104.9, + 105, + 105, + 100.52, + 100.52, + 96.66, + 96.66, + 92.54, + 92.54, + 92.41, + 92.41, + 91.65, + 91.65, + 86.71, + 86.71, + 85.48, + 85.48, + 81.26, + 81.26, + 79.98, + 79.98, + 76.83, + 76.83, + 74.75, + 74.75, + 72.27, + 72.27, + 69.32, + 69.32, + 67.36, + 67.36, + 65.28, + 65.28, + 61.91, + 61.91, + 60.17, + 60.17, + 59.8, + 59.8, + 57.19, + 57.19, + 55.43, + 55.43, + 54.13, + 54.13, + 51.12, + 51.12, + 50.55, + 50.55, + 49.98, + 49.98, + 49.02, + 49.02, + 47.58, + 47.58, + 46.74, + 46.74, + 45.8, + 45.8, + 44.91, + 44.91, + 43.89, + 43.89, + 43.21, + 43.21, + 42.27, + 42.27 + ], + [ + 179.69, + 179.69, + 179.27, + 179.27, + 179.43, + 179.43, + 178.56, + 178.56, + 174.37, + 174.37, + 169.67, + 169.67, + 166.38, + 166.38, + 163.61, + 163.61, + 160.58, + 160.58, + 156.51, + 156.51, + 153.8, + 153.8, + 152.1, + 152.1, + 151.18, + 151.18, + 150.24, + 150.24, + 147.23, + 147.23, + 145, + 145, + 141.64, + 141.64, + 137.84, + 137.84, + 132.83, + 132.83, + 124.23, + 124.23, + 121.58, + 121.58, + 119.96, + 119.96, + 117.98, + 117.98, + 116.55, + 116.55, + 111.93, + 111.93, + 107.52, + 107.52, + 103.23, + 103.23, + 101.88, + 101.88, + 101.47, + 101.47, + 102.2, + 102.2, + 102.94, + 102.94, + 103.36, + 103.36, + 101.86, + 101.86, + 100.72, + 100.72, + 100, + 100, + 101.55, + 101.55, + 103.82, + 103.82, + 105.75, + 105.75, + 104.9, + 104.9, + 105, + 105, + 100.52, + 100.52, + 96.66, + 96.66, + 92.54, + 92.54, + 92.41, + 92.41, + 91.65, + 91.65, + 86.71, + 86.71, + 85.48, + 85.48, + 81.26, + 81.26, + 79.98, + 79.98, + 76.83, + 76.83, + 74.75, + 74.75, + 72.27, + 72.27, + 69.32, + 69.32, + 67.36, + 67.36, + 65.28, + 65.28, + 61.91, + 61.91, + 60.17, + 60.17, + 59.8, + 59.8, + 57.19, + 57.19, + 55.43, + 55.43, + 54.13, + 54.13, + 51.12, + 51.12, + 50.55, + 50.55, + 49.98, + 49.98, + 49.02, + 49.02, + 47.58, + 47.58, + 46.74, + 46.74, + 45.8, + 45.8, + 44.91, + 44.91, + 43.89, + 43.89, + 43.21, + 43.21, + 42.27, + 42.27 + ], + [ + 257.46, + 257.46, + 256.94, + 256.94, + 256.76, + 256.76, + 256.29, + 256.29, + 260.5, + 260.5, + 264.82, + 264.82, + 269.11, + 269.11, + 272.93, + 272.93, + 277.19, + 277.19, + 279.93, + 279.93, + 280.78, + 280.78, + 280.81, + 280.81, + 282.27, + 282.27, + 283.22, + 283.22, + 284.97, + 284.97, + 286.43, + 286.43, + 288, + 288, + 289.81, + 289.81, + 287.55, + 287.55, + 283.1, + 283.1, + 284.38, + 284.38, + 283.65, + 283.65, + 282.57, + 282.57, + 282.33, + 282.33, + 280.42, + 280.42, + 278.69, + 278.69, + 275.5, + 275.5, + 273.96, + 273.96, + 270.71, + 270.71, + 267.86, + 267.86, + 264.04, + 264.04, + 261.26, + 261.26, + 258.22, + 258.22, + 255.82, + 255.82, + 252.65, + 252.65, + 255.02, + 255.02, + 256.81, + 256.81, + 257.67, + 257.67, + 258.86, + 258.86, + 260.08, + 260.08, + 261.5, + 261.5, + 259.68, + 259.68, + 257.17, + 257.17, + 259.12, + 259.12, + 258.65, + 258.65, + 258.21, + 258.21, + 254.19, + 254.19, + 247.56, + 247.56, + 246.37, + 246.37, + 241.14, + 241.14, + 232.69, + 232.69, + 227.83, + 227.83, + 221.9, + 221.9, + 218.15, + 218.15, + 214.44, + 214.44, + 209.29, + 209.29, + 207.19, + 207.19, + 207.54, + 207.54, + 205.77, + 205.77, + 205.38, + 205.38, + 205.06, + 205.06, + 200.77, + 200.77, + 196.63, + 196.63, + 192.8, + 192.8, + 188.93, + 188.93, + 184.07, + 184.07, + 181.46, + 181.46, + 179.93, + 179.93, + 177.96, + 177.96, + 175.9, + 175.9, + 174.64, + 174.64, + 172.24, + 172.24 + ], + [ + 257.46, + 257.46, + 256.94, + 256.94, + 256.76, + 256.76, + 256.29, + 256.29, + 260.5, + 260.5, + 264.82, + 264.82, + 269.11, + 269.11, + 272.93, + 272.93, + 277.19, + 277.19, + 279.93, + 279.93, + 280.78, + 280.78, + 280.81, + 280.81, + 282.27, + 282.27, + 283.22, + 283.22, + 284.97, + 284.97, + 286.43, + 286.43, + 288, + 288, + 289.81, + 289.81, + 287.55, + 287.55, + 283.1, + 283.1, + 284.38, + 284.38, + 283.65, + 283.65, + 282.57, + 282.57, + 282.33, + 282.33, + 280.42, + 280.42, + 278.69, + 278.69, + 275.5, + 275.5, + 273.96, + 273.96, + 270.71, + 270.71, + 267.86, + 267.86, + 264.04, + 264.04, + 261.26, + 261.26, + 258.22, + 258.22, + 255.82, + 255.82, + 252.65, + 252.65, + 255.02, + 255.02, + 256.81, + 256.81, + 257.67, + 257.67, + 258.86, + 258.86, + 260.08, + 260.08, + 261.5, + 261.5, + 259.68, + 259.68, + 257.17, + 257.17, + 259.12, + 259.12, + 258.65, + 258.65, + 258.21, + 258.21, + 254.19, + 254.19, + 247.56, + 247.56, + 246.37, + 246.37, + 241.14, + 241.14, + 232.69, + 232.69, + 227.83, + 227.83, + 221.9, + 221.9, + 218.15, + 218.15, + 214.44, + 214.44, + 209.29, + 209.29, + 207.19, + 207.19, + 207.54, + 207.54, + 205.77, + 205.77, + 205.38, + 205.38, + 205.06, + 205.06, + 200.77, + 200.77, + 196.63, + 196.63, + 192.8, + 192.8, + 188.93, + 188.93, + 184.07, + 184.07, + 181.46, + 181.46, + 179.93, + 179.93, + 177.96, + 177.96, + 175.9, + 175.9, + 174.64, + 174.64, + 172.24, + 172.24 + ], + [ + 301.84, + 301.84, + 301.69, + 301.69, + 301.17, + 301.17, + 301.17, + 301.17, + 301.83, + 301.83, + 302.3, + 302.3, + 302.07, + 302.07, + 301.34, + 301.34, + 301.82, + 301.82, + 303.6, + 303.6, + 306.34, + 306.34, + 307.91, + 307.91, + 308.4, + 308.4, + 308.94, + 308.94, + 311.14, + 311.14, + 313.59, + 313.59, + 315.26, + 315.26, + 316.24, + 316.24, + 315.16, + 315.16, + 314.49, + 314.49, + 315.94, + 315.94, + 317.98, + 317.98, + 320.04, + 320.04, + 322.34, + 322.34, + 322.8, + 322.8, + 323.53, + 323.53, + 322.55, + 322.55, + 321.46, + 321.46, + 320.14, + 320.14, + 317.26, + 317.26, + 315.63, + 315.63, + 313.37, + 313.37, + 311.24, + 311.24, + 309.41, + 309.41, + 307.58, + 307.58, + 307.32, + 307.32, + 307.34, + 307.34, + 307.37, + 307.37, + 304.62, + 304.62, + 301.33, + 301.33, + 300.37, + 300.37, + 301.25, + 301.25, + 302.17, + 302.17, + 301.92, + 301.92, + 298.66, + 298.66, + 296.25, + 296.25, + 290.95, + 290.95, + 285.74, + 285.74, + 282.89, + 282.89, + 280.08, + 280.08, + 275.09, + 275.09, + 273.1, + 273.1, + 273.29, + 273.29, + 271.07, + 271.07, + 268.98, + 268.98, + 261.84, + 261.84, + 257.75, + 257.75, + 258.3, + 258.3, + 257.85, + 257.85, + 255.09, + 255.09, + 252.09, + 252.09, + 248.83, + 248.83, + 247.73, + 247.73, + 245.09, + 245.09, + 239.53, + 239.53, + 234.74, + 234.74, + 231.29, + 231.29, + 227.96, + 227.96, + 224.63, + 224.63, + 221.39, + 221.39, + 218.35, + 218.35, + 213.99, + 213.99 + ], + [ + 301.84, + 301.84, + 301.69, + 301.69, + 301.17, + 301.17, + 301.17, + 301.17, + 301.83, + 301.83, + 302.3, + 302.3, + 302.07, + 302.07, + 301.34, + 301.34, + 301.82, + 301.82, + 303.6, + 303.6, + 306.34, + 306.34, + 307.91, + 307.91, + 308.4, + 308.4, + 308.94, + 308.94, + 311.14, + 311.14, + 313.59, + 313.59, + 315.26, + 315.26, + 316.24, + 316.24, + 315.16, + 315.16, + 314.49, + 314.49, + 315.94, + 315.94, + 317.98, + 317.98, + 320.04, + 320.04, + 322.34, + 322.34, + 322.8, + 322.8, + 323.53, + 323.53, + 322.55, + 322.55, + 321.46, + 321.46, + 320.14, + 320.14, + 317.26, + 317.26, + 315.63, + 315.63, + 313.37, + 313.37, + 311.24, + 311.24, + 309.41, + 309.41, + 307.58, + 307.58, + 307.32, + 307.32, + 307.34, + 307.34, + 307.37, + 307.37, + 304.62, + 304.62, + 301.33, + 301.33, + 300.37, + 300.37, + 301.25, + 301.25, + 302.17, + 302.17, + 301.92, + 301.92, + 298.66, + 298.66, + 296.25, + 296.25, + 290.95, + 290.95, + 285.74, + 285.74, + 282.89, + 282.89, + 280.08, + 280.08, + 275.09, + 275.09, + 273.1, + 273.1, + 273.29, + 273.29, + 271.07, + 271.07, + 268.98, + 268.98, + 261.84, + 261.84, + 257.75, + 257.75, + 258.3, + 258.3, + 257.85, + 257.85, + 255.09, + 255.09, + 252.09, + 252.09, + 248.83, + 248.83, + 247.73, + 247.73, + 245.09, + 245.09, + 239.53, + 239.53, + 234.74, + 234.74, + 231.29, + 231.29, + 227.96, + 227.96, + 224.63, + 224.63, + 221.39, + 221.39, + 218.35, + 218.35, + 213.99, + 213.99 + ], + [ + 269.87, + 269.87, + 270.68, + 270.68, + 271.72, + 271.72, + 271.92, + 271.92, + 271.62, + 271.62, + 271.87, + 271.87, + 271.69, + 271.69, + 271.29, + 271.29, + 270.29, + 270.29, + 269.16, + 269.16, + 269.51, + 269.51, + 269.93, + 269.93, + 270.73, + 270.73, + 271.75, + 271.75, + 272.26, + 272.26, + 272.54, + 272.54, + 272.52, + 272.52, + 272.8, + 272.8, + 272.92, + 272.92, + 274.7, + 274.7, + 275.1, + 275.1, + 275.74, + 275.74, + 275.76, + 275.76, + 277.18, + 277.18, + 278.33, + 278.33, + 279.18, + 279.18, + 280.75, + 280.75, + 280.52, + 280.52, + 279.14, + 279.14, + 277.82, + 277.82, + 276.17, + 276.17, + 273.25, + 273.25, + 272.8, + 272.8, + 270.97, + 270.97, + 269.77, + 269.77, + 269.68, + 269.68, + 268.59, + 268.59, + 267.73, + 267.73, + 268.37, + 268.37, + 268.04, + 268.04, + 265.65, + 265.65, + 263.47, + 263.47, + 260.62, + 260.62, + 255.36, + 255.36, + 250.98, + 250.98, + 246.98, + 246.98, + 240.75, + 240.75, + 237.21, + 237.21, + 234.53, + 234.53, + 232.31, + 232.31, + 229.2, + 229.2, + 223.44, + 223.44, + 217.89, + 217.89, + 211.18, + 211.18, + 205.95, + 205.95, + 202.72, + 202.72, + 199.32, + 199.32, + 199.4, + 199.4, + 197.21, + 197.21, + 194.99, + 194.99, + 192.84, + 192.84, + 195.12, + 195.12, + 190.93, + 190.93, + 188.14, + 188.14, + 184.3, + 184.3, + 180.38, + 180.38, + 176.42, + 176.42, + 172.31, + 172.31, + 168.3, + 168.3, + 165.33, + 165.33, + 160.87, + 160.87, + 155.84, + 155.84 + ], + [ + 269.87, + 269.87, + 270.68, + 270.68, + 271.72, + 271.72, + 271.92, + 271.92, + 271.62, + 271.62, + 271.87, + 271.87, + 271.69, + 271.69, + 271.29, + 271.29, + 270.29, + 270.29, + 269.16, + 269.16, + 269.51, + 269.51, + 269.93, + 269.93, + 270.73, + 270.73, + 271.75, + 271.75, + 272.26, + 272.26, + 272.54, + 272.54, + 272.52, + 272.52, + 272.8, + 272.8, + 272.92, + 272.92, + 274.7, + 274.7, + 275.1, + 275.1, + 275.74, + 275.74, + 275.76, + 275.76, + 277.18, + 277.18, + 278.33, + 278.33, + 279.18, + 279.18, + 280.75, + 280.75, + 280.52, + 280.52, + 279.14, + 279.14, + 277.82, + 277.82, + 276.17, + 276.17, + 273.25, + 273.25, + 272.8, + 272.8, + 270.97, + 270.97, + 269.77, + 269.77, + 269.68, + 269.68, + 268.59, + 268.59, + 267.73, + 267.73, + 268.37, + 268.37, + 268.04, + 268.04, + 265.65, + 265.65, + 263.47, + 263.47, + 260.62, + 260.62, + 255.36, + 255.36, + 250.98, + 250.98, + 246.98, + 246.98, + 240.75, + 240.75, + 237.21, + 237.21, + 234.53, + 234.53, + 232.31, + 232.31, + 229.2, + 229.2, + 223.44, + 223.44, + 217.89, + 217.89, + 211.18, + 211.18, + 205.95, + 205.95, + 202.72, + 202.72, + 199.32, + 199.32, + 199.4, + 199.4, + 197.21, + 197.21, + 194.99, + 194.99, + 192.84, + 192.84, + 195.12, + 195.12, + 190.93, + 190.93, + 188.14, + 188.14, + 184.3, + 184.3, + 180.38, + 180.38, + 176.42, + 176.42, + 172.31, + 172.31, + 168.3, + 168.3, + 165.33, + 165.33, + 160.87, + 160.87, + 155.84, + 155.84 + ], + [ + 204.68, + 204.68, + 205.36, + 205.36, + 204.76, + 204.76, + 204.94, + 204.94, + 205.27, + 205.27, + 205.23, + 205.23, + 204.59, + 204.59, + 203.87, + 203.87, + 203.69, + 203.69, + 202.86, + 202.86, + 201.72, + 201.72, + 200.81, + 200.81, + 200.85, + 200.85, + 201.16, + 201.16, + 200.51, + 200.51, + 200.19, + 200.19, + 200.8, + 200.8, + 201.36, + 201.36, + 202.95, + 202.95, + 207.33, + 207.33, + 207.34, + 207.34, + 207.18, + 207.18, + 208.26, + 208.26, + 208.57, + 208.57, + 210.05, + 210.05, + 211.58, + 211.58, + 212.49, + 212.49, + 213.29, + 213.29, + 214.48, + 214.48, + 215.14, + 215.14, + 215.47, + 215.47, + 215.86, + 215.86, + 216.04, + 216.04, + 216.06, + 216.06, + 216.42, + 216.42, + 212.21, + 212.21, + 208.12, + 208.12, + 204.55, + 204.55, + 202.71, + 202.71, + 199.78, + 199.78, + 197.73, + 197.73, + 194.92, + 194.92, + 191.62, + 191.62, + 185.18, + 185.18, + 181.92, + 181.92, + 176.16, + 176.16, + 170.7, + 170.7, + 166.68, + 166.68, + 158.11, + 158.11, + 153.18, + 153.18, + 150.22, + 150.22, + 143.15, + 143.15, + 137.09, + 137.09, + 131.16, + 131.16, + 125.26, + 125.26, + 123.32, + 123.32, + 117.49, + 117.49, + 115.28, + 115.28, + 110.59, + 110.59, + 106.65, + 106.65, + 103.31, + 103.31, + 101.38, + 101.38, + 99.15, + 99.15, + 96.54, + 96.54, + 92.62, + 92.62, + 89.13, + 89.13, + 86.47, + 86.47, + 84.52, + 84.52, + 81.85, + 81.85, + 79.74, + 79.74, + 77.7, + 77.7, + 75.35, + 75.35 + ], + [ + 204.68, + 204.68, + 205.36, + 205.36, + 204.76, + 204.76, + 204.94, + 204.94, + 205.27, + 205.27, + 205.23, + 205.23, + 204.59, + 204.59, + 203.87, + 203.87, + 203.69, + 203.69, + 202.86, + 202.86, + 201.72, + 201.72, + 200.81, + 200.81, + 200.85, + 200.85, + 201.16, + 201.16, + 200.51, + 200.51, + 200.19, + 200.19, + 200.8, + 200.8, + 201.36, + 201.36, + 202.95, + 202.95, + 207.33, + 207.33, + 207.34, + 207.34, + 207.18, + 207.18, + 208.26, + 208.26, + 208.57, + 208.57, + 210.05, + 210.05, + 211.58, + 211.58, + 212.49, + 212.49, + 213.29, + 213.29, + 214.48, + 214.48, + 215.14, + 215.14, + 215.47, + 215.47, + 215.86, + 215.86, + 216.04, + 216.04, + 216.06, + 216.06, + 216.42, + 216.42, + 212.21, + 212.21, + 208.12, + 208.12, + 204.55, + 204.55, + 202.71, + 202.71, + 199.78, + 199.78, + 197.73, + 197.73, + 194.92, + 194.92, + 191.62, + 191.62, + 185.18, + 185.18, + 181.92, + 181.92, + 176.16, + 176.16, + 170.7, + 170.7, + 166.68, + 166.68, + 158.11, + 158.11, + 153.18, + 153.18, + 150.22, + 150.22, + 143.15, + 143.15, + 137.09, + 137.09, + 131.16, + 131.16, + 125.26, + 125.26, + 123.32, + 123.32, + 117.49, + 117.49, + 115.28, + 115.28, + 110.59, + 110.59, + 106.65, + 106.65, + 103.31, + 103.31, + 101.38, + 101.38, + 99.15, + 99.15, + 96.54, + 96.54, + 92.62, + 92.62, + 89.13, + 89.13, + 86.47, + 86.47, + 84.52, + 84.52, + 81.85, + 81.85, + 79.74, + 79.74, + 77.7, + 77.7, + 75.35, + 75.35 + ], + [ + 108.56, + 108.56, + 108.54, + 108.54, + 108.85, + 108.85, + 109.67, + 109.67, + 109.38, + 109.38, + 109.2, + 109.2, + 109.03, + 109.03, + 109.48, + 109.48, + 109.11, + 109.11, + 109.81, + 109.81, + 109.29, + 109.29, + 109.61, + 109.61, + 108.18, + 108.18, + 106.69, + 106.69, + 106.98, + 106.98, + 106.47, + 106.47, + 106.79, + 106.79, + 106.85, + 106.85, + 110.55, + 110.55, + 113.77, + 113.77, + 113.32, + 113.32, + 113.52, + 113.52, + 113.66, + 113.66, + 112.57, + 112.57, + 114.16, + 114.16, + 115.33, + 115.33, + 117.16, + 117.16, + 117.73, + 117.73, + 119.27, + 119.27, + 120.19, + 120.19, + 120.9, + 120.9, + 121.56, + 121.56, + 122.9, + 122.9, + 123.9, + 123.9, + 124.44, + 124.44, + 120.91, + 120.91, + 117.33, + 117.33, + 113.39, + 113.39, + 110.52, + 110.52, + 107.4, + 107.4, + 104.93, + 104.93, + 101.49, + 101.49, + 98.32, + 98.32, + 93.78, + 93.78, + 89.55, + 89.55, + 86.78, + 86.78, + 84.76, + 84.76, + 82.39, + 82.39, + 74.64, + 74.64, + 70.84, + 70.84, + 66.89, + 66.89, + 62.98, + 62.98, + 59.84, + 59.84, + 56.28, + 56.28, + 51.83, + 51.83, + 50.23, + 50.23, + 47.03, + 47.03, + 46.26, + 46.26, + 43.82, + 43.82, + 41.67, + 41.67, + 40.25, + 40.25, + 38.77, + 38.77, + 37.78, + 37.78, + 37.21, + 37.21, + 35.08, + 35.08, + 33.21, + 33.21, + 31.71, + 31.71, + 30.67, + 30.67, + 29.26, + 29.26, + 28.35, + 28.35, + 27.46, + 27.46, + 26.17, + 26.17 + ], + [ + 108.56, + 108.56, + 108.54, + 108.54, + 108.85, + 108.85, + 109.67, + 109.67, + 109.38, + 109.38, + 109.2, + 109.2, + 109.03, + 109.03, + 109.48, + 109.48, + 109.11, + 109.11, + 109.81, + 109.81, + 109.29, + 109.29, + 109.61, + 109.61, + 108.18, + 108.18, + 106.69, + 106.69, + 106.98, + 106.98, + 106.47, + 106.47, + 106.79, + 106.79, + 106.85, + 106.85, + 110.55, + 110.55, + 113.77, + 113.77, + 113.32, + 113.32, + 113.52, + 113.52, + 113.66, + 113.66, + 112.57, + 112.57, + 114.16, + 114.16, + 115.33, + 115.33, + 117.16, + 117.16, + 117.73, + 117.73, + 119.27, + 119.27, + 120.19, + 120.19, + 120.9, + 120.9, + 121.56, + 121.56, + 122.9, + 122.9, + 123.9, + 123.9, + 124.44, + 124.44, + 120.91, + 120.91, + 117.33, + 117.33, + 113.39, + 113.39, + 110.52, + 110.52, + 107.4, + 107.4, + 104.93, + 104.93, + 101.49, + 101.49, + 98.32, + 98.32, + 93.78, + 93.78, + 89.55, + 89.55, + 86.78, + 86.78, + 84.76, + 84.76, + 82.39, + 82.39, + 74.64, + 74.64, + 70.84, + 70.84, + 66.89, + 66.89, + 62.98, + 62.98, + 59.84, + 59.84, + 56.28, + 56.28, + 51.83, + 51.83, + 50.23, + 50.23, + 47.03, + 47.03, + 46.26, + 46.26, + 43.82, + 43.82, + 41.67, + 41.67, + 40.25, + 40.25, + 38.77, + 38.77, + 37.78, + 37.78, + 37.21, + 37.21, + 35.08, + 35.08, + 33.21, + 33.21, + 31.71, + 31.71, + 30.67, + 30.67, + 29.26, + 29.26, + 28.35, + 28.35, + 27.46, + 27.46, + 26.17, + 26.17 + ], + [ + 24.47, + 24.47, + 24.32, + 24.32, + 24.18, + 24.18, + 24.33, + 24.33, + 24.62, + 24.62, + 25.15, + 25.15, + 25.74, + 25.74, + 26.41, + 26.41, + 26.7, + 26.7, + 28.1, + 28.1, + 28.95, + 28.95, + 29.81, + 29.81, + 29.75, + 29.75, + 29.6, + 29.6, + 29.27, + 29.27, + 28.98, + 28.98, + 28.92, + 28.92, + 28.7, + 28.7, + 30.57, + 30.57, + 34.22, + 34.22, + 34.36, + 34.36, + 34.54, + 34.54, + 34.67, + 34.67, + 33.79, + 33.79, + 35.87, + 35.87, + 37.82, + 37.82, + 41.23, + 41.23, + 42.28, + 42.28, + 42.94, + 42.94, + 43.49, + 43.49, + 43.58, + 43.58, + 44.43, + 44.43, + 44.68, + 44.68, + 45.41, + 45.41, + 45.87, + 45.87, + 44.01, + 44.01, + 42.07, + 42.07, + 40.23, + 40.23, + 38.54, + 38.54, + 36.94, + 36.94, + 36.08, + 36.08, + 34.98, + 34.98, + 33.58, + 33.58, + 29.3, + 29.3, + 25.18, + 25.18, + 22.91, + 22.91, + 22.3, + 22.3, + 22.3, + 22.3, + 19.89, + 19.89, + 19.07, + 19.07, + 17.74, + 17.74, + 17.56, + 17.56, + 18, + 18, + 16.61, + 16.61, + 15.12, + 15.12, + 14.52, + 14.52, + 13.24, + 13.24, + 12.83, + 12.83, + 11.99, + 11.99, + 11.29, + 11.29, + 10.74, + 10.74, + 10.41, + 10.41, + 10.82, + 10.82, + 11.32, + 11.32, + 11.41, + 11.41, + 11.41, + 11.41, + 10.92, + 10.92, + 10.31, + 10.31, + 9.83, + 9.83, + 9.35, + 9.35, + 8.71, + 8.71, + 8.19, + 8.19 + ], + [ + 24.47, + 24.47, + 24.32, + 24.32, + 24.18, + 24.18, + 24.33, + 24.33, + 24.62, + 24.62, + 25.15, + 25.15, + 25.74, + 25.74, + 26.41, + 26.41, + 26.7, + 26.7, + 28.1, + 28.1, + 28.95, + 28.95, + 29.81, + 29.81, + 29.75, + 29.75, + 29.6, + 29.6, + 29.27, + 29.27, + 28.98, + 28.98, + 28.92, + 28.92, + 28.7, + 28.7, + 30.57, + 30.57, + 34.22, + 34.22, + 34.36, + 34.36, + 34.54, + 34.54, + 34.67, + 34.67, + 33.79, + 33.79, + 35.87, + 35.87, + 37.82, + 37.82, + 41.23, + 41.23, + 42.28, + 42.28, + 42.94, + 42.94, + 43.49, + 43.49, + 43.58, + 43.58, + 44.43, + 44.43, + 44.68, + 44.68, + 45.41, + 45.41, + 45.87, + 45.87, + 44.01, + 44.01, + 42.07, + 42.07, + 40.23, + 40.23, + 38.54, + 38.54, + 36.94, + 36.94, + 36.08, + 36.08, + 34.98, + 34.98, + 33.58, + 33.58, + 29.3, + 29.3, + 25.18, + 25.18, + 22.91, + 22.91, + 22.3, + 22.3, + 22.3, + 22.3, + 19.89, + 19.89, + 19.07, + 19.07, + 17.74, + 17.74, + 17.56, + 17.56, + 18, + 18, + 16.61, + 16.61, + 15.12, + 15.12, + 14.52, + 14.52, + 13.24, + 13.24, + 12.83, + 12.83, + 11.99, + 11.99, + 11.29, + 11.29, + 10.74, + 10.74, + 10.41, + 10.41, + 10.82, + 10.82, + 11.32, + 11.32, + 11.41, + 11.41, + 11.41, + 11.41, + 10.92, + 10.92, + 10.31, + 10.31, + 9.83, + 9.83, + 9.35, + 9.35, + 8.71, + 8.71, + 8.19, + 8.19 + ], + [ + 0.91, + 0.91, + 0.91, + 0.91, + 0.9, + 0.9, + 0.91, + 0.91, + 0.95, + 0.95, + 1.02, + 1.02, + 1.1, + 1.1, + 1.18, + 1.18, + 1.23, + 1.23, + 1.4, + 1.4, + 1.52, + 1.52, + 1.63, + 1.63, + 1.66, + 1.66, + 1.67, + 1.67, + 1.63, + 1.63, + 1.61, + 1.61, + 1.61, + 1.61, + 1.59, + 1.59, + 1.75, + 1.75, + 2.11, + 2.11, + 2.14, + 2.14, + 2.17, + 2.17, + 2.19, + 2.19, + 2.12, + 2.12, + 2.32, + 2.32, + 2.52, + 2.52, + 2.85, + 2.85, + 2.95, + 2.95, + 2.99, + 2.99, + 3.04, + 3.04, + 3.04, + 3.04, + 3.12, + 3.12, + 3.13, + 3.13, + 3.19, + 3.19, + 3.22, + 3.22, + 3.06, + 3.06, + 2.9, + 2.9, + 2.76, + 2.76, + 2.63, + 2.63, + 2.52, + 2.52, + 2.49, + 2.49, + 2.44, + 2.44, + 2.36, + 2.36, + 1.99, + 1.99, + 1.62, + 1.62, + 1.44, + 1.44, + 1.38, + 1.38, + 1.4, + 1.4, + 1.24, + 1.24, + 1.19, + 1.19, + 1.09, + 1.09, + 1.13, + 1.13, + 1.25, + 1.25, + 1.17, + 1.17, + 1.08, + 1.08, + 1.05, + 1.05, + 0.96, + 0.96, + 0.93, + 0.93, + 0.89, + 0.89, + 0.85, + 0.85, + 0.82, + 0.82, + 0.81, + 0.81, + 0.85, + 0.85, + 0.9, + 0.9, + 0.93, + 0.93, + 0.95, + 0.95, + 0.91, + 0.91, + 0.86, + 0.86, + 0.83, + 0.83, + 0.79, + 0.79, + 0.74, + 0.74, + 0.7, + 0.7 + ], + [ + 0.91, + 0.91, + 0.91, + 0.91, + 0.9, + 0.9, + 0.91, + 0.91, + 0.95, + 0.95, + 1.02, + 1.02, + 1.1, + 1.1, + 1.18, + 1.18, + 1.23, + 1.23, + 1.4, + 1.4, + 1.52, + 1.52, + 1.63, + 1.63, + 1.66, + 1.66, + 1.67, + 1.67, + 1.63, + 1.63, + 1.61, + 1.61, + 1.61, + 1.61, + 1.59, + 1.59, + 1.75, + 1.75, + 2.11, + 2.11, + 2.14, + 2.14, + 2.17, + 2.17, + 2.19, + 2.19, + 2.12, + 2.12, + 2.32, + 2.32, + 2.52, + 2.52, + 2.85, + 2.85, + 2.95, + 2.95, + 2.99, + 2.99, + 3.04, + 3.04, + 3.04, + 3.04, + 3.12, + 3.12, + 3.13, + 3.13, + 3.19, + 3.19, + 3.22, + 3.22, + 3.06, + 3.06, + 2.9, + 2.9, + 2.76, + 2.76, + 2.63, + 2.63, + 2.52, + 2.52, + 2.49, + 2.49, + 2.44, + 2.44, + 2.36, + 2.36, + 1.99, + 1.99, + 1.62, + 1.62, + 1.44, + 1.44, + 1.38, + 1.38, + 1.4, + 1.4, + 1.24, + 1.24, + 1.19, + 1.19, + 1.09, + 1.09, + 1.13, + 1.13, + 1.25, + 1.25, + 1.17, + 1.17, + 1.08, + 1.08, + 1.05, + 1.05, + 0.96, + 0.96, + 0.93, + 0.93, + 0.89, + 0.89, + 0.85, + 0.85, + 0.82, + 0.82, + 0.81, + 0.81, + 0.85, + 0.85, + 0.9, + 0.9, + 0.93, + 0.93, + 0.95, + 0.95, + 0.91, + 0.91, + 0.86, + 0.86, + 0.83, + 0.83, + 0.79, + 0.79, + 0.74, + 0.74, + 0.7, + 0.7 + ] + ] + } + } + + } + ] +} \ No newline at end of file diff --git a/tests/sft_tests/__init__.py b/tests/sft_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/sft_tests/create_sif_with_idm_test/SlurmStage_my_shiny_new_idm_test.id b/tests/sft_tests/create_sif_with_idm_test/SlurmStage_my_shiny_new_idm_test.id new file mode 100644 index 0000000..21234d8 --- /dev/null +++ b/tests/sft_tests/create_sif_with_idm_test/SlurmStage_my_shiny_new_idm_test.id @@ -0,0 +1 @@ +8eff4934-9d73-ee11-92fd-f0921c167864::Asset Collection \ No newline at end of file diff --git a/tests/sft_tests/create_sif_with_idm_test/create_idm_test_sif.py b/tests/sft_tests/create_sif_with_idm_test/create_idm_test_sif.py new file mode 100644 index 0000000..a55b6a2 --- /dev/null +++ b/tests/sft_tests/create_sif_with_idm_test/create_idm_test_sif.py @@ -0,0 +1,19 @@ +from COMPS.utils.get_output_files_for_workitem import get_files +from idmtools.assets import AssetCollection, Asset + +from idmtools.core.platform_factory import Platform +from idmtools_platform_comps.utils.singularity_build import SingularityBuildWorkItem + +if __name__ == '__main__': + platform = Platform("SlurmStage") + sbi = SingularityBuildWorkItem(name="Create emodpy-typhoid-idm-test", definition_file="my_shiny_new_idm_test.def", image_name="my_shiny_new_idm_test.sif", force=True) + ac = AssetCollection() + sbi.add_assets(AssetCollection.from_id_file("dtk_centos_2018_stage.id")) + ac_obj = sbi.run(wait_until_done=True, platform=platform) + + if sbi.succeeded: + print("sbi.id: ", sbi.id) + # Write ID file + sbi.asset_collection.to_id_file(f"{platform._config_block}_my_shiny_new_idm_test.id") + print("ac_obj: ", ac_obj.id) + get_files(sbi.id, "my_shiny_new_idm_test.sif") \ No newline at end of file diff --git a/tests/sft_tests/create_sif_with_idm_test/dtk_centos_2018_stage.id b/tests/sft_tests/create_sif_with_idm_test/dtk_centos_2018_stage.id new file mode 100644 index 0000000..4b3f9d0 --- /dev/null +++ b/tests/sft_tests/create_sif_with_idm_test/dtk_centos_2018_stage.id @@ -0,0 +1 @@ +0e5d6dc3-425e-ee11-92fc-f0921c167864::Asset Collection \ No newline at end of file diff --git a/tests/sft_tests/create_sif_with_idm_test/generate_dummy_sim_for_sif.py b/tests/sft_tests/create_sif_with_idm_test/generate_dummy_sim_for_sif.py new file mode 100644 index 0000000..06ae702 --- /dev/null +++ b/tests/sft_tests/create_sif_with_idm_test/generate_dummy_sim_for_sif.py @@ -0,0 +1,23 @@ +import os + +from idmtools.assets import AssetCollection +from idmtools.core.platform_factory import Platform +from idmtools.entities import CommandLine +from idmtools.entities.command_task import CommandTask +from idmtools.entities.experiment import Experiment + +with Platform("SlurmStage") as platform: + ac = AssetCollection.from_id_file("SlurmStage_my_shiny_new_idm_test.id") + result = platform.create_items(ac) + print(result[0].id) + + # option code to keep ac in system + command = CommandLine("singularity exec ./Assets/my_shiny_new_idm_test.sif python3 --version") + task = CommandTask(command=command) + task.common_assets.add_assets(AssetCollection.from_id(result[0].id)) + experiment = Experiment.from_task( + task, + name="generate sif ac", + tags=dict(type='singularity', description='run test', sif_ac=result[0].id) + ) + experiment.run(wait_until_done=True) diff --git a/tests/sft_tests/create_sif_with_idm_test/my_shiny_new_idm_test.def b/tests/sft_tests/create_sif_with_idm_test/my_shiny_new_idm_test.def new file mode 100644 index 0000000..09aff9f --- /dev/null +++ b/tests/sft_tests/create_sif_with_idm_test/my_shiny_new_idm_test.def @@ -0,0 +1,12 @@ +Bootstrap: localimage +From: Assets/my_shiny_new.sif + +%environment + # ADD our asset to path so we can add live versions + export PYTHONPATH=Assets/:. + #export PYTHONPATH=$(pwd)/Assets/site-packages:$(pwd)/Assets/:$PYTHONPATH +%post + pip3 install --no-cache-dir idm-test==0.1.2 --extra-index-url https://packages.idmod.org/api/pypi/pypi-production/simple + +%runscript + echo add idm-test to my_shiny_new.sif \ No newline at end of file diff --git a/tests/sft_tests/helper.py b/tests/sft_tests/helper.py new file mode 100644 index 0000000..6de81fb --- /dev/null +++ b/tests/sft_tests/helper.py @@ -0,0 +1,69 @@ +import os +from functools import partial + +from idm_test.dtk_test.integration import manifest +from idmtools.assets import Asset +from idmtools.builders import SimulationBuilder + +from emodpy_typhoid.utility.sweeping import ItvFn, set_param, sweep_functions + +BASE_YEAR = 2005 + + +def year_to_days(year): + return (year - BASE_YEAR) * 365 + + +def update_sim_random_seed(simulation, value): + simulation.task.config.parameters.Run_Number = value + return {"Run_Number": value} + + +def build_camp(): + """ + Build a campaign input file for the DTK using emod_api. + Right now this function creates the file and returns the filename. If calling code just needs an asset that's fine. + """ + import emod_api.campaign as camp + + print(f"Telling emod-api to use {manifest.schema_file} as schema.") + camp.set_schema(manifest.schema_file) + import emod_api.interventions.outbreak as ob + ob.seed(camp, Start_Day=1, Coverage=0.5, Honor_Immunity=False) + ob.seed(camp, Start_Day=365, Coverage=0.005, Tot_Rep=10, Rep_Interval=30, Honor_Immunity=False) + return camp + + +def get_sweep_builders(sweep_list, add_vax_intervention): + """ + Build simulation builders. + Args: + kwargs: User inputs may overwrite the entries in the block. + + Returns: + lis of Simulation builders + """ + builder = SimulationBuilder() + funcs_list = [[ + ItvFn(add_vax_intervention, ce), + partial(set_param, param='Run_Number', value=x), + ] + for ce in sweep_list # for sweep on sweep_list + for x in range(2) # for sweep Run_Number + ] + + builder.add_sweep_definition(sweep_functions, funcs_list) + + return [builder] + + +def setup(platform=None): + manifest.CURRENT_DIR = "." # do not use this: os.path.abspath(os.path.dirname(__file__)) + import emod_typhoid.bootstrap as dtk + manifest.SIF_DIR = os.path.join(manifest.CURRENT_DIR, "sif") + manifest.eradication_path = os.path.join(manifest.SIF_DIR, "Eradication") + manifest.schema_file = os.path.join(manifest.SIF_DIR, "schema.json") + manifest.sft_id = os.path.join(os.path.abspath(os.path.dirname(__file__)), "create_sif_with_idm_test", + "SlurmStage_my_shiny_new_idm_test.id") + manifest.platform = platform + dtk.setup(manifest.SIF_DIR) diff --git a/tests/sft_tests/hint_tests/test_multiple_route_hint_cross/ep4_dir/dtk_post_process.py b/tests/sft_tests/hint_tests/test_multiple_route_hint_cross/ep4_dir/dtk_post_process.py new file mode 100644 index 0000000..3fcfdfb --- /dev/null +++ b/tests/sft_tests/hint_tests/test_multiple_route_hint_cross/ep4_dir/dtk_post_process.py @@ -0,0 +1,60 @@ +#!/usr/bin/python +import math +import os.path + +import pandas as pd + +from idm_test.dtk_test.sft_class import arg_parser, SFT + +class HINTCrossTest(SFT): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # overwrite the test method + def test(self): + self.success = False + with open(self.report_name, "w") as outfile: + with open(os.path.join(self.output_folder, "ReportTyphoidByAgeAndGender.csv"), 'r') as infile: + df = pd.read_csv(infile) + df.columns = df.columns.to_series().apply(lambda x: x.strip()) + new_infected_urban = df.loc[(df['HINT Group'] == 'Region:Urban')][['Newly Infected']] + new_infected_rural = df.loc[(df['HINT Group'] == 'Region:Rural')][['Newly Infected']] + # verify both region should have some new infections. Region:Urban infections are from original outbreak + # Region:Rural's new infections are from Region:Urban + if new_infected_urban['Newly Infected'].notna().any() and new_infected_rural['Newly Infected'].notna().any(): + self.success = True + outfile.write("GOOD: There is some transmission for IP Region:Urban and Region:Rural \n") + outfile.write("Part1: Result is True.\n") + else: + self.success = False + outfile.write("BAD: There should have some transmission for both regoins\n") + outfile.write("Part1: Result is False.\n") + population_buran = df.loc[(df['HINT Group'] == 'Region:Urban')][['Population']].sum().values[0] + population_rural = df.loc[(df['HINT Group'] == 'Region:Rural')][['Population']].sum().values[0] + ratio = population_rural/population_buran + expected_ration = 0.8/0.2 + if not math.isclose(ratio, expected_ration, abs_tol=0.5): + self.success = False + outfile.write(f" BAD: population ration is not correct") + outfile.write("Part2: Population test is failed.\n") + else: + self.success = True + outfile.write(f" GOOD: population ration is correct\n") + outfile.write("Part2: Population test is passed.\n") + return self.success + + +def application(output_folder="output", my_arg=None): + if not my_arg: + my_sft = HINTCrossTest(stdout='stdout.txt') + else: + my_sft = HINTCrossTest( + output=my_arg.output, stdout='stdout.txt', json_report=my_arg.json_report, event_csv=my_arg.event_csv, + config=my_arg.config, campaign=my_arg.campaign, report_name=my_arg.report_name, debug=my_arg.debug) + my_sft.run() + + +if __name__ == "__main__": + # execute only if run as a script + my_arg = arg_parser() + application(my_arg=my_arg) diff --git a/tests/sft_tests/hint_tests/test_multiple_route_hint_cross/test_multiple_route_hint_cross.py b/tests/sft_tests/hint_tests/test_multiple_route_hint_cross/test_multiple_route_hint_cross.py new file mode 100644 index 0000000..af2088d --- /dev/null +++ b/tests/sft_tests/hint_tests/test_multiple_route_hint_cross/test_multiple_route_hint_cross.py @@ -0,0 +1,143 @@ +import itertools +import os +import shutil +import sys + + +from emodpy.emod_task import EMODTask +import emod_api.campaign as camp +from idmtools.entities.experiment import Experiment + +from idmtools.core.platform_factory import Platform +from idm_test.dtk_test.integration.integration_test import IntegrationTest +from idm_test.dtk_test.integration import manifest, bootstrap +import emod_api.interventions.common as comm + +sys.path.append('../..') +from helper import year_to_days, setup +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +BASE_YEAR = 2005 +SIMULATION_DURATION_IN_YEARS = 20 +CAMP_START_YEAR = 2015 + +current_dir = os.path.dirname(__file__) + + +def set_param_fn(config): + """ + Update the config parameters from default values. + """ + print("Setting params.") + config.parameters.Simulation_Type = "TYPHOID_SIM" + config.parameters.Simulation_Duration = SIMULATION_DURATION_IN_YEARS * 365.0 + config.parameters.Base_Individual_Sample_Rate = 0.1 + + config.parameters.Base_Year = BASE_YEAR + config.parameters.Inset_Chart_Reporting_Start_Year = 2005 + config.parameters.Inset_Chart_Reporting_Stop_Year = 2020 + config.parameters.Enable_Demographics_Reporting = 0 + config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 2005 + config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2020 + config.parameters.Demographics_Filenames = ["TestDemographics_pak_updated.json"] + config.parameters.Enable_Property_Output = 0 + config.parameters.Report_Event_Recorder_Events = ["VaccineDistributed", "NewInfectionEvent"] + config.parameters["Listed_Events"] = ["VaccineDistributed"] # old school + + config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" + config.parameters.Death_Rate_Dependence = "NONDISEASE_MORTALITY_BY_YEAR_AND_AGE_FOR_EACH_GENDER" + config.parameters.Birth_Rate_Dependence = "INDIVIDUAL_PREGNANCIES_BY_AGE_AND_YEAR" + # when using 2018 binary + import emodpy_typhoid.config as config_utils + config_utils.cleanup_for_2018_mode(config) + return config + + +def add_vax_intervention(campaign, values): + 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) + tv_iv = tv.new_vax(campaign, + efficacy=1, + expected_expiration=2190, + constant_period=0) + notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") + one_time_campaign = comm.ScheduledCampaignEvent(camp, + Start_Day=year_to_days(CAMP_START_YEAR) + 1, + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=values['coverage'], + Target_Age_Min=0, + Target_Age_Max=100 + ) + camp.add(one_time_campaign) + return {'coverage': values['coverage']} + +def build_camp(): + """ + Build a campaign input file for the DTK using emod_api. + Right now this function creates the file and returns the filename. If calling code just needs an asset that's fine. + """ + import emod_api.campaign as camp + + print(f"Telling emod-api to use {manifest.schema_file} as schema.") + camp.set_schema(manifest.schema_file) + import emod_api.interventions.outbreak as ob + ob.seed(camp, Start_Day=1, Coverage=0.5, Honor_Immunity=False, Target_Props=["Region:Rural"]) + ob.seed(camp, Start_Day=365, Coverage=0.005, Tot_Rep=10, Rep_Interval=30, Honor_Immunity=False, Target_Props=["Region:Rural"]) + return camp + +def build_demog(): + """ + Build a demographics input file for the DTK using emod_api. + """ + import emodpy_typhoid.demographics.TyphoidDemographics as Demographics # OK to call into emod-api + + demog = Demographics.from_template_node(lat=0, lon=0, pop=10000, name=1, forced_id=1) + # We're getting all our demographics from a static file overlay. + demog.AddIndividualPropertyAndHINT( Property="Region", + Values = [ "Rural", "Urban" ], + InitialDistribution = [ 0.8, 0.2 ], + TransmissionMatrix = [ + [ 0, 1.8 ], + [ 0, 0 ] + ], + EnviroTransmissionMatrix = [ + [ 0, 1.8 ], + [ 0, 0 ] + ] + ) + + return demog + +class TestHintCross(IntegrationTest): + def setUp(self): + self.test_name = self.case_name = os.path.basename(__file__) + "--" + self._testMethodName + self.platform = Platform("SLURM2", priority="Normal") + setup(self.platform) + + def tearDown(self) -> None: + exp_folder = self.experiment.id + if os.path.exists(exp_folder) and os.path.isdir(exp_folder): + shutil.rmtree(exp_folder, ignore_errors=True) + + def test_multiple_route_hint_cross(self): + task = EMODTask.from_default2(config_path="config.json", eradication_path=manifest.eradication_path, + campaign_builder=build_camp, demog_builder=build_demog, + schema_path=manifest.schema_file, param_custom_cb=set_param_fn, + ep4_custom_cb=self._add_ep4) + + task.common_assets.add_directory(os.path.join("..", "..", "Assets")) + task.config.parameters.Demographics_Filenames = ["demographics.json", "TestDemographics_pak_updated.json"] + task.set_sif(manifest.sft_id) + self.experiment = Experiment.from_task(task, name=self.test_name) + # The last step is to call run() on the ExperimentManager to run the simulations. + self.experiment.run(wait_until_done=True) + task.handle_experiment_completion(self.experiment) + self.experiment = self.experiment + self._check_result() + + +if __name__ == '__main__': + import unittest + + unittest.main() diff --git a/tests/sft_tests/hint_tests/test_multiple_route_hint_no_mix/ep4_dir/dtk_post_process.py b/tests/sft_tests/hint_tests/test_multiple_route_hint_no_mix/ep4_dir/dtk_post_process.py new file mode 100644 index 0000000..9f48e78 --- /dev/null +++ b/tests/sft_tests/hint_tests/test_multiple_route_hint_no_mix/ep4_dir/dtk_post_process.py @@ -0,0 +1,45 @@ +#!/usr/bin/python + +import os.path + +import pandas as pd + +from idm_test.dtk_test.sft_class import arg_parser, SFT + +class HINTNoMixTest(SFT): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # overwrite the test method + def test(self): + self.success = False + with open(self.report_name, "w") as outfile: + with open(os.path.join(self.output_folder, "ReportTyphoidByAgeAndGender.csv"), 'r') as infile: + df = pd.read_csv(infile) + df.columns = df.columns.to_series().apply(lambda x: x.strip()) + new_infected = df.loc[(df['HINT Group'] == 'Region:Urban')][['Newly Infected']] + if (new_infected['Newly Infected'] == 0).all(): + self.success = True + outfile.write("GOOD: There is no transmission for IP Region:Urban \n") + outfile.write("Part1: Result is True.\n") + else: + self.success = False + outfile.write("BAD: There should be no transmission for IP Region:Urban\n") + outfile.write("Part1: Result is False.\n") + return self.success + + +def application(output_folder="output", my_arg=None): + if not my_arg: + my_sft = HINTNoMixTest(stdout='stdout.txt') + else: + my_sft = HINTNoMixTest( + output=my_arg.output, stdout='stdout.txt', json_report=my_arg.json_report, event_csv=my_arg.event_csv, + config=my_arg.config, campaign=my_arg.campaign, report_name=my_arg.report_name, debug=my_arg.debug) + my_sft.run() + + +if __name__ == "__main__": + # execute only if run as a script + my_arg = arg_parser() + application(my_arg=my_arg) diff --git a/tests/sft_tests/hint_tests/test_multiple_route_hint_no_mix/test_multiple_route_hint_no_mix.py b/tests/sft_tests/hint_tests/test_multiple_route_hint_no_mix/test_multiple_route_hint_no_mix.py new file mode 100644 index 0000000..cfa6dfe --- /dev/null +++ b/tests/sft_tests/hint_tests/test_multiple_route_hint_no_mix/test_multiple_route_hint_no_mix.py @@ -0,0 +1,147 @@ +import itertools +import os +import shutil +import sys + + +import numpy as np +from emodpy.emod_task import EMODTask +import emod_api.campaign as camp +from idmtools.entities.experiment import Experiment +from idmtools.entities.templated_simulation import TemplatedSimulations + +from idmtools.core.platform_factory import Platform +from idm_test.dtk_test.integration.integration_test import IntegrationTest +from idm_test.dtk_test.integration import manifest, bootstrap +import emod_api.interventions.common as comm + +sys.path.append('../..') +from helper import year_to_days, setup + + +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +BASE_YEAR = 2005 +SIMULATION_DURATION_IN_YEARS = 20 +CAMP_START_YEAR = 2015 + +current_dir = os.path.dirname(__file__) + + +def set_param_fn(config): + """ + Update the config parameters from default values. + """ + print("Setting params.") + config.parameters.Simulation_Type = "TYPHOID_SIM" + config.parameters.Simulation_Duration = SIMULATION_DURATION_IN_YEARS * 365.0 + config.parameters.Base_Individual_Sample_Rate = 0.1 + + config.parameters.Base_Year = BASE_YEAR + config.parameters.Inset_Chart_Reporting_Start_Year = 2010 + config.parameters.Inset_Chart_Reporting_Stop_Year = 2020 + config.parameters.Enable_Demographics_Reporting = 0 + config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 2010 + config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2020 + config.parameters.Demographics_Filenames = ["TestDemographics_pak_updated.json"] + config.parameters.Enable_Property_Output = 0 + config.parameters.Report_Event_Recorder_Events = ["VaccineDistributed", "NewInfectionEvent"] + config.parameters["Listed_Events"] = ["VaccineDistributed"] # old school + + config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" + config.parameters.Death_Rate_Dependence = "NONDISEASE_MORTALITY_BY_YEAR_AND_AGE_FOR_EACH_GENDER" + config.parameters.Birth_Rate_Dependence = "INDIVIDUAL_PREGNANCIES_BY_AGE_AND_YEAR" + # when using 2018 binary + import emodpy_typhoid.config as config_utils + config_utils.cleanup_for_2018_mode(config) + return config + + +def add_vax_intervention(campaign, values): + 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) + tv_iv = tv.new_vax(campaign, + efficacy=1, + expected_expiration=2190, + constant_period=0) + notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") + one_time_campaign = comm.ScheduledCampaignEvent(camp, + Start_Day=year_to_days(CAMP_START_YEAR) + 1, + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=values['coverage'], + Target_Age_Min=0, + Target_Age_Max=100 + ) + camp.add(one_time_campaign) + return {'coverage': values['coverage']} + +def build_camp(): + """ + Build a campaign input file for the DTK using emod_api. + Right now this function creates the file and returns the filename. If calling code just needs an asset that's fine. + """ + import emod_api.campaign as camp + + print(f"Telling emod-api to use {manifest.schema_file} as schema.") + camp.set_schema(manifest.schema_file) + import emod_api.interventions.outbreak as ob + ob.seed(camp, Start_Day=1, Coverage=0.5, Honor_Immunity=False, Target_Props=["Region:Rural"]) + ob.seed(camp, Start_Day=365, Coverage=0.005, Tot_Rep=10, Rep_Interval=30, Honor_Immunity=False, Target_Props=["Region:Rural"]) + return camp + +def build_demog(): + """ + Build a demographics input file for the DTK using emod_api. + """ + import emodpy_typhoid.demographics.TyphoidDemographics as Demographics # OK to call into emod-api + + demog = Demographics.from_template_node(lat=0, lon=0, pop=10000, name=1, forced_id=1) + # We're getting all our demographics from a static file overlay. + demog.AddIndividualPropertyAndHINT( Property="Region", + Values = [ "Rural", "Urban" ], + InitialDistribution = [ 0.8, 0.2 ], + TransmissionMatrix = [ + [ 1, 0 ], + [ 0, 1 ] + ], + EnviroTransmissionMatrix = [ + [ 1, 0 ], + [ 0, 1 ] + ] + ) + + return demog + +class TestHintNoMix(IntegrationTest): + def setUp(self): + self.test_name = self.case_name = os.path.basename(__file__) + "--" + self._testMethodName + self.platform = Platform("SLURM2", priority="Normal") + setup(self.platform) + + def tearDown(self) -> None: + exp_folder = self.experiment.id + if os.path.exists(exp_folder) and os.path.isdir(exp_folder): + shutil.rmtree(exp_folder, ignore_errors=True) + + def test_multiple_route_hint_no_mix(self): + task = EMODTask.from_default2(config_path="config.json", eradication_path=manifest.eradication_path, + campaign_builder=build_camp, demog_builder=build_demog, + schema_path=manifest.schema_file, param_custom_cb=set_param_fn, + ep4_custom_cb=self._add_ep4) + + task.common_assets.add_directory(os.path.join("..", "..", "Assets")) + task.config.parameters.Demographics_Filenames = ["demographics.json", "TestDemographics_pak_updated.json"] + task.set_sif(manifest.sft_id) + self.experiment = Experiment.from_task(task, name=self.test_name) + # The last step is to call run() on the ExperimentManager to run the simulations. + self.experiment.run(wait_until_done=True) + task.handle_experiment_completion(self.experiment) + self.experiment = self.experiment + self._check_result() + + +if __name__ == '__main__': + import unittest + + unittest.main() diff --git a/tests/sft_tests/requirements.txt b/tests/sft_tests/requirements.txt new file mode 100644 index 0000000..bf27801 --- /dev/null +++ b/tests/sft_tests/requirements.txt @@ -0,0 +1 @@ +idm-test>=0.1.2 \ No newline at end of file diff --git a/tests/sft_tests/run_all_sft_tests.py b/tests/sft_tests/run_all_sft_tests.py new file mode 100644 index 0000000..f8420e9 --- /dev/null +++ b/tests/sft_tests/run_all_sft_tests.py @@ -0,0 +1,28 @@ +import os +import subprocess +import sys + + +def walkdir(dirname): + test_list = [] + for cur, _dirs, files in os.walk(dirname): + for f in files: + if f.startswith("test") and f.endswith(".py"): + file_path = {cur: f} + test_list.append(file_path) + return test_list + + +if __name__ == '__main__': + CURRENT_DIRECTORY = os.path.dirname(__file__) + + test_list = walkdir(".") + cwd = os.getcwd() + for dict_item in test_list: + for key in dict_item: + print(f"test file {dict_item[key]}") + print(f"test dir {key}") + p = subprocess.run(["pytest", "-x", "-v", "--junitxml", "test_results.xml"], cwd=key) + if p.returncode != 0: + sys.exit(p.returncode) + diff --git a/tests/sft_tests/vaccine_tests/test_vax_campaign_coverage/ep4_dir/dtk_post_process.py b/tests/sft_tests/vaccine_tests/test_vax_campaign_coverage/ep4_dir/dtk_post_process.py new file mode 100644 index 0000000..6293f5b --- /dev/null +++ b/tests/sft_tests/vaccine_tests/test_vax_campaign_coverage/ep4_dir/dtk_post_process.py @@ -0,0 +1,134 @@ +#!/usr/bin/python + +import json +import os.path + +import numpy as np +import pandas as pd + +from idm_test.dtk_test.general_support import InsetKeys, ConfigKeys, CampaignKeys, load_config_parameters +from idm_test.dtk_test.sft_class import arg_parser, SFT + +with open("config.json") as infile: + run_number = json.load(infile)['parameters']['Run_Number'] +np.random.seed(run_number) +import math + +from idm_test.dtk_test import sft + +channels = [InsetKeys.ChannelsKeys.Infected, + InsetKeys.ChannelsKeys.New_Infections, + InsetKeys.ChannelsKeys.Statistical_Population] + +config_keys = [ConfigKeys.Base_Individual_Sample_Rate, + ConfigKeys.Simulation_Timestep, + ConfigKeys.Simulation_Duration, + "Base_Year"] + + +class Stdout: + dose = "gave out " + stat_pop = "StatPop: " + + +matches = ["'MultiInterventionDistributor' interventions ", + "StatPop: " + ] + + +def load_campaign_file(campaign_filename, debug): + with open(campaign_filename) as infile: + cf = json.load(infile) + campaign_obj = {CampaignKeys.Start_Day: [], CampaignKeys.Demographic_Coverage: [], + CampaignKeys.Property_Restrictions: []} + events = cf[CampaignKeys.Events] + for event in events: + start_day = event[CampaignKeys.Start_Day] + campaign_obj[CampaignKeys.Start_Day].append(int(start_day)) + + coverage = event[CampaignKeys.Event_Coordinator_Config][CampaignKeys.Demographic_Coverage] + campaign_obj[CampaignKeys.Demographic_Coverage].append(float(coverage)) + + if debug: + with open("DEBUG_campaign_object.json", 'w') as outfile: + json.dump(campaign_obj, outfile, indent=4) + + return campaign_obj + + +def parse_stdout_file(stdout_filename="stdout.txt", simulation_timestep=1, debug=True): + filtered_lines = [] + with open(stdout_filename) as logfile: + for line in logfile: + if sft.has_match(line, matches): + filtered_lines.append(line) + if debug: + with open("DEBUG_filtered_lines.txt", "w") as outfile: + outfile.writelines(filtered_lines) + + # initialize variables + for line in filtered_lines: + if matches[0] in line: + dose = int(sft.get_val(Stdout.dose, line)) + elif matches[1] in line and str(simulation_timestep) in line: + population_at_time = int(sft.get_val(Stdout.stat_pop, line)) + return dose, population_at_time + + +class VaxCoverageTest(SFT): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.params = load_config_parameters(self.config_filename, config_keys, self.debug) + self.campaign_obj = load_campaign_file(self.campaign_filename, self.debug) + self.dose, self.population_at_time = parse_stdout_file(self.stdout_filename, self.campaign_obj['Start_Day'][2], + self.debug) + from idm_test.dtk_test.output_file import ReportEventRecorder + self.report_event_recorder_csv = ReportEventRecorder( + file=os.path.join(self.output_folder, self.event_report_name)) + + # overwrite the test method + def test(self): + self.success = False + with open(self.report_name, "w") as outfile: + campain_cax_start = int(self.campaign_obj['Start_Day'][2] / 365) + self.params['Base_Year'] + df = self.report_event_recorder_csv.df + vax_id = df.loc[(df['Year'] == round(campain_cax_start, 5)) & (df['Event_Name'] == 'VaccineDistributed')][ + ['Event_Name', 'Year', "Age", "Individual_ID"]].groupby(['Individual_ID']).sum() + total_vax_count = vax_id.shape[0] + actual_vax_coverage = total_vax_count / self.population_at_time /self.params['Base_Individual_Sample_Rate'] + expected_coverage = self.campaign_obj['Demographic_Coverage'][2] + if not math.isclose(actual_vax_coverage, expected_coverage, abs_tol=0.05): # we allow 5% difference + self.success = False + outfile.write( + f" BAD: at time step {self.campaign_obj['Start_Day'][2]}, vax coverage {actual_vax_coverage}, expected {expected_coverage}.\n") + outfile.write("Result is False.\n") + else: + self.success = True + outfile.write("GOOD: coverage is correct!\n") + outfile.write("Result is True.\n") + if total_vax_count != self.dose: # make sure report and stdout value are same + self.success = False + outfile.write( + f" BAD: at time step {self.campaign_obj['Start_Day'][2]}, dose in stdout {self.dose} is not matched with vax in report {total_vax_count}.\n") + outfile.write("Result is False.\n") + else: + self.success = True + outfile.write("GOOD: dose from stdout and vax count from report are matched\n") + outfile.write("Result is True.\n") + return self.success + + +def application(output_folder="output", my_arg=None): + if not my_arg: + my_sft = VaxCoverageTest(stdout='stdout.txt') + else: + my_sft = VaxCoverageTest( + output=my_arg.output, stdout='stdout.txt', json_report=my_arg.json_report, event_csv=my_arg.event_csv, + config=my_arg.config, campaign=my_arg.campaign, report_name=my_arg.report_name, debug=my_arg.debug) + my_sft.run() + + +if __name__ == "__main__": + # execute only if run as a script + my_arg = arg_parser() + application(my_arg=my_arg) diff --git a/tests/sft_tests/vaccine_tests/test_vax_campaign_coverage/test_vax_campaign_coverage.py b/tests/sft_tests/vaccine_tests/test_vax_campaign_coverage/test_vax_campaign_coverage.py new file mode 100644 index 0000000..fa47d3f --- /dev/null +++ b/tests/sft_tests/vaccine_tests/test_vax_campaign_coverage/test_vax_campaign_coverage.py @@ -0,0 +1,117 @@ +import itertools +import os +import shutil +import sys + + +import numpy as np +from emodpy.emod_task import EMODTask +import emod_api.campaign as camp +from idmtools.entities.experiment import Experiment +from idmtools.entities.templated_simulation import TemplatedSimulations + +from idmtools.core.platform_factory import Platform +from idm_test.dtk_test.integration.integration_test import IntegrationTest +from idm_test.dtk_test.integration import manifest +import emod_api.interventions.common as comm + +sys.path.append('../..') +from helper import year_to_days, build_camp, get_sweep_builders, setup +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +BASE_YEAR = 2005 +SIMULATION_DURATION_IN_YEARS = 20 +CAMP_START_YEAR = 2015 + +current_dir = os.path.dirname(__file__) + + +def set_param_fn(config): + """ + Update the config parameters from default values. + """ + print("Setting params.") + config.parameters.Simulation_Type = "TYPHOID_SIM" + config.parameters.Simulation_Duration = SIMULATION_DURATION_IN_YEARS * 365.0 + config.parameters.Base_Individual_Sample_Rate = 0.1 + + config.parameters.Base_Year = BASE_YEAR + config.parameters.Inset_Chart_Reporting_Start_Year = 2010 + config.parameters.Inset_Chart_Reporting_Stop_Year = 2020 + config.parameters.Enable_Demographics_Reporting = 0 + config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 2010 + config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2020 + config.parameters.Demographics_Filenames = ["TestDemographics_pak_updated.json"] + config.parameters.Enable_Property_Output = 0 + config.parameters.Report_Event_Recorder_Events = ["VaccineDistributed", "NewInfectionEvent"] + config.parameters["Listed_Events"] = ["VaccineDistributed"] # old school + + config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" + config.parameters.Death_Rate_Dependence = "NONDISEASE_MORTALITY_BY_YEAR_AND_AGE_FOR_EACH_GENDER" + config.parameters.Birth_Rate_Dependence = "INDIVIDUAL_PREGNANCIES_BY_AGE_AND_YEAR" + # when using 2018 binary + import emodpy_typhoid.config as config_utils + config_utils.cleanup_for_2018_mode(config) + return config + + +def add_vax_intervention(campaign, values): + 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) + tv_iv = tv.new_vax(campaign, + efficacy=1, + expected_expiration=2190, + constant_period=0) + notification_iv = comm.BroadcastEvent(campaign, "VaccineDistributed") + one_time_campaign = comm.ScheduledCampaignEvent(camp, + Start_Day=year_to_days(CAMP_START_YEAR) + 1, + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=values['coverage'], + Target_Age_Min=0, + Target_Age_Max=100 + ) + camp.add(one_time_campaign) + return {'coverage': values['coverage']} + + +class TestVaxCampaignCoverage(IntegrationTest): + def setUp(self): + self.test_name = self.case_name = os.path.basename(__file__) + "--" + self._testMethodName + self.platform = Platform("SLURM2", priority="Normal") + setup(self.platform) + + def tearDown(self) -> None: + exp_folder = self.experiment.id + if os.path.exists(exp_folder) and os.path.isdir(exp_folder): + shutil.rmtree(exp_folder, ignore_errors=True) + + def test_campaign_vax_coverage(self): + + task = EMODTask.from_default2(config_path="config.json", eradication_path=manifest.eradication_path, + campaign_builder=build_camp, demog_builder=None, + schema_path=manifest.schema_file, param_custom_cb=set_param_fn, + ep4_custom_cb=self._add_ep4) + + task.common_assets.add_directory(os.path.join("..", "..", "Assets")) + task.set_sif(manifest.sft_id) + cov = np.linspace(start=0.5, stop=1.0, num=6) + sweep_list = [] + combinations = list(itertools.product(cov)) + for c in combinations: + sweep_list.append({'coverage': c[0]}) + builders = get_sweep_builders(sweep_list, add_vax_intervention) + ts = TemplatedSimulations(base_task=task, builders=builders) + # create experiment from TemplatedSimulations + self.experiment = Experiment.from_template(ts, name=self.test_name) + # The last step is to call run() on the ExperimentManager to run the simulations. + self.experiment.run(wait_until_done=True) + task.handle_experiment_completion(self.experiment) + self.experiment = self.experiment + self._check_result() + + +if __name__ == '__main__': + import unittest + + unittest.main() diff --git a/tests/sft_tests/vaccine_tests/test_vax_rollout_coverage/ep4_dir/dtk_post_process.py b/tests/sft_tests/vaccine_tests/test_vax_rollout_coverage/ep4_dir/dtk_post_process.py new file mode 100644 index 0000000..b37e027 --- /dev/null +++ b/tests/sft_tests/vaccine_tests/test_vax_rollout_coverage/ep4_dir/dtk_post_process.py @@ -0,0 +1,136 @@ +#!/usr/bin/python + +import json +import os.path + +import numpy as np +import pandas as pd + +from idm_test.dtk_test.general_support import InsetKeys, ConfigKeys, CampaignKeys, load_config_parameters +from idm_test.dtk_test.sft_class import arg_parser, SFT + +with open("config.json") as infile: + run_number = json.load(infile)['parameters']['Run_Number'] +np.random.seed(run_number) +import math + +from idm_test.dtk_test import sft + +channels = [InsetKeys.ChannelsKeys.Infected, + InsetKeys.ChannelsKeys.New_Infections, + InsetKeys.ChannelsKeys.Statistical_Population] + +config_keys = [ConfigKeys.Base_Individual_Sample_Rate, + ConfigKeys.Simulation_Timestep, + ConfigKeys.Simulation_Duration, + "Base_Year"] + + +class Stdout: + dose = "gave out " + stat_pop = "StatPop: " + + +matches = ["'SimpleVaccine' interventions at ", + "StatPop: " + ] + + +def load_campaign_file(campaign_filename, debug): + with open(campaign_filename) as infile: + cf = json.load(infile) + campaign_obj = {CampaignKeys.Start_Day: [], CampaignKeys.Demographic_Coverage: [], + CampaignKeys.Property_Restrictions: []} + events = cf[CampaignKeys.Events] + for event in events: + start_day = event[CampaignKeys.Start_Day] + campaign_obj[CampaignKeys.Start_Day].append(int(start_day)) + if event[CampaignKeys.Event_Coordinator_Config][CampaignKeys.Intervention_Config][ + CampaignKeys.class_key] != CampaignKeys.InterventionClassKeys.OutbreakIndividual: + coverage = event[CampaignKeys.Event_Coordinator_Config][CampaignKeys.Intervention_Config][ + CampaignKeys.Demographic_Coverage] + campaign_obj[CampaignKeys.Demographic_Coverage].append(float(coverage)) + else: + coverage = event[CampaignKeys.Event_Coordinator_Config][CampaignKeys.Demographic_Coverage] + campaign_obj[CampaignKeys.Demographic_Coverage].append(float(coverage)) + + if debug: + with open("DEBUG_campaign_object.json", 'w') as outfile: + json.dump(campaign_obj, outfile, indent=4) + + return campaign_obj + + +def parse_stdout_file(stdout_filename="stdout.txt", simulation_timestep=1, debug=True): + filtered_lines = [] + with open(stdout_filename) as logfile: + for line in logfile: + if sft.has_match(line, matches): + filtered_lines.append(line) + if debug: + with open("DEBUG_filtered_lines.txt", "w") as outfile: + outfile.writelines(filtered_lines) + + # initialize variables + for line in filtered_lines: + if matches[0] in line: + dose = int(sft.get_val(Stdout.dose, line)) + elif matches[1] in line and str(simulation_timestep) in line: + population_at_time = int(sft.get_val(Stdout.stat_pop, line)) + return dose, population_at_time + + +class VaxRolloutCoverageTest(SFT): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.params = load_config_parameters(self.config_filename, config_keys, self.debug) + self.campaign_obj = load_campaign_file(self.campaign_filename, self.debug) + from idm_test.dtk_test.output_file import ReportEventRecorder + self.report_event_recorder_csv = ReportEventRecorder( + file=os.path.join(self.output_folder, self.event_report_name)) + + # overwrite the test method + def test(self): + self.success = False + with open(self.report_name, "w") as outfile: + campain_cax_start = int(self.campaign_obj['Start_Day'][2] / 365) + self.params['Base_Year'] + df = self.report_event_recorder_csv.df + # Baby born in last 9 month before simulation ends may not get vaxed + end_caculate_birth_time = ((self.params['Simulation_Duration'] / 365 + self.params['Base_Year']) * 365 - ( + 9 * 30 + 7)) / 365 + births_id = \ + df.loc[(df['Year'] >= campain_cax_start) & (df['Event_Name'] == 'Births') & ( + df['Year'] < end_caculate_birth_time)][ + ['Event_Name', 'Year', "Age", "Individual_ID"]].sort_values(by='Year', ascending=True) + vax_id = df.loc[(df['Year'] >= campain_cax_start) & (df['Event_Name'] == 'VaccineDistributed2')][ + ['Event_Name', 'Year', "Age", "Individual_ID"]].sort_values(by='Year', ascending=True) + total_vax_count = vax_id.shape[0] + total_birth_count = births_id.shape[0] + actual_vax_coverage = total_vax_count / total_birth_count + expected_coverage = self.campaign_obj['Demographic_Coverage'][2] + if not math.isclose(actual_vax_coverage, expected_coverage, abs_tol=0.05): # we allow 5% difference + self.success = False + outfile.write( + f" BAD: at time step {self.campaign_obj['Start_Day'][2]}, vax coverage {actual_vax_coverage}, expected {expected_coverage}.\n") + outfile.write("Result is False.\n") + else: + self.success = True + outfile.write("GOOD: coverage is correct!\n") + outfile.write("Result is True.\n") + return self.success + + +def application(output_folder="output", my_arg=None): + if not my_arg: + my_sft = VaxRolloutCoverageTest(stdout='stdout.txt') + else: + my_sft = VaxRolloutCoverageTest( + output=my_arg.output, stdout='stdout.txt', json_report=my_arg.json_report, event_csv=my_arg.event_csv, + config=my_arg.config, campaign=my_arg.campaign, report_name=my_arg.report_name, debug=my_arg.debug) + my_sft.run() + + +if __name__ == "__main__": + # execute only if run as a script + my_arg = arg_parser() + application(my_arg=my_arg) diff --git a/tests/sft_tests/vaccine_tests/test_vax_rollout_coverage/test_vax_rollout_coverage.py b/tests/sft_tests/vaccine_tests/test_vax_rollout_coverage/test_vax_rollout_coverage.py new file mode 100644 index 0000000..f4049c9 --- /dev/null +++ b/tests/sft_tests/vaccine_tests/test_vax_rollout_coverage/test_vax_rollout_coverage.py @@ -0,0 +1,109 @@ +import itertools +import os +import shutil +import sys + +import numpy as np +from emodpy.emod_task import EMODTask +import emod_api.campaign as camp +from idmtools.entities.experiment import Experiment +from idmtools.entities.templated_simulation import TemplatedSimulations + +from idmtools.core.platform_factory import Platform +from idm_test.dtk_test.integration.integration_test import IntegrationTest +from idm_test.dtk_test.integration import manifest + +sys.path.append('../..') +from helper import year_to_days, build_camp, get_sweep_builders, setup +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +BASE_YEAR = 2005 +SIMULATION_DURATION_IN_YEARS = 20 +CAMP_START_YEAR = 2015 + + +def set_param_fn(config): + """ + Update the config parameters from default values. + """ + print("Setting params.") + config.parameters.Simulation_Type = "TYPHOID_SIM" + config.parameters.Simulation_Duration = SIMULATION_DURATION_IN_YEARS * 365.0 + config.parameters.Base_Individual_Sample_Rate = 0.1 + + config.parameters.Base_Year = BASE_YEAR + config.parameters.Inset_Chart_Reporting_Start_Year = 2010 + config.parameters.Inset_Chart_Reporting_Stop_Year = 2020 + config.parameters.Enable_Demographics_Reporting = 0 + config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 2010 + config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2020 + config.parameters.Demographics_Filenames = ["TestDemographics_pak_updated.json"] + config.parameters.Enable_Property_Output = 0 + config.parameters.Report_Event_Recorder_Events = ["Births", "NewInfectionEvent", "VaccineDistributed2"] + config.parameters["Listed_Events"] = ["VaccineDistributed2"] # old school + + config.parameters.Age_Initialization_Distribution_Type = "DISTRIBUTION_COMPLEX" + config.parameters.Death_Rate_Dependence = "NONDISEASE_MORTALITY_BY_YEAR_AND_AGE_FOR_EACH_GENDER" + config.parameters.Birth_Rate_Dependence = "INDIVIDUAL_PREGNANCIES_BY_AGE_AND_YEAR" + # when using 2018 binary + import emodpy_typhoid.config as config_utils + config_utils.cleanup_for_2018_mode(config) + return config + + +def add_vax_intervention(campaign, values): + import emodpy_typhoid.interventions.typhoid_vaccine as tv + from emod_api.interventions import common + print(f"Telling emod-api to use {manifest.schema_file} as schema.") + campaign.set_schema(manifest.schema_file) + ria = tv.new_routine_immunization(camp, + efficacy=1, + constant_period=30000, + start_day=year_to_days(CAMP_START_YEAR) + 1, + coverage=values['coverage'], + co_event="VaccineDistributed2" + ) + camp.add(ria) + return {'coverage': values['coverage']} + + +class TestVaxRolloutCoverage(IntegrationTest): + def setUp(self): + self.test_name = self.case_name = os.path.basename(__file__) + "--" + self._testMethodName + self.platform = Platform("SLURM2", priority="Normal") + setup(self.platform) + + def tearDown(self) -> None: + exp_folder = self.experiment.id + if os.path.exists(exp_folder) and os.path.isdir(exp_folder): + shutil.rmtree(exp_folder, ignore_errors=True) + + def test_vax_rollout_coverage(self): + task = EMODTask.from_default2(config_path="config.json", eradication_path=manifest.eradication_path, + campaign_builder=build_camp, demog_builder=None, + schema_path=manifest.schema_file, param_custom_cb=set_param_fn, + ep4_custom_cb=self._add_ep4) + + task.common_assets.add_directory(os.path.join("..", "..", "Assets")) + task.config.parameters.Demographics_Filenames = ["TestDemographics_pak_updated.json"] + task.set_sif(manifest.sft_id) + cov = np.linspace(start=0.5, stop=1.0, num=6) + sweep_list = [] + combinations = list(itertools.product(cov)) + for c in combinations: + sweep_list.append({'coverage': c[0]}) + builders = get_sweep_builders(sweep_list, add_vax_intervention) + ts = TemplatedSimulations(base_task=task, builders=builders) + # create experiment from TemplatedSimulations + self.experiment = Experiment.from_template(ts, name=self.test_name) + # The last step is to call run() on the ExperimentManager to run the simulations. + self.experiment.run(wait_until_done=True) + task.handle_experiment_completion(self.experiment) + self.experiment = self.experiment + self._check_result() + + +if __name__ == '__main__': + import unittest + + unittest.main() diff --git a/tests/unittests/coverage_check.py b/tests/unittests/coverage_check.py new file mode 100644 index 0000000..03ade01 --- /dev/null +++ b/tests/unittests/coverage_check.py @@ -0,0 +1,24 @@ +""" +Script to check the coverage numbers. +Not a part of the test suite. +""" +import json +import sys +from pathlib import Path + +PASS_PERCENTAGE = 50.0 + +current_directory = Path.cwd() +with open(current_directory / 'coverage_data' / 'new_results.json', 'r', encoding='utf-8') as f: + data = json.load(f) + +if "total_coverage" not in data: + print("'total_coverage' key not found in the coverage data.") + sys.exit(1) # Fails jenkins step + +if data["total_coverage"] < PASS_PERCENTAGE: + print(f"Coverage is below the threshold. Coverage: {data['total_coverage']}% < 80%") + sys.exit(1) + +print("Coverage check passed!") +sys.exit(0) # Passes jenkins step \ No newline at end of file diff --git a/tests/unittests/get_unittest_coverage.py b/tests/unittests/get_unittest_coverage.py new file mode 100644 index 0000000..2414856 --- /dev/null +++ b/tests/unittests/get_unittest_coverage.py @@ -0,0 +1,129 @@ +import coverage +import unittest +import xmlrunner +from pathlib import Path +import os +import tarfile +import argparse +import json + + +run_examples = False + +covered_classes = [ + "emodpy_typhoid.interventions.typhoid_vaccine" + "emodpy_typhoid.interventions.outbreak", + "emodpy_typhoid.interventions.tcc", + "emodpy_typhoid.interventions.tcd", + "emodpy_typhoid.interventions.typhoid.wash", + "emodpy_typhoid.demographics.typhoid.TyphoidDemographics", + "emodpy_typhoid.utility.sweeping" +] + +class Coverage: + def __init__(self): + """ + Initialize to not replace existing results + """ + self.set_baseline = False + self.cov = None + pass + + def get_baseline_bool(self): + """ + Sets user input for saving coverage results json + """ + parser = argparse.ArgumentParser(description="Settings for coverage logging") + parser.add_argument( + "--set_baseline", + action="store_true", + default=False, + help="Use this flag to save the current coverage json as the baseline in coverage_data/.", + ) + + self.set_baseline = parser.parse_args().set_baseline + + pass + + def run_tests(self): + self.cov = coverage.Coverage(source=covered_classes) + self.cov.start() + + # First, load and run the unittest tests + loader = unittest.TestLoader() + test_suite = unittest.TestSuite() + for folder in loader.discover(start_dir='.', pattern='test_*.py', top_level_dir=None): + test_suite.addTest(folder) + runner = xmlrunner.XMLTestRunner(output='test_reports') + results = runner.run(test_suite) + + return results + + def save_results(self): + """ + Saves new results and optionally baseline results, + and print coverage by module + """ + coverage_data = self.cov.get_data() + measured_files = coverage_data.measured_files() + + # Getting the percentage coverage from the Coverage object + def count_lines_in_file(file_path): + try: + with open(file_path, "r", encoding='utf-8') as file: + line = sum([1 for line in file]) + return line + except FileNotFoundError: + print(f"File not found: {file_path}") + return None + + total_statements = 0 + total_covered_statements = 0 + coverage_results = {} + for filename in measured_files: + num_statements = count_lines_in_file(filename) + covered_statements = len(coverage_data.lines(filename)) + + total_statements += num_statements + total_covered_statements += covered_statements + + coverage_percentage = round((covered_statements / num_statements) * 100, 2) + coverage_results[filename] = coverage_percentage + + total_coverage_percentage = round((total_covered_statements / total_statements) * 100, 2) + coverage_results["total_coverage"] = total_coverage_percentage + + # Formatting results to print out + # Saving new results to /coverage_data + new_results_path = Path.cwd() / 'coverage_data' / 'new_results.json' + old_results_path = Path.cwd() / 'coverage_data' / 'baseline_results.json' + os.makedirs(os.path.dirname(new_results_path), exist_ok=True) + with open(new_results_path, "w", encoding='utf-8') as outfile: + json.dump(coverage_results, outfile) + + if not os.path.exists(old_results_path): + with open(old_results_path, "w", encoding='utf-8') as outfile: + json.dump(coverage_results, outfile) + + # Access baseline results + with open(old_results_path, "r", encoding='utf-8') as file: + old_coverage_results = json.load(file) + + self.cov.save() + self.cov.html_report() + + # Packaging as.tar.gz for jenkins + print("Saving coverage file as: coverage.tar.gz") + with tarfile.open("coverage.tar.gz", "w:gz") as tar: + tar.add("htmlcov", arcname="coverage_app") # Within coverage.tar.gz is coverage_app/files + + def run(self): + self.get_baseline_bool() + self.run_tests() + self.cov.stop() + self.save_results() + + +if __name__ == "__main__": + coverage_class = Coverage() + coverage_class.run() \ No newline at end of file diff --git a/tests/unittests/test_iv.py b/tests/unittests/test_iv.py index 2547b2f..86c117a 100644 --- a/tests/unittests/test_iv.py +++ b/tests/unittests/test_iv.py @@ -10,6 +10,7 @@ import emod_api.campaign as camp import pytest + def read_camp(filename): f = open(os.path.join(os.getcwd(), filename)) camp_data = json.load(f) @@ -118,7 +119,9 @@ def test_new_routine_immunization(self): vax_eff = 0.8 start_day = 4 child_age = 250 - event = ty.new_routine_immunization(camp=self.camp, efficacy=vax_eff, start_day=start_day, child_age=child_age) + # add custom co_event for notification + event = ty.new_routine_immunization(camp=self.camp, efficacy=vax_eff, start_day=start_day, child_age=child_age, + co_event="VaccineDistributed!") self.parse_intervention(event) self.assertEqual(event['Start_Day'], float(start_day)) self.assertEqual(self.intervention_config['Trigger_Condition_List'], ['Births']) @@ -133,17 +136,21 @@ def test_new_routine_immunization(self): self.assertEqual( actual_idv_config['Actual_IndividualIntervention_Configs'][0]['Waning_Config']['Initial_Effect'], vax_eff) self.assertEqual( - actual_idv_config['Actual_IndividualIntervention_Configs'][0]['Waning_Config']['Decay_Time_Constant'], - 6935.0) + actual_idv_config['Actual_IndividualIntervention_Configs'][0]['Waning_Config']['Expected_Discard_Time'], + 0) self.assertEqual( actual_idv_config['Actual_IndividualIntervention_Configs'][0]['Waning_Config']['class'], - 'WaningEffectBoxExponential') + 'WaningEffectRandomBox') self.assertEqual( actual_idv_config['Actual_IndividualIntervention_Configs'][0]['Vaccine_Type'], 'AcquisitionBlocking') self.assertEqual( actual_idv_config['Actual_IndividualIntervention_Configs'][0]['Intervention_Name'], 'SimpleVaccine') self.assertEqual( actual_idv_config['Actual_IndividualIntervention_Configs'][0]['class'], 'SimpleVaccine') + self.assertEqual(actual_idv_config['Actual_IndividualIntervention_Configs'][1]['Broadcast_Event'], + 'VaccineDistributed!') + self.assertEqual(actual_idv_config['Actual_IndividualIntervention_Configs'][1]['Intervention_Name'], + 'BroadcastEvent') def test_camp_immunization(self): vax_eff = 0.8 @@ -155,11 +162,11 @@ def test_camp_immunization(self): import emod_api.interventions.common as comm tv_iv = ty.new_vax(camp=self.camp, efficacy=vax_eff, decay_constant=decay_constant) event = comm.ScheduledCampaignEvent(camp=self.camp, - Start_Day=start_day, - Intervention_List=[tv_iv], - Demographic_Coverage=coverage, - Target_Age_Min=target_age_min, - Target_Age_Max=target_age_max) + Start_Day=start_day, + Intervention_List=[tv_iv], + Demographic_Coverage=coverage, + Target_Age_Min=target_age_min, + Target_Age_Max=target_age_max) self.parse_intervention(event) self.assertEqual(event['Start_Day'], float(start_day)) self.assertEqual(self.intervention_config['Vaccine_Type'], 'AcquisitionBlocking') @@ -174,6 +181,41 @@ def test_camp_immunization(self): self.assertEqual(self.event_coordinator['Node_Property_Restrictions'], []) self.assertEqual(self.event_coordinator['Demographic_Coverage'], coverage) + def test_camp_immunization_waning_effect(self): + vax_eff = 0.8 + start_day = 10 + coverage = 0.5 + target_age_min = 0.5 + target_age_max = 10 + expected_expiration = 365 + import emod_api.interventions.common as comm + + tv_iv = ty.new_vax(camp=self.camp, efficacy=vax_eff, expected_expiration=expected_expiration) + # add notification_iv + notification_iv = comm.BroadcastEvent(self.camp, "VaccineDistributed") + event = comm.ScheduledCampaignEvent(camp=self.camp, + Start_Day=start_day, + Intervention_List=[tv_iv, notification_iv], + Demographic_Coverage=coverage, + Target_Age_Min=target_age_min, + Target_Age_Max=target_age_max) + self.parse_intervention(event) + self.assertEqual(event['Start_Day'], float(start_day)) + self.assertEqual(self.intervention_config['Intervention_Name'], 'SimpleVaccine') + self.assertEqual(self.intervention_config['class'], 'MultiInterventionDistributor') + self.assertEqual(len(self.intervention_config['Intervention_List']),2) + self.assertEqual(self.intervention_config['Intervention_List'][0]['Vaccine_Type'], 'AcquisitionBlocking') + waning_config = self.intervention_config['Intervention_List'][0]['Waning_Config'] + self.assertEqual(waning_config['Expected_Discard_Time'], expected_expiration) + self.assertEqual(waning_config['Initial_Effect'], vax_eff) + self.assertEqual(waning_config['class'], 'WaningEffectRandomBox') + self.assertEqual(self.event_coordinator['Target_Age_Max'], target_age_max) + self.assertEqual(self.event_coordinator['Target_Age_Min'], target_age_min) + self.assertEqual(self.event_coordinator['Target_Demographic'], 'ExplicitAgeRanges') + self.assertEqual(self.event_coordinator['Target_Gender'], 'All') + self.assertEqual(self.event_coordinator['Node_Property_Restrictions'], []) + self.assertEqual(self.event_coordinator['Demographic_Coverage'], coverage) + if __name__ == "__main__": unittest.main() diff --git a/tests/workflow_tests/.gitignore b/tests/workflow_tests/.gitignore new file mode 100644 index 0000000..115cc94 --- /dev/null +++ b/tests/workflow_tests/.gitignore @@ -0,0 +1,4 @@ +**/COMPS_ID +**/stash +**/test_results.xml +**/demographics.json \ No newline at end of file diff --git a/tests/workflow_tests/manifest.py b/tests/workflow_tests/manifest.py index cd0f3f3..cc071f1 100644 --- a/tests/workflow_tests/manifest.py +++ b/tests/workflow_tests/manifest.py @@ -1,9 +1,10 @@ import os +current_dir = os.path.dirname(__file__) requirements = "./requirements.txt" model_dl_dir = "stash" schema_file=os.path.join(model_dl_dir, "schema.json") eradication_path=os.path.join(model_dl_dir, "Eradication") -assets_input_dir="Assets" +assets_input_dir=os.path.join(current_dir,"Assets") reporters=model_dl_dir -sif="dtk_centos_2018_stage.id" -world_bank_dataset="../../examples/world_bank_dataset.csv" +sif=os.path.join(current_dir,"dtk_centos_2018_stage.id") +world_bank_dataset=os.path.join(current_dir, "../../examples/world_bank_dataset.csv") diff --git a/tests/workflow_tests/test_multiroute_HINT.py b/tests/workflow_tests/test_multiroute_HINT.py index a56c5c9..6881ed7 100644 --- a/tests/workflow_tests/test_multiroute_HINT.py +++ b/tests/workflow_tests/test_multiroute_HINT.py @@ -377,7 +377,6 @@ def build_demog1(): total_population_C = df[['HINT Group', 'Population']].groupby('HINT Group').sum().loc['Region:C'].values[0] total_population_D = df[['HINT Group', 'Population']].groupby('HINT Group').sum().loc['Region:D'].values[0] total_population = total_population_A + total_population_B + total_population_C + total_population_D - # Verify each group (A,B,C,D) has rough 1/4 of population since we build demographic from HINT with: # Values = ["A", "B", "C", "D"], # InitialDistribution = [0.1, 0.2, 0.3, 0.4] self.assertAlmostEqual(total_population_A / total_population, 0.1, delta=0.05) @@ -499,7 +498,6 @@ def build_demog2(): total_population_C = df[['HINT Group', 'Population']].groupby('HINT Group').sum().loc['Region:C'].values[0] total_population_D = df[['HINT Group', 'Population']].groupby('HINT Group').sum().loc['Region:D'].values[0] total_population = total_population_A + total_population_B + total_population_C + total_population_D - # Verify each group (A,B,C,D) has rough 1/4 of population since we build demographic from HINT with: # Values = ["A", "B", "C", "D"], # InitialDistribution = [0.1, 0.2, 0.3, 0.4] self.assertAlmostEqual(total_population_A / total_population, 0.1, delta=0.05) @@ -616,7 +614,6 @@ def build_demog3(): total_population_C = df[['HINT Group', 'Population']].groupby('HINT Group').sum().loc['Region:C'].values[0] total_population_D = df[['HINT Group', 'Population']].groupby('HINT Group').sum().loc['Region:D'].values[0] total_population = total_population_A + total_population_B + total_population_C + total_population_D - # Verify each group (A,B,C,D) has rough 1/4 of population since we build demographic from HINT with: # Values = ["A", "B", "C", "D"], # InitialDistribution = [0.1, 0.2, 0.3, 0.4] self.assertAlmostEqual(total_population_A / total_population, 0.1, delta=0.05) diff --git a/tests/workflow_tests/test_typhoid_vaccine.py b/tests/workflow_tests/test_typhoid_vaccine.py index 95273eb..14910ea 100644 --- a/tests/workflow_tests/test_typhoid_vaccine.py +++ b/tests/workflow_tests/test_typhoid_vaccine.py @@ -24,7 +24,7 @@ import manifest BASE_YEAR = 2005 -SIMULATION_DURATION_IN_YEARS = 20 +SIMULATION_DURATION_IN_YEARS = 25 CAMP_START_YEAR = 2015 @@ -34,42 +34,60 @@ def year_to_days(year): class TyphoidVaxTests(unittest.TestCase): - def compare_infected_before_vax(self, a, b): - a_infected_before_vax = a.loc[a['Time Of Report (Year)'].astype(int) < CAMP_START_YEAR] + def compare_cases_before_vax(self, a, b): + a_cases_before_vax = a.loc[a['Time Of Report (Year)'].astype(int) < CAMP_START_YEAR] a_list = \ - a_infected_before_vax[['Time Of Report (Year)', 'Infected', "Age"]].groupby( + a_cases_before_vax[['Time Of Report (Year)', 'Newly Infected', "Age"]].groupby( ['Time Of Report (Year)', 'Age']).sum()[ - 'Infected'].tolist() - b_infected_before_vax = b.loc[b['Time Of Report (Year)'].astype(int) < CAMP_START_YEAR] + 'Newly Infected'].tolist() + b_cases_before_vax = b.loc[b['Time Of Report (Year)'].astype(int) < CAMP_START_YEAR] b_list = \ - b_infected_before_vax[['Time Of Report (Year)', 'Infected', "Age"]].groupby( + b_cases_before_vax[['Time Of Report (Year)', 'Newly Infected', "Age"]].groupby( ['Time Of Report (Year)', 'Age']).sum()[ - 'Infected'].tolist() + 'Newly Infected'].tolist() # validate before vax starts, all simulations has same infected number self.assertListEqual(a_list, b_list) - def compare_infected_after_vax(self, a, b): - a_infected_after_vax = a.loc[a['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR] + def compare_cases_after_vax(self, a, b, vax_expiration_year=0): + if vax_expiration_year != 0: + a_cases_after_vax = a.loc[ + (a['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR) & (a['Time Of Report (Year)'].astype( + int) <= CAMP_START_YEAR + vax_expiration_year)] + b_cases_after_vax = b.loc[ + (b['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR) & (b['Time Of Report (Year)'].astype( + int) <= CAMP_START_YEAR + vax_expiration_year)] + else: + a_cases_after_vax = a.loc[a['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR] + b_cases_after_vax = a.loc[a['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR] a_list = \ - a_infected_after_vax[['Time Of Report (Year)', 'Infected', "Age"]].groupby( - ['Time Of Report (Year)', 'Age']).sum()[ - 'Infected'].tolist() - b_infected_after_vax = b.loc[b['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR] + a_cases_after_vax[['Time Of Report (Year)', 'Newly Infected', "Age"]].groupby( + ['Time Of Report (Year)', 'Age']).mean()[ + 'Newly Infected'].tolist() + # b_cases_after_vax = b.loc[b['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR] b_list = \ - b_infected_after_vax[['Time Of Report (Year)', 'Infected', "Age"]].groupby( - ['Time Of Report (Year)', 'Age']).sum()[ - 'Infected'].tolist() - self.assertTrue(sum(a_list) >= sum(b_list)) + b_cases_after_vax[['Time Of Report (Year)', 'Newly Infected', "Age"]].groupby( + ['Time Of Report (Year)', 'Age']).mean()[ + 'Newly Infected'].tolist() + self.assertTrue(sum(a_list) >=sum(b_list)) + + def compare_cases_after_vax_age_15(self, a, b, vax_expiration_year=0): + if vax_expiration_year != 0: + a_cases_after_vax = a.loc[ + (a['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR) & (a['Time Of Report (Year)'].astype( + int) <= CAMP_START_YEAR + vax_expiration_year)] + b_cases_after_vax = b.loc[ + (b['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR) & (b['Time Of Report (Year)'].astype( + int) <= CAMP_START_YEAR + vax_expiration_year)] + else: + a_cases_after_vax = a.loc[a['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR] + b_cases_after_vax = b.loc[b['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR] - def compare_infected_after_vax_age_15(self, a, b): - a_infected_after_vax = a.loc[a['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR] - b_infected_after_vax = b.loc[b['Time Of Report (Year)'].astype(int) >= CAMP_START_YEAR] # validate after vax starts, total infected number of age groups from 0-15 from a is always greater than in b's - first_15_age_groups_infected_a = a_infected_after_vax[['Time Of Report (Year)', 'Infected', "Age"]].groupby( + first_15_age_groups_cases_a = a_cases_after_vax[['Time Of Report (Year)', 'Newly Infected', "Age"]].groupby( ['Time Of Report (Year)', 'Age']).sum().head(15).iloc[:, 0].tolist() - first_15_age_groups_infected_b = b_infected_after_vax[['Time Of Report (Year)', 'Infected', "Age"]].groupby( + first_15_age_groups_cases_b = b_cases_after_vax[['Time Of Report (Year)', 'Newly Infected', "Age"]].groupby( ['Time Of Report (Year)', 'Age']).sum().head(15).iloc[:, 0].tolist() - self.assertTrue(sum(first_15_age_groups_infected_a) >= sum(first_15_age_groups_infected_b)) + self.assertTrue(sum(first_15_age_groups_cases_a) >= sum(first_15_age_groups_cases_b)) @classmethod def setUpClass(cls) -> None: @@ -87,11 +105,11 @@ def set_param_fn(self, config): config.parameters.Base_Individual_Sample_Rate = 0.2 config.parameters.Base_Year = BASE_YEAR - config.parameters.Inset_Chart_Reporting_Start_Year = 2010 - config.parameters.Inset_Chart_Reporting_Stop_Year = 2020 + config.parameters.Inset_Chart_Reporting_Start_Year = 2005 + config.parameters.Inset_Chart_Reporting_Stop_Year = 2030 config.parameters.Enable_Demographics_Reporting = 0 - config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 2010 - config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2020 + config.parameters.Report_Typhoid_ByAgeAndGender_Start_Year = 2005 + config.parameters.Report_Typhoid_ByAgeAndGender_Stop_Year = 2030 config.parameters.Demographics_Filenames = [ "TestDemographics_pak_updated.json" ] @@ -138,11 +156,13 @@ def get_emod_task(self, set_param_fn): schema_path=manifest.schema_file, param_custom_cb=set_param_fn, ep4_custom_cb=None) - task.common_assets.add_asset("../../examples/HINTy/Assets/TestDemographics_pak_updated.json") + current_dir = os.path.dirname(__file__) + task.common_assets.add_asset(os.path.join(current_dir, "../../examples/HINTy/Assets/TestDemographics_pak_updated.json")) task.set_sif(manifest.sif) return task def test_new_routine_immunization_sweep_vax_effs(self): + vax_expiration_year = 2 def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): camp.set_schema(manifest.schema_file) @@ -153,7 +173,9 @@ def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): ria = tv.new_routine_immunization(camp, efficacy=vax_eff, start_day=year_to_days(CAMP_START_YEAR) + start_day_offset, - coverage=coverage + coverage=coverage, + expected_expiration=vax_expiration_year * 365, + decay_constant=0 ) camp.add(ria) return camp @@ -168,6 +190,8 @@ def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): experiment = Experiment.from_builder(builder, task, name=self.case_name) experiment.run(wait_until_done=True, platform=self.platform) + # exp_id = "55f5f1db-6e77-ee11-92fd-f0921c167864" + # experiment = self.platform.get_item(exp_id, item_type=ItemType.EXPERIMENT) task.handle_experiment_completion(experiment) task.get_file_from_comps(experiment.uid, ["ReportTyphoidByAgeAndGender.csv", "campaign.json"]) # Get downloaded local ReportEventRecorder.csv file path for all simulations @@ -203,7 +227,7 @@ def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): 0]['Vaccine_Type'], 'AcquisitionBlocking') self.assertEqual( camp_intv_config['Actual_IndividualIntervention_Config']['Actual_IndividualIntervention_Configs'][ - 0]['Waning_Config']['Decay_Time_Constant'], 6935.0) + 0]['Waning_Config']['Expected_Discard_Time'], 730) if i == 0: self.assertEqual( camp_intv_config['Actual_IndividualIntervention_Config'][ @@ -227,16 +251,12 @@ def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): df_list.append(df) for a, b in itertools.combinations(df_list, 2): - infected_by_age[i] = df[[df.columns.values[0], 'Infected', "Age"]].groupby('Age').sum()["Infected"].tolist() - self.compare_infected_before_vax(a, b) + self.compare_cases_before_vax(a, b) + self.compare_cases_after_vax(a, b) - # Since df_list is saved with simulation with order of vax_eff from 0 to 1, - # i.e first df in df_list is corresponding to vax_eff = 0, and last df in df_list is for sim with cax_eff = 1 - # we want to make sure after vax ingested, infected number for sim with vax_eff=0 should always greater than sim with vax_eff = 1 - # note, I did not compare every single sim since occasionally there maybe some outliers for comparison for sims in middle - self.compare_infected_after_vax(df_list[0], df_list[len(df_list) - 1]) def test_campaign_immunization_sweep_vax_effs(self): + vax_expiration_year = 2 def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): camp.set_schema(manifest.schema_file) @@ -245,14 +265,15 @@ def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): camp.add(event) tv_iv = tv.new_vax(camp, - efficacy=vax_eff + efficacy=vax_eff, + expected_expiration=vax_expiration_year * 365 ) one_time_campaign = comm.ScheduledCampaignEvent(camp, Start_Day=year_to_days(CAMP_START_YEAR) + start_day_offset, Intervention_List=[tv_iv], Demographic_Coverage=coverage, Target_Age_Min=0.75, - Target_Age_Max=15 + Target_Age_Max=15, ) camp.add(one_time_campaign) @@ -262,8 +283,6 @@ def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): builder = SimulationBuilder() vax_effs = np.linspace(0, 1.0, 3) # 0.0, 0.5, 1 (total 3 sims) builder.add_sweep_definition(partial(self.update_campaign_efficacy, build_camp), vax_effs) - # cov = np.linspace(0, 1.0, 3) - # builder.add_sweep_definition(partial(self.update_campaign_coverage, build_camp), cov) builder.add_sweep_definition(self.update_sim_random_seed, range(1)) experiment = Experiment.from_builder(builder, task, name=self.case_name) experiment.run(wait_until_done=True, platform=self.platform) @@ -308,12 +327,12 @@ def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): df_list.append(df) for a, b in itertools.combinations(df_list, 2): - infected_by_age[i] = df[[df.columns.values[0], 'Infected', "Age"]].groupby('Age').sum()["Infected"].tolist() - self.compare_infected_before_vax(a, b) - - self.compare_infected_after_vax_age_15(df_list[0], df_list[len(df_list) - 1]) + self.compare_cases_before_vax(a, b) + self.compare_cases_after_vax_age_15(a,b, vax_expiration_year=vax_expiration_year) def test_campaign_immunization_sweep_coverage(self): + vax_expiration_year = 2 + def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): camp.set_schema(manifest.schema_file) @@ -322,7 +341,8 @@ def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): camp.add(event) tv_iv = tv.new_vax(camp, - efficacy=vax_eff + efficacy=vax_eff, + expected_expiration=vax_expiration_year * 365 ) one_time_campaign = comm.ScheduledCampaignEvent(camp, Start_Day=year_to_days(CAMP_START_YEAR) + start_day_offset, @@ -344,8 +364,8 @@ def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): builder.add_sweep_definition(self.update_sim_random_seed, range(1)) experiment = Experiment.from_builder(builder, task, name=self.case_name) experiment.run(wait_until_done=True, platform=self.platform) - # exp_id = '38848a13-be67-ee11-92fc-f0921c167864' - # experiment = self.platform.get_item(exp_id, item_type=ItemType.EXPERIMENT) + #exp_id = 'dd4fd81b-5f77-ee11-92fd-f0921c167864' + #experiment = self.platform.get_item(exp_id, item_type=ItemType.EXPERIMENT) task.handle_experiment_completion(experiment) task.get_file_from_comps(experiment.uid, ["ReportTyphoidByAgeAndGender.csv", "campaign.json"]) reporteventrecorder_downloaded = list( @@ -368,7 +388,10 @@ def build_camp(start_day_offset=1, vax_eff=0.82, coverage=1): df = pd.read_csv(reporteventrecorder_downloaded[i]) df.columns = df.columns.to_series().apply(lambda x: x.strip()) df_list.append(df) - self.compare_infected_after_vax(df_list[0], df_list[len(df_list) - 1]) + + # compare cases after vax with duration of expected_expiration_year for all simulations + for a, b in itertools.combinations(df_list, 2): + self.compare_cases_after_vax(a, b, vax_expiration_year=vax_expiration_year) def test_campaign_immunization_sweep_decay_time_constant(self): def build_camp(start_day_offset=1, vax_eff=0.82, decay_constant=6935, coverage=1): @@ -397,13 +420,11 @@ def build_camp(start_day_offset=1, vax_eff=0.82, decay_constant=6935, coverage=1 builder = SimulationBuilder() decay_constant = [1000, 2000, 3000] builder.add_sweep_definition(partial(self.update_campaign_decay, build_camp), decay_constant) - # cov = np.linspace(0, 1.0, 3) - # builder.add_sweep_definition(partial(self.update_campaign_coverage, build_camp), cov) builder.add_sweep_definition(self.update_sim_random_seed, range(1)) experiment = Experiment.from_builder(builder, task, name=self.case_name) experiment.run(wait_until_done=True, platform=self.platform) - #exp_id = 'ca28fe4b-c367-ee11-92fc-f0921c167864' - #experiment = self.platform.get_item(exp_id, item_type=ItemType.EXPERIMENT) + # exp_id = 'fb4df8ca-6f77-ee11-92fd-f0921c167864' + # experiment = self.platform.get_item(exp_id, item_type=ItemType.EXPERIMENT) task.handle_experiment_completion(experiment) task.get_file_from_comps(experiment.uid, ["ReportTyphoidByAgeAndGender.csv", "campaign.json"]) reporteventrecorder_downloaded = list( @@ -426,7 +447,6 @@ def build_camp(start_day_offset=1, vax_eff=0.82, decay_constant=6935, coverage=1 df = pd.read_csv(reporteventrecorder_downloaded[i]) df.columns = df.columns.to_series().apply(lambda x: x.strip()) df_list.append(df) - infected_by_age = pd.DataFrame() for a, b in itertools.combinations(df_list, 2): - infected_by_age[i] = df[[df.columns.values[0], 'Infected', "Age"]].groupby('Age').sum()["Infected"].tolist() - self.compare_infected_before_vax(a, b) + self.compare_cases_before_vax(a, b) + self.compare_cases_after_vax(a, b) diff --git a/version.py b/version.py index 44133c9..2ed865a 100644 --- a/version.py +++ b/version.py @@ -1,3 +1,3 @@ -__version__ = '0.0.4' +__version__ = '0.1.0'