From 7590a5b12bd3d9f506bc4a303b839e743c6a53a2 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 9 Feb 2024 13:52:47 +0000 Subject: [PATCH 001/131] updates from paper analyses for HIV, TB and malaria --- resources/ResourceFile_HIV.xlsx | 4 +- resources/ResourceFile_TB.xlsx | 4 +- ...ourceFile_PriorityRanking_ALLPOLICIES.xlsx | 4 +- resources/malaria/ResourceFile_malaria.xlsx | 4 +- src/tlo/methods/hiv.py | 1354 ++++++++++----- src/tlo/methods/malaria.py | 1489 +++++++++++------ src/tlo/methods/tb.py | 1061 ++++++------ tests/test_malaria.py | 20 +- tests/test_tb.py | 60 +- 9 files changed, 2446 insertions(+), 1554 deletions(-) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index ade32b3ccd..96f361dece 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7613f565d446cdf31f7360cc3d72d2deae6545294a41d3b0580430bec63f7d5 -size 158138 +oid sha256:d3ad1b77dc635a0df8d552da3f054856c3ef4feccb7f2e26280d3a1ac4c3d336 +size 160604 diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index 494e6b31fd..266fa479c1 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6760fe2b529eb1538bbbfbd6d5e7350f9dbc2272ab997b363771c8b901739bb3 -size 54894 +oid sha256:4adad7f8861f069bc3515b34ec39e4aa50b8fe260caa60bf49274a2c9a1fbc6a +size 55722 diff --git a/resources/healthsystem/priority_policies/ResourceFile_PriorityRanking_ALLPOLICIES.xlsx b/resources/healthsystem/priority_policies/ResourceFile_PriorityRanking_ALLPOLICIES.xlsx index ef1c48eb20..4b6c6a144b 100644 --- a/resources/healthsystem/priority_policies/ResourceFile_PriorityRanking_ALLPOLICIES.xlsx +++ b/resources/healthsystem/priority_policies/ResourceFile_PriorityRanking_ALLPOLICIES.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:88a459c5b66dd7d6f5a6d5597577cbe5c0892463302da69ec7a0d19183f11f62 -size 33223 +oid sha256:1012950be2346232550e7d2efaa5efe936b77a34a4bcb3e713dd12c4abc434d5 +size 42381 diff --git a/resources/malaria/ResourceFile_malaria.xlsx b/resources/malaria/ResourceFile_malaria.xlsx index 8b24ddfbb3..9692613985 100644 --- a/resources/malaria/ResourceFile_malaria.xlsx +++ b/resources/malaria/ResourceFile_malaria.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bba763e85615f7bf3d2ae3765495b556a22c7c9ecd146eb96bfdbd6096276a35 -size 68359 +oid sha256:9b5e7cadad2a14d0e547238a0cb02c75c41c39f3bf1106ca8a903f0364f8c048 +size 67591 diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index fd631b8ca4..0b192a5235 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -30,7 +30,12 @@ import pandas as pd from tlo import DAYS_IN_YEAR, DateOffset, Module, Parameter, Property, Types, logging -from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent +from tlo.events import ( + Event, + IndividualScopeEventMixin, + PopulationScopeEventMixin, + RegularEvent, +) from tlo.lm import LinearModel, LinearModelType, Predictor from tlo.methods import Metadata, demography, tb from tlo.methods.causes import Cause @@ -58,12 +63,13 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): self.stored_test_numbers = [] # create empty list for storing hiv test numbers # hiv outputs needed for calibration - keys = ["date", - "hiv_prev_adult_1549", - "hiv_adult_inc_1549", - "hiv_prev_child", - "population" - ] + keys = [ + "date", + "hiv_prev_adult_1549", + "hiv_adult_inc_1549", + "hiv_prev_child", + "population", + ] # initialise empty dict with set keys self.hiv_outputs = {k: [] for k in keys} @@ -75,13 +81,13 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): OPTIONAL_INIT_DEPENDENCIES = {"HealthBurden"} - ADDITIONAL_DEPENDENCIES = {'Tb', 'NewbornOutcomes'} + ADDITIONAL_DEPENDENCIES = {"Tb", "NewbornOutcomes"} METADATA = { Metadata.DISEASE_MODULE, Metadata.USES_SYMPTOMMANAGER, Metadata.USES_HEALTHSYSTEM, - Metadata.USES_HEALTHBURDEN + Metadata.USES_HEALTHBURDEN, } # Declare Causes of Death @@ -106,9 +112,13 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "ART status of person, whether on ART or not; and whether viral load is suppressed or not if on ART.", categories=["not", "on_VL_suppressed", "on_not_VL_suppressed"], ), + "hv_on_cotrimoxazole": Property( + Types.BOOL, + "Whether the person is currently taking and receiving a malaria-protective effect from cotrimoxazole", + ), "hv_is_on_prep": Property( Types.BOOL, - "Whether the person is currently taking and receiving a protective effect from Pre-Exposure Prophylaxis.", + "Whether the person is currently taking and receiving a protective effect from Pre-Exposure Prophylaxis", ), "hv_behaviour_change": Property( Types.BOOL, @@ -130,11 +140,14 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): Types.DATA_FRAME, "prob of time since infection for baseline adult pop" ), "art_coverage": Parameter(Types.DATA_FRAME, "coverage of ART at baseline"), - "treatment_cascade": Parameter(Types.DATA_FRAME, "spectrum estimates of treatment cascade"), + "treatment_cascade": Parameter( + Types.DATA_FRAME, "spectrum estimates of treatment cascade" + ), # Natural history - transmission - overall rates "beta": Parameter(Types.REAL, "Transmission rate"), "unaids_prevalence_adjustment_factor": Parameter( - Types.REAL, "adjustment for baseline age-specific prevalence values to give correct population prevalence" + Types.REAL, + "adjustment for baseline age-specific prevalence values to give correct population prevalence", ), "prob_mtct_untreated": Parameter( Types.REAL, "Probability of mother to child transmission" @@ -370,6 +383,10 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): Types.REAL, "probability of death if aids and tb, person on treatment for tb", ), + "hiv_healthseekingbehaviour_cap": Parameter( + Types.INT, + "number of repeat visits assumed for healthcare services", + ), } def read_parameters(self, data_folder): @@ -380,7 +397,7 @@ def read_parameters(self, data_folder): # 1) Read the ResourceFiles - # Short cut to parameters dict + # Shortcut to parameters dict p = self.parameters workbook = pd.read_excel( @@ -442,8 +459,9 @@ def pre_initialise_population(self): Predictor("age_years").when("<15", 0.0).when("<49", 1.0).otherwise(0.0), Predictor("sex").when("F", p["rr_sex_f"]), Predictor("li_is_circ").when(True, p["rr_circumcision"]), - Predictor("hv_is_on_prep"). - when(True, 1.0 - p["proportion_reduction_in_risk_of_hiv_aq_if_on_prep"]), + Predictor("hv_is_on_prep").when( + True, 1.0 - p["proportion_reduction_in_risk_of_hiv_aq_if_on_prep"] + ), Predictor("li_urban").when(False, p["rr_rural"]), Predictor("li_wealth", conditions_are_mutually_exclusive=True) .when(2, p["rr_windex_poorer"]) @@ -461,9 +479,13 @@ def pre_initialise_population(self): Predictor( "age_years", conditions_are_mutually_exclusive=True, - conditions_are_exhaustive=True) + conditions_are_exhaustive=True, + ) .when("==0", p["mean_survival_for_infants_infected_prior_to_birth"]) - .when(".between(1,4)", p["infection_to_death_infant_infection_after_birth_weibull_scale"]) + .when( + ".between(1,4)", + p["infection_to_death_infant_infection_after_birth_weibull_scale"], + ) .when(".between(5, 19)", p["infection_to_death_weibull_scale_1519"]) .when(".between(20, 24)", p["infection_to_death_weibull_scale_2024"]) .when(".between(25, 29)", p["infection_to_death_weibull_scale_2529"]) @@ -478,9 +500,15 @@ def pre_initialise_population(self): Predictor( "age_years", conditions_are_mutually_exclusive=True, - conditions_are_exhaustive=True) - .when("==0", 1) # Weibull with shape=1 equivalent to exponential distribution - .when(".between(1,4)", p["infection_to_death_infant_infection_after_birth_weibull_shape"]) + conditions_are_exhaustive=True, + ) + .when( + "==0", 1 + ) # Weibull with shape=1 equivalent to exponential distribution + .when( + ".between(1,4)", + p["infection_to_death_infant_infection_after_birth_weibull_shape"], + ) .when(".between(5, 19)", p["infection_to_death_weibull_shape_1519"]) .when(".between(20, 24)", p["infection_to_death_weibull_shape_2024"]) .when(".between(25, 29)", p["infection_to_death_weibull_shape_2529"]) @@ -492,13 +520,16 @@ def pre_initialise_population(self): ) # -- Linear Model to give the mean months between aids and death depending on age - self.lm["offset_parameter_for_months_from_aids_to_death"] = LinearModel.multiplicative( - Predictor( - "age_years", - conditions_are_mutually_exclusive=True, - conditions_are_exhaustive=True) - .when("<5", p["mean_months_between_aids_and_death_infant"]) - .when(">=5", p["mean_months_between_aids_and_death"]) + self.lm["offset_parameter_for_months_from_aids_to_death"] = ( + LinearModel.multiplicative( + Predictor( + "age_years", + conditions_are_mutually_exclusive=True, + conditions_are_exhaustive=True, + ) + .when("<5", p["mean_months_between_aids_and_death_infant"]) + .when(">=5", p["mean_months_between_aids_and_death"]) + ) ) # -- Linear Models for the Uptake of Services @@ -541,11 +572,14 @@ def pre_initialise_population(self): self.lm["lm_circ_child"] = LinearModel.multiplicative( Predictor("sex").when("M", 1.0).otherwise(0.0), Predictor("age_years").when("<15", 1.0).otherwise(0.0), - Predictor("year", - external=True, - conditions_are_mutually_exclusive=True, - conditions_are_exhaustive=True).when("<2020", p["prob_circ_for_child_before_2020"]) - .otherwise(p["prob_circ_for_child_from_2020"]) + Predictor( + "year", + external=True, + conditions_are_mutually_exclusive=True, + conditions_are_exhaustive=True, + ) + .when("<2020", p["prob_circ_for_child_before_2020"]) + .otherwise(p["prob_circ_for_child_from_2020"]), ) def initialise_population(self, population): @@ -570,7 +604,9 @@ def initialise_population(self, population): self.initialise_baseline_prevalence(population) # allocate baseline prevalence self.initialise_baseline_art(population) # allocate baseline art coverage - self.initialise_baseline_tested(population) # allocate baseline testing coverage + self.initialise_baseline_tested( + population + ) # allocate baseline testing coverage def initialise_baseline_prevalence(self, population): """ @@ -597,16 +633,14 @@ def initialise_baseline_prevalence(self, population): Predictor("li_is_sexworker").when(True, params["rr_fsw"]), Predictor("li_is_circ").when(True, params["rr_circumcision"]), Predictor("li_urban").when(False, params["rr_rural"]), - Predictor( - "li_wealth", conditions_are_mutually_exclusive=True).when( - 2, params["rr_windex_poorer"]).when( - 3, params["rr_windex_middle"]).when( - 4, params["rr_windex_richer"]).when( - 5, params["rr_windex_richest"]), - Predictor( - "li_ed_lev", conditions_are_mutually_exclusive=True).when( - 2, params["rr_edlevel_primary"]).when( - 3, params["rr_edlevel_secondary"]) + Predictor("li_wealth", conditions_are_mutually_exclusive=True) + .when(2, params["rr_windex_poorer"]) + .when(3, params["rr_windex_middle"]) + .when(4, params["rr_windex_richer"]) + .when(5, params["rr_windex_richest"]), + Predictor("li_ed_lev", conditions_are_mutually_exclusive=True) + .when(2, params["rr_edlevel_primary"]) + .when(3, params["rr_edlevel_secondary"]), ).predict(df.loc[df.is_alive]) # Rescale relative probability of infection so that its average is 1.0 within each age/sex group @@ -628,12 +662,15 @@ def initialise_baseline_prevalence(self, population): # add scaling factor 1.1 to match overall unaids prevalence # different assumptions on pop size result in slightly different overall prevalence so use adjustment factor p["overall_prob_of_infec"] = ( - p["scaled_rel_prob_by_risk_factor"] * p["prob_of_infec"] * params["unaids_prevalence_adjustment_factor"] + p["scaled_rel_prob_by_risk_factor"] + * p["prob_of_infec"] + * params["unaids_prevalence_adjustment_factor"] ) # this needs to be series of True/False infec = ( - self.rng.random_sample(len(p["overall_prob_of_infec"])) - < p["overall_prob_of_infec"]) & df.is_alive + self.rng.random_sample(len(p["overall_prob_of_infec"])) + < p["overall_prob_of_infec"] + ) & df.is_alive # Assign the designated person as infected in the population.props dataframe: df.loc[infec, "hv_inf"] = True @@ -679,7 +716,7 @@ def initialise_baseline_art(self, population): rr_art = pd.Series(1, index=df.index) rr_art.loc[ df.is_alive & (df.hv_date_inf < (self.sim.date - pd.DateOffset(years=10))) - ] = params["rel_probability_art_baseline_aids"] + ] = params["rel_probability_art_baseline_aids"] # Rescale relative probability of infection so that its average is 1.0 within each age/sex group p = pd.DataFrame( @@ -703,7 +740,7 @@ def initialise_baseline_art(self, population): art_idx = df.index[ (random_draw < p["overall_prob_of_art"]) & df.is_alive & df.hv_inf - ] + ] # 2) Determine adherence levels for those currently on ART, for each of adult men, adult women and children adult_f_art_idx = df.loc[ @@ -723,7 +760,9 @@ def split_into_vl_and_notvl(all_idx, prob): notsuppr.extend(all_idx[~vl_suppr]) # get expected viral suppression rates by age and year - prob_vs_adult = self.prob_viral_suppression(self.sim.date.year, age_of_person=20) + prob_vs_adult = self.prob_viral_suppression( + self.sim.date.year, age_of_person=20 + ) prob_vs_child = self.prob_viral_suppression(self.sim.date.year, age_of_person=5) split_into_vl_and_notvl(adult_f_art_idx, prob_vs_adult) @@ -765,64 +804,68 @@ def initialise_baseline_tested(self, population): testing_data = worksheet.loc[ worksheet.year == 2010, ["year", "age", "know_status"] ] - adult_know_status = testing_data.loc[(testing_data.age == "adults"), "know_status"].values[0] / 100 - children_know_status = testing_data.loc[(testing_data.age == "children"), "know_status"].values[0] / 100 + adult_know_status = ( + testing_data.loc[(testing_data.age == "adults"), "know_status"].values[0] + / 100 + ) + children_know_status = ( + testing_data.loc[(testing_data.age == "children"), "know_status"].values[0] + / 100 + ) # ADULTS # find proportion of adult PLHIV diagnosed (currently on ART) - adults_diagnosed = len(df[df.is_alive - & df.hv_diagnosed - & (df.age_years >= 15)]) + adults_diagnosed = len(df[df.is_alive & df.hv_diagnosed & (df.age_years >= 15)]) - adults_infected = len(df[df.is_alive - & df.hv_inf - & (df.age_years >= 15)]) + adults_infected = len(df[df.is_alive & df.hv_inf & (df.age_years >= 15)]) - prop_currently_diagnosed = adults_diagnosed / adults_infected if adults_infected > 0 else 0 + prop_currently_diagnosed = ( + adults_diagnosed / adults_infected if adults_infected > 0 else 0 + ) hiv_test_deficit = adult_know_status - prop_currently_diagnosed number_deficit = int(hiv_test_deficit * adults_infected) adult_test_index = [] if hiv_test_deficit > 0: # sample number_deficit from remaining undiagnosed pop - adult_undiagnosed = df.loc[df.is_alive - & df.hv_inf - & ~df.hv_diagnosed - & (df.age_years >= 15)].index + adult_undiagnosed = df.loc[ + df.is_alive & df.hv_inf & ~df.hv_diagnosed & (df.age_years >= 15) + ].index - adult_test_index = self.rng.choice(adult_undiagnosed, size=number_deficit, replace=False) + adult_test_index = self.rng.choice( + adult_undiagnosed, size=number_deficit, replace=False + ) # CHILDREN # find proportion of adult PLHIV diagnosed (currently on ART) - children_diagnosed = len(df[df.is_alive - & df.hv_diagnosed - & (df.age_years < 15)]) + children_diagnosed = len( + df[df.is_alive & df.hv_diagnosed & (df.age_years < 15)] + ) - children_infected = len(df[df.is_alive - & df.hv_inf - & (df.age_years < 15)]) + children_infected = len(df[df.is_alive & df.hv_inf & (df.age_years < 15)]) - prop_currently_diagnosed = children_diagnosed / children_infected if children_infected > 0 else 0 + prop_currently_diagnosed = ( + children_diagnosed / children_infected if children_infected > 0 else 0 + ) hiv_test_deficit = children_know_status - prop_currently_diagnosed number_deficit = int(hiv_test_deficit * children_infected) child_test_index = [] if hiv_test_deficit > 0: - child_undiagnosed = df.loc[df.is_alive - & df.hv_inf - & ~df.hv_diagnosed - & (df.age_years < 15)].index + child_undiagnosed = df.loc[ + df.is_alive & df.hv_inf & ~df.hv_diagnosed & (df.age_years < 15) + ].index - child_test_index = self.rng.choice(child_undiagnosed, size=number_deficit, replace=False) + child_test_index = self.rng.choice( + child_undiagnosed, size=number_deficit, replace=False + ) # join indices test_index = list(adult_test_index) + list(child_test_index) df.loc[df.index.isin(test_index), "hv_diagnosed"] = True # dummy date for date last hiv test (before sim start), otherwise see big spike in testing 01-01-2010 - df.loc[test_index, "hv_last_test_date"] = self.sim.date - pd.DateOffset( - years=3 - ) + df.loc[test_index, "hv_last_test_date"] = self.sim.date - pd.DateOffset(years=3) def initialise_simulation(self, sim): """ @@ -837,9 +880,7 @@ def initialise_simulation(self, sim): df = sim.population.props # 1) Schedule the Main HIV Regular Polling Event - sim.schedule_event( - HivRegularPollingEvent(self), sim.date + DateOffset(days=0) - ) + sim.schedule_event(HivRegularPollingEvent(self), sim.date + DateOffset(days=0)) # 2) Schedule the Logging Event sim.schedule_event(HivLoggingEvent(self), sim.date + DateOffset(years=1)) @@ -856,10 +897,14 @@ def initialise_simulation(self, sim): & df.hv_inf & ((self.sim.date - df.hv_date_inf).dt.days > 10 * 365) & (df.hv_art == "not") - ].index + ].index # Those that are in neither category are "before AIDS" (will have AIDS Onset Event scheduled) - before_aids_idx = df.loc[df.is_alive & df.hv_inf].index.difference(has_aids_idx).difference(on_art_idx) + before_aids_idx = ( + df.loc[df.is_alive & df.hv_inf] + .index.difference(has_aids_idx) + .difference(on_art_idx) + ) # Impose the symptom to those that have AIDS (the symptom is the definition of having AIDS) self.sim.modules["SymptomManager"].change_symptom( @@ -873,9 +918,17 @@ def initialise_simulation(self, sim): # AIDS Onset Event for those who are infected but not yet AIDS and have not ever started ART # NB. This means that those on ART at the start of the simulation may not have an AIDS event -- # like it happened at some point in the past - scale, shape, offset = self.get_time_from_infection_to_aids_distribution_parameters(before_aids_idx) - days_infection_to_aids = self.sample_time_from_infection_to_aids_given_parameters(scale, shape, offset) - days_since_infection = (self.sim.date - df.loc[before_aids_idx, "hv_date_inf"]) + scale, shape, offset = ( + self.get_time_from_infection_to_aids_distribution_parameters( + before_aids_idx + ) + ) + days_infection_to_aids = ( + self.sample_time_from_infection_to_aids_given_parameters( + scale, shape, offset + ) + ) + days_since_infection = self.sim.date - df.loc[before_aids_idx, "hv_date_inf"] # If any days_since_infection >= days_infection_to_aids are negative resample # these values until all are positive days_until_aids_is_negative = days_since_infection >= days_infection_to_aids @@ -889,10 +942,12 @@ def initialise_simulation(self, sim): ) days_until_aids_is_negative = days_since_infection >= days_infection_to_aids days_until_aids = days_infection_to_aids - days_since_infection - date_onset_aids = self.sim.date + pd.to_timedelta(days_until_aids, unit='D') + date_onset_aids = self.sim.date + pd.to_timedelta(days_until_aids, unit="D") for person_id, date in zip(before_aids_idx, date_onset_aids): sim.schedule_event( - HivAidsOnsetEvent(person_id=person_id, module=self, cause='AIDS_non_TB'), + HivAidsOnsetEvent( + person_id=person_id, module=self, cause="AIDS_non_TB" + ), date=date, ) @@ -902,24 +957,46 @@ def initialise_simulation(self, sim): date_test = self.sim.date + pd.DateOffset(days=self.rng.randint(0, 60)) self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_TestAndRefer( - person_id=person_id, module=self, referred_from="initialise_simulation"), + person_id=person_id, + module=self, + referred_from="initialise_simulation", + ), priority=1, topen=date_test, tclose=self.sim.date + pd.DateOffset(days=365), ) - date_aids_death = ( - self.sim.date + pd.DateOffset(months=self.rng.randint(low=0, high=18)) + date_aids_death = self.sim.date + pd.DateOffset( + months=self.rng.randint(low=0, high=18) ) # 30% AIDS deaths have TB co-infection - cause_of_death = self.rng.choice(a=["AIDS_non_TB", "AIDS_TB"], size=1, p=[0.7, 0.3]) + cause_of_death = self.rng.choice( + a=["AIDS_non_TB", "AIDS_TB"], size=1, p=[0.7, 0.3] + ) sim.schedule_event( - HivAidsDeathEvent(person_id=person_id, module=self, cause=cause_of_death), + HivAidsDeathEvent( + person_id=person_id, module=self, cause=cause_of_death + ), date=date_aids_death, ) + # schedule hospital stay for end of life care if untreated + beddays = self.rng.randint(low=14, high=20) + date_admission = date_aids_death - pd.DateOffset(days=beddays) + self.sim.modules["HealthSystem"].schedule_hsi_event( + hsi_event=HSI_Hiv_EndOfLifeCare( + person_id=person_id, module=self, beddays=beddays + ), + priority=0, + topen=( + date_admission + if (date_admission >= self.sim.date) + else self.sim.date + ), + ) + # 5) (Optionally) Schedule the event to check the configuration of all properties if self.run_with_checks: sim.schedule_event( @@ -931,74 +1008,104 @@ def initialise_simulation(self, sim): # updated consumables listing # blood tube and gloves are optional items - self.item_codes_for_consumables_required['hiv_rapid_test'] = \ + self.item_codes_for_consumables_required["hiv_rapid_test"] = ( hs.get_item_code_from_item_name("Test, HIV EIA Elisa") + ) - self.item_codes_for_consumables_required['hiv_early_infant_test'] = \ + self.item_codes_for_consumables_required["hiv_early_infant_test"] = ( hs.get_item_code_from_item_name("Test, HIV EIA Elisa") + ) - self.item_codes_for_consumables_required['blood_tube'] = \ + self.item_codes_for_consumables_required["blood_tube"] = ( hs.get_item_code_from_item_name("Blood collecting tube, 5 ml") + ) - self.item_codes_for_consumables_required['gloves'] = \ - hs.get_item_code_from_item_name("Disposables gloves, powder free, 100 pieces per box") + self.item_codes_for_consumables_required["gloves"] = ( + hs.get_item_code_from_item_name( + "Disposables gloves, powder free, 100 pieces per box" + ) + ) - self.item_codes_for_consumables_required['vl_measurement'] = \ + self.item_codes_for_consumables_required["vl_measurement"] = ( hs.get_item_codes_from_package_name("Viral Load") + ) - self.item_codes_for_consumables_required['circ'] = \ + self.item_codes_for_consumables_required["circ"] = ( hs.get_item_codes_from_package_name("Male circumcision ") + ) - self.item_codes_for_consumables_required['prep'] = { - hs.get_item_code_from_item_name("Tenofovir (TDF)/Emtricitabine (FTC), tablet, 300/200 mg"): 1} + self.item_codes_for_consumables_required["prep"] = { + hs.get_item_code_from_item_name( + "Tenofovir (TDF)/Emtricitabine (FTC), tablet, 300/200 mg" + ): 1 + } # infant NVP given in 3-monthly dosages - self.item_codes_for_consumables_required['infant_prep'] = { - hs.get_item_code_from_item_name("Nevirapine, oral solution, 10 mg/ml"): 1} + self.item_codes_for_consumables_required["infant_prep"] = { + hs.get_item_code_from_item_name("Nevirapine, oral solution, 10 mg/ml"): 1 + } # First - line ART for adults(age > "ART_age_cutoff_older_child") - self.item_codes_for_consumables_required['First-line ART regimen: adult'] = { - hs.get_item_code_from_item_name("First-line ART regimen: adult"): 1} - self.item_codes_for_consumables_required['First-line ART regimen: adult: cotrimoxazole'] = { - hs.get_item_code_from_item_name("Cotrimoxizole, 960mg pppy"): 1} + self.item_codes_for_consumables_required["First-line ART regimen: adult"] = { + hs.get_item_code_from_item_name("First-line ART regimen: adult"): 1 + } + self.item_codes_for_consumables_required[ + "First-line ART regimen: adult: cotrimoxazole" + ] = {hs.get_item_code_from_item_name("Cotrimoxizole, 960mg pppy"): 1} # ART for older children aged ("ART_age_cutoff_younger_child" < age <= "ART_age_cutoff_older_child"): # cotrim is separate item - optional in get_cons call - self.item_codes_for_consumables_required['First line ART regimen: older child'] = { - hs.get_item_code_from_item_name("First line ART regimen: older child"): 1} - self.item_codes_for_consumables_required['First line ART regimen: older child: cotrimoxazole'] = { - hs.get_item_code_from_item_name("Sulfamethoxazole + trimethropin, tablet 400 mg + 80 mg"): 1} + self.item_codes_for_consumables_required[ + "First line ART regimen: older child" + ] = {hs.get_item_code_from_item_name("First line ART regimen: older child"): 1} + self.item_codes_for_consumables_required[ + "First line ART regimen: older child: cotrimoxazole" + ] = { + hs.get_item_code_from_item_name( + "Sulfamethoxazole + trimethropin, tablet 400 mg + 80 mg" + ): 1 + } # ART for younger children aged (age < "ART_age_cutoff_younger_child"): - self.item_codes_for_consumables_required['First line ART regimen: young child'] = { - hs.get_item_code_from_item_name("First line ART regimen: young child"): 1} - self.item_codes_for_consumables_required['First line ART regimen: young child: cotrimoxazole'] = { - hs.get_item_code_from_item_name("Sulfamethoxazole + trimethropin, oral suspension, 240 mg, 100 ml"): 1} + self.item_codes_for_consumables_required[ + "First line ART regimen: young child" + ] = {hs.get_item_code_from_item_name("First line ART regimen: young child"): 1} + self.item_codes_for_consumables_required[ + "First line ART regimen: young child: cotrimoxazole" + ] = { + hs.get_item_code_from_item_name( + "Sulfamethoxazole + trimethropin, oral suspension, 240 mg, 100 ml" + ): 1 + } # 7) Define the DxTests # HIV Rapid Diagnostic Test: # NB. The rapid test is assumed to be 100% specific and sensitive. This is used to guarantee that all persons # that start ART are truly HIV-pos. - self.sim.modules['HealthSystem'].dx_manager.register_dx_test( + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( hiv_rapid_test=DxTest( - property='hv_inf', - item_codes=self.item_codes_for_consumables_required['hiv_rapid_test'], + property="hv_inf", + item_codes=self.item_codes_for_consumables_required["hiv_rapid_test"], optional_item_codes=[ - self.item_codes_for_consumables_required['blood_tube'], - self.item_codes_for_consumables_required['gloves']] + self.item_codes_for_consumables_required["blood_tube"], + self.item_codes_for_consumables_required["gloves"], + ], ) ) # Test for Early Infect Diagnosis - self.sim.modules['HealthSystem'].dx_manager.register_dx_test( + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( hiv_early_infant_test=DxTest( - property='hv_inf', + property="hv_inf", sensitivity=1.0, specificity=1.0, - item_codes=self.item_codes_for_consumables_required['hiv_early_infant_test'], + item_codes=self.item_codes_for_consumables_required[ + "hiv_early_infant_test" + ], optional_item_codes=[ - self.item_codes_for_consumables_required['blood_tube'], - self.item_codes_for_consumables_required['gloves']] + self.item_codes_for_consumables_required["blood_tube"], + self.item_codes_for_consumables_required["gloves"], + ], ) ) @@ -1015,6 +1122,7 @@ def on_birth(self, mother_id, child_id): # --- Current status df.at[child_id, "hv_inf"] = False df.at[child_id, "hv_art"] = "not" + df.at[child_id, "hv_on_cotrimoxazole"] = False df.at[child_id, "hv_date_treated"] = pd.NaT df.at[child_id, "hv_is_on_prep"] = False df.at[child_id, "hv_behaviour_change"] = False @@ -1042,15 +1150,11 @@ def on_birth(self, mother_id, child_id): child_infected = self.rng.random_sample() < p["prob_mtct_treated"] else: # mother was infected prior to pregnancy but is not on VL suppressed at time of delivery - child_infected = ( - self.rng.random_sample() < p["prob_mtct_untreated"] - ) + child_infected = self.rng.random_sample() < p["prob_mtct_untreated"] elif mother_infected_during_pregnancy: # mother has incident infection during pregnancy, NO ART - child_infected = ( - self.rng.random_sample() < p["prob_mtct_incident_preg"] - ) + child_infected = self.rng.random_sample() < p["prob_mtct_incident_preg"] else: # mother is not infected @@ -1073,52 +1177,57 @@ def on_birth(self, mother_id, child_id): if "CareOfWomenDuringPregnancy" not in self.sim.modules: # if mother's HIV status not known, schedule test at delivery # usually performed by care_of_women_during_pregnancy module - if not mother.hv_diagnosed and \ - mother.is_alive and ( - self.rng.random_sample() < p["prob_hiv_test_at_anc_or_delivery"]): + if ( + not mother.hv_diagnosed + and mother.is_alive + and (self.rng.random_sample() < p["prob_hiv_test_at_anc_or_delivery"]) + ): self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_TestAndRefer( - person_id=abs(mother_id), # Pass mother's id, whether from true or direct birth + person_id=abs( + mother_id + ), # Pass mother's id, whether from true or direct birth module=self, - referred_from='ANC_routine'), + referred_from="ANC_routine", + ), priority=1, topen=self.sim.date, tclose=None, ) - # if mother known HIV+, schedule virological test for infant + # if mother known HIV+, schedule virological test for infant and give prep if mother.hv_diagnosed and df.at[child_id, "is_alive"]: self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_StartInfantProphylaxis( person_id=child_id, module=self, referred_from="on_birth", - repeat_visits=0), + repeat_visits=0, + ), priority=1, topen=self.sim.date, tclose=None, ) if "newborn_outcomes" not in self.sim.modules and ( - self.rng.random_sample() < p['prob_hiv_test_for_newborn_infant']): + self.rng.random_sample() < p["prob_hiv_test_for_newborn_infant"] + ): self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_TestAndRefer( - person_id=child_id, - module=self, - referred_from='Infant_testing'), + person_id=child_id, module=self, referred_from="Infant_testing" + ), priority=1, topen=self.sim.date + pd.DateOffset(weeks=6), tclose=None, ) # these later infant tests are not in newborn_outcomes - if self.rng.random_sample() < p['prob_hiv_test_for_newborn_infant']: + if self.rng.random_sample() < p["prob_hiv_test_for_newborn_infant"]: self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_TestAndRefer( - person_id=child_id, - module=self, - referred_from='Infant_testing'), + person_id=child_id, module=self, referred_from="Infant_testing" + ), priority=1, topen=self.sim.date + pd.DateOffset(months=9), tclose=None, @@ -1126,9 +1235,8 @@ def on_birth(self, mother_id, child_id): self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_TestAndRefer( - person_id=child_id, - module=self, - referred_from='Infant_testing'), + person_id=child_id, module=self, referred_from="Infant_testing" + ), priority=1, topen=self.sim.date + pd.DateOffset(months=18), tclose=None, @@ -1144,9 +1252,9 @@ def report_daly_values(self): dalys.loc[df.hv_inf] = self.daly_wts["hiv_infection_but_not_aids"] # Overwrite the value for those that currently have symptoms of AIDS with the 'AIDS' daly_wt: - dalys.loc[ - self.sim.modules["SymptomManager"].who_has("aids_symptoms") - ] = self.daly_wts["aids"] + dalys.loc[self.sim.modules["SymptomManager"].who_has("aids_symptoms")] = ( + self.daly_wts["aids"] + ) return dalys @@ -1198,7 +1306,7 @@ def do_new_infection(self, person_id): + self.sample_time_from_infection_to_aids_given_parameters(*parameters) ).iloc[0] self.sim.schedule_event( - event=HivAidsOnsetEvent(self, person_id, cause='AIDS_non_TB'), + event=HivAidsOnsetEvent(self, person_id, cause="AIDS_non_TB"), date=date_onset_aids, ) @@ -1218,7 +1326,7 @@ def sample_time_from_infection_to_aids_given_parameters(self, scale, shape, offs months_to_death = self.rng.weibull(shape) * scale * 12 months_to_aids = np.round(months_to_death - offset).clip(0).astype(int) - return pd.to_timedelta(months_to_aids * 30.5, unit='D') + return pd.to_timedelta(months_to_aids * 30.5, unit="D") def get_time_from_infection_to_aids_distribution_parameters(self, person_ids): """Compute per-person parameters of distribution of time from infection to aids. @@ -1255,10 +1363,10 @@ def get_time_from_aids_to_death(self): """ mean = self.parameters["mean_months_between_aids_and_death"] draw_number_of_months = int(np.round(self.rng.exponential(mean))) - return pd.DateOffset(months=draw_number_of_months) + return pd.DateOffset(months=(draw_number_of_months + 1)) def do_when_hiv_diagnosed(self, person_id): - """Things to do when a person has been tested and found (newly) be be HIV-positive:. + """Things to do when a person has been tested and found (newly) be HIV-positive:. * Consider if ART should be initiated, and schedule HSI if so. The person should not yet be on ART. """ @@ -1274,19 +1382,22 @@ def do_when_hiv_diagnosed(self, person_id): if df.loc[person_id, "age_years"] <= 15: starts_art = True else: - starts_art = self.rng.random_sample() < self.prob_art_start_after_test(self.sim.date.year) + starts_art = self.rng.random_sample() < self.prob_art_start_after_test( + self.sim.date.year + ) if starts_art: self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Hiv_StartOrContinueTreatment(person_id=person_id, module=self, - facility_level_of_this_hsi="1a"), + HSI_Hiv_StartOrContinueTreatment( + person_id=person_id, module=self, facility_level_of_this_hsi="1a" + ), topen=self.sim.date, tclose=None, priority=0, ) def prob_art_start_after_test(self, year): - """ returns the probability of starting ART after a positive HIV test + """returns the probability of starting ART after a positive HIV test this value for initiation can be higher than the current reported coverage levels to account for defaulters """ @@ -1294,15 +1405,18 @@ def prob_art_start_after_test(self, year): current_year = year if year <= 2025 else 2025 # use iloc to index by position as index will change by year - return_prob = prob_art.loc[ - (prob_art.year == current_year) & - (prob_art.age == "adults"), - "prob_art_if_dx"].values[0] * self.parameters["treatment_initiation_adjustment"] + return_prob = ( + prob_art.loc[ + (prob_art.year == current_year) & (prob_art.age == "adults"), + "prob_art_if_dx", + ].values[0] + * self.parameters["treatment_initiation_adjustment"] + ) return return_prob def prob_viral_suppression(self, year, age_of_person): - """ returns the probability of viral suppression once on ART + """returns the probability of viral suppression once on ART data from 2012 - 2020 from spectrum assume constant values 2010-2012 and 2020 on time-series ends at 2025 @@ -1313,9 +1427,9 @@ def prob_viral_suppression(self, year, age_of_person): age_group = "adults" if age_of_person >= 15 else "children" return_prob = prob_vs.loc[ - (prob_vs.year == current_year) & - (prob_vs.age == age_group), - "virally_suppressed_on_art"].values[0] + (prob_vs.year == current_year) & (prob_vs.age == age_group), + "virally_suppressed_on_art", + ].values[0] # convert to probability and adjust for defaulters return_prob = (return_prob / 100) * self.parameters["vs_adjustment"] @@ -1326,7 +1440,8 @@ def prob_viral_suppression(self, year, age_of_person): def stops_treatment(self, person_id): """Helper function that is called when someone stops being on ART. - Sets the flag for ART status. If the person was already on ART, it schedules a new AIDSEvent""" + Sets the flag for ART status. If the person was already on ART, it schedules a new AIDSEvent + """ df = self.sim.population.props @@ -1341,12 +1456,15 @@ def stops_treatment(self, person_id): ) ) self.sim.schedule_event( - event=HivAidsOnsetEvent(person_id=person_id, module=self, cause='AIDS_non_TB'), + event=HivAidsOnsetEvent( + person_id=person_id, module=self, cause="AIDS_non_TB" + ), date=self.sim.date + pd.DateOffset(months=months_to_aids), ) - # Set that the person is no longer on ART + # Set that the person is no longer on ART or cotrimoxazole df.at[person_id, "hv_art"] = "not" + df.at[person_id, "hv_on_cotrimoxazole"] = False def per_capita_testing_rate(self): """This calculates the numbers of hiv tests performed in each time period. @@ -1391,17 +1509,19 @@ def decide_whether_hiv_test_for_mother(self, person_id, referred_from) -> bool: """ df = self.sim.population.props - if not df.at[person_id, 'hv_diagnosed'] and ( - self.rng.random_sample() < self.parameters['prob_hiv_test_at_anc_or_delivery']): + if not df.at[person_id, "hv_diagnosed"] and ( + self.rng.random_sample() + < self.parameters["prob_hiv_test_at_anc_or_delivery"] + ): - self.sim.modules['HealthSystem'].schedule_hsi_event( + self.sim.modules["HealthSystem"].schedule_hsi_event( HSI_Hiv_TestAndRefer( - person_id=person_id, - module=self, - referred_from=referred_from), + person_id=person_id, module=self, referred_from=referred_from + ), topen=self.sim.date, tclose=None, - priority=0) + priority=0, + ) return True @@ -1409,7 +1529,7 @@ def decide_whether_hiv_test_for_mother(self, person_id, referred_from) -> bool: return False def decide_whether_hiv_test_for_infant(self, mother_id, child_id) -> None: - """ This will schedule an HIV testing HSI for a child under certain conditions. + """This will schedule an HIV testing HSI for a child under certain conditions. It is called from newborn_outcomes.py under hiv_screening_for_at_risk_newborns. """ @@ -1417,19 +1537,23 @@ def decide_whether_hiv_test_for_infant(self, mother_id, child_id) -> None: mother_id = mother_id child_id = child_id - if not df.at[child_id, 'hv_diagnosed'] and \ - df.at[mother_id, 'hv_diagnosed'] and ( - df.at[child_id, 'nb_pnc_check'] == 1) and ( - self.rng.random_sample() < self.parameters['prob_hiv_test_for_newborn_infant']): + if ( + not df.at[child_id, "hv_diagnosed"] + and df.at[mother_id, "hv_diagnosed"] + and (df.at[child_id, "nb_pnc_check"] == 1) + and ( + self.rng.random_sample() + < self.parameters["prob_hiv_test_for_newborn_infant"] + ) + ): - self.sim.modules['HealthSystem'].schedule_hsi_event( + self.sim.modules["HealthSystem"].schedule_hsi_event( HSI_Hiv_TestAndRefer( - person_id=child_id, - module=self, - referred_from="newborn_outcomes"), + person_id=child_id, module=self, referred_from="newborn_outcomes" + ), topen=self.sim.date + pd.DateOffset(weeks=6), tclose=None, - priority=0 + priority=0, ) def check_config_of_properties(self): @@ -1488,7 +1612,7 @@ def is_subset(col_for_set, col_for_subset): df_alive.is_alive & (df_alive.hv_art == "on_VL_suppressed") & (df_alive.tb_inf == "uninfected") - ].index + ].index ) ) @@ -1499,7 +1623,7 @@ def is_subset(col_for_set, col_for_subset): class HivRegularPollingEvent(RegularEvent, PopulationScopeEventMixin): - """ The HIV Regular Polling Events + """The HIV Regular Polling Events * Schedules persons becoming newly infected through horizontal transmission * Schedules who will present for voluntary ("spontaneous") testing """ @@ -1529,7 +1653,7 @@ def horizontal_transmission(to_sex, from_sex): & df.hv_inf & (df.hv_art != "on_VL_suppressed") & (df.sex == from_sex) - ] + ] ) if n_infectious > 0: @@ -1540,7 +1664,7 @@ def horizontal_transmission(to_sex, from_sex): & ~df.hv_inf & df.age_years.between(15, 80) & (df.sex == to_sex) - ].index + ].index n_susceptible = len(susc_idx) # Compute chance that each susceptible person becomes infected: @@ -1551,7 +1675,9 @@ def horizontal_transmission(to_sex, from_sex): # - probability of infection = beta * I/N p_infection = ( - rr_of_infection * beta * (n_infectious / (n_infectious + n_susceptible)) + rr_of_infection + * beta + * (n_infectious / (n_infectious + n_susceptible)) ) # New infections: @@ -1570,24 +1696,31 @@ def horizontal_transmission(to_sex, from_sex): & df.li_is_sexworker & ~df.hv_is_on_prep & df.age_years.between(15, 80) - ].index + ].index # - probability of infection - relative risk applies only to fsw p_infection_fsw = ( - self.module.parameters["rr_fsw"] * beta * (n_infectious / (n_infectious + n_susceptible)) + self.module.parameters["rr_fsw"] + * beta + * (n_infectious / (n_infectious + n_susceptible)) ) fsw_infected = ( - self.module.rng.random_sample(len(fsw_at_risk)) < p_infection_fsw + self.module.rng.random_sample(len(fsw_at_risk)) + < p_infection_fsw ) idx_new_infection_fsw = fsw_at_risk[fsw_infected] - idx_new_infection = list(idx_new_infection) + list(idx_new_infection_fsw) + idx_new_infection = list(idx_new_infection) + list( + idx_new_infection_fsw + ) # Schedule the date of infection for each new infection: for idx in idx_new_infection: date_of_infection = self.sim.date + pd.DateOffset( - days=self.module.rng.randint(0, 365 * fraction_of_year_between_polls) + days=self.module.rng.randint( + 0, 365 * fraction_of_year_between_polls + ) ) self.sim.schedule_event( HivInfectionEvent(self.module, idx), date_of_infection @@ -1599,31 +1732,47 @@ def spontaneous_testing(current_year): # extract annual testing rates from MoH Reports test_rates = p["hiv_testing_rates"] - testing_rate_adults = test_rates.loc[ - test_rates.year == current_year, "annual_testing_rate_adults" - ].values[0] * p["hiv_testing_rate_adjustment"] + testing_rate_adults = ( + test_rates.loc[ + test_rates.year == current_year, "annual_testing_rate_adults" + ].values[0] + * p["hiv_testing_rate_adjustment"] + ) # adult testing trends also informed by demographic characteristics # relative probability of testing - this may skew testing rates higher or lower than moh reports - rr_of_test = self.module.lm["lm_spontaneous_test_12m"].predict(df[df.is_alive & (df.age_years >= 15)]) + rr_of_test = self.module.lm["lm_spontaneous_test_12m"].predict( + df[df.is_alive & (df.age_years >= 15)] + ) mean_prob_test = (rr_of_test * testing_rate_adults).mean() scaled_prob_test = (rr_of_test * testing_rate_adults) / mean_prob_test overall_prob_test = scaled_prob_test * testing_rate_adults - random_draw = rng.random_sample(size=len(df[df.is_alive & (df.age_years >= 15)])) - adult_tests_idx = df.loc[df.is_alive & (df.age_years >= 15) & (random_draw < overall_prob_test)].index + random_draw = rng.random_sample( + size=len(df[df.is_alive & (df.age_years >= 15)]) + ) + adult_tests_idx = df.loc[ + df.is_alive & (df.age_years >= 15) & (random_draw < overall_prob_test) + ].index idx_will_test = adult_tests_idx for person_id in idx_will_test: date_test = self.sim.date + pd.DateOffset( - days=self.module.rng.randint(0, 365 * fraction_of_year_between_polls) + days=self.module.rng.randint( + 0, 365 * fraction_of_year_between_polls + ) ) self.sim.modules["HealthSystem"].schedule_hsi_event( - hsi_event=HSI_Hiv_TestAndRefer(person_id=person_id, module=self.module, referred_from='HIV_poll'), + hsi_event=HSI_Hiv_TestAndRefer( + person_id=person_id, + module=self.module, + referred_from="HIV_poll", + ), priority=1, topen=date_test, - tclose=date_test + pd.DateOffset( + tclose=date_test + + pd.DateOffset( months=self.frequency.months ), # (to occur before next polling) ) @@ -1638,7 +1787,7 @@ def prep_for_agyw(): & df.age_years.between(15, 30) & (df.sex == "F") & ~df.hv_is_on_prep - ].index + ].index rr_of_infection_in_agyw = self.module.lm["rr_of_infection"].predict( df.loc[agyw_idx] @@ -1650,25 +1799,26 @@ def prep_for_agyw(): overall_risk_and_prob_of_prep = scaled_risk * p["prob_prep_for_agyw"] # give prep - give_prep = df.loc[( - self.module.rng.random_sample(len(overall_risk_and_prob_of_prep)) - < overall_risk_and_prob_of_prep) - & df.is_alive - & ~df.hv_diagnosed - & df.age_years.between(15, 30) - & (df.sex == "F") - & ~df.hv_is_on_prep - ].index + give_prep = df.loc[ + ( + self.module.rng.random_sample(len(overall_risk_and_prob_of_prep)) + < overall_risk_and_prob_of_prep + ) + & df.is_alive + & ~df.hv_diagnosed + & df.age_years.between(15, 30) + & (df.sex == "F") + & ~df.hv_is_on_prep + ].index for person in give_prep: self.sim.modules["HealthSystem"].schedule_hsi_event( - hsi_event=HSI_Hiv_StartOrContinueOnPrep(person_id=person, - module=self.module), + hsi_event=HSI_Hiv_StartOrContinueOnPrep( + person_id=person, module=self.module + ), priority=1, topen=self.sim.date, - tclose=self.sim.date + pd.DateOffset( - months=self.frequency.months - ) + tclose=self.sim.date + pd.DateOffset(months=self.frequency.months), ) # ----------------------------------- SPONTANEOUS VMMC FOR <15 YRS ----------------------------------- @@ -1721,8 +1871,9 @@ def vmmc_for_child(): # Natural History Events # --------------------------------------------------------------------------- + class HivInfectionEvent(Event, IndividualScopeEventMixin): - """ This person will become infected. + """This person will become infected. * Do the infection process * Check for onward transmission through MTCT if the infection is to a mother who is currently breastfeeding. """ @@ -1743,14 +1894,14 @@ def apply(self, person_id): # Consider mother-to-child-transmission (MTCT) from this person to their children: children_of_this_person_being_breastfed = df.loc[ (df.mother_id == person_id) & (df.nb_breastfeeding_status != "none") - ].index + ].index # - Do the MTCT routine for each child: for child_id in children_of_this_person_being_breastfed: self.module.mtct_during_breastfeeding(person_id, child_id) class HivInfectionDuringBreastFeedingEvent(Event, IndividualScopeEventMixin): - """ This person will become infected during breastfeeding + """This person will become infected during breastfeeding * Do the infection process """ @@ -1778,7 +1929,7 @@ def apply(self, person_id): class HivAidsOnsetEvent(Event, IndividualScopeEventMixin): - """ This person has developed AIDS. + """This person has developed AIDS. * Update their symptomatic status * Record the date at which AIDS onset * Schedule the AIDS death @@ -1804,25 +1955,35 @@ def apply(self, person_id): # Do nothing if person is now on ART and VL suppressed (non-VL suppressed has no effect) # if cause is TB, allow AIDS onset - if (df.at[person_id, "hv_art"] == "on_VL_suppressed") and (self.cause != 'AIDS_TB'): + if (df.at[person_id, "hv_art"] == "on_VL_suppressed") and ( + self.cause != "AIDS_TB" + ): return # need to delay onset of AIDS (non-tb) to compensate for AIDS-TB if (self.cause == "AIDS_non_TB") and ( - self.sim.modules["Hiv"].rng.rand() < self.sim.modules["Hiv"].parameters["prop_delayed_aids_onset"]): + self.sim.modules["Hiv"].rng.rand() + < self.sim.modules["Hiv"].parameters["prop_delayed_aids_onset"] + ): # redraw time to aids and reschedule months_to_aids = int( np.floor( self.sim.modules["Hiv"].rng.exponential( - scale=self.sim.modules["Hiv"].parameters["art_default_to_aids_mean_years"] + scale=self.sim.modules["Hiv"].parameters[ + "art_default_to_aids_mean_years" + ] ) * 12.0 ) ) self.sim.schedule_event( - event=HivAidsOnsetEvent(person_id=person_id, module=self.sim.modules["Hiv"], cause='AIDS_non_TB'), + event=HivAidsOnsetEvent( + person_id=person_id, + module=self.sim.modules["Hiv"], + cause="AIDS_non_TB", + ), date=self.sim.date + pd.DateOffset(months=months_to_aids), ) @@ -1839,26 +2000,66 @@ def apply(self, person_id): ) # Schedule AidsDeath - date_of_aids_death = self.sim.date + self.sim.modules["Hiv"].get_time_from_aids_to_death() + date_of_aids_death = ( + self.sim.date + self.sim.modules["Hiv"].get_time_from_aids_to_death() + ) if self.cause == "AIDS_non_TB": # cause is HIV self.sim.schedule_event( event=HivAidsDeathEvent( - person_id=person_id, module=self.sim.modules["Hiv"], cause=self.cause + person_id=person_id, + module=self.sim.modules["Hiv"], + cause=self.cause, ), date=date_of_aids_death, ) + # schedule hospital stay + beddays = self.sim.modules["Hiv"].rng.randint(low=14, high=20) + date_admission = date_of_aids_death - pd.DateOffset(days=beddays) + self.sim.modules["HealthSystem"].schedule_hsi_event( + hsi_event=HSI_Hiv_EndOfLifeCare( + person_id=person_id, + module=self.sim.modules["Hiv"], + beddays=beddays, + ), + priority=0, + topen=( + date_admission + if (date_admission > self.sim.date) + else self.sim.date + ), + tclose=date_of_aids_death, + ) else: # cause is active TB self.sim.schedule_event( event=HivAidsTbDeathEvent( - person_id=person_id, module=self.sim.modules["Hiv"], cause=self.cause + person_id=person_id, + module=self.sim.modules["Hiv"], + cause=self.cause, ), date=date_of_aids_death, ) + # schedule hospital stay + beddays = self.sim.modules["Hiv"].rng.randint(low=14, high=20) + date_admission = date_of_aids_death - pd.DateOffset(days=beddays) + self.sim.modules["HealthSystem"].schedule_hsi_event( + hsi_event=HSI_Hiv_EndOfLifeCare( + person_id=person_id, + module=self.sim.modules["Hiv"], + beddays=beddays, + ), + priority=0, + topen=( + date_admission + if (date_admission >= self.sim.date) + else self.sim.date + ), + tclose=date_of_aids_death, + ) class HivAidsDeathEvent(Event, IndividualScopeEventMixin): @@ -1885,12 +2086,14 @@ def apply(self, person_id): # Do nothing if person is now on ART and VL suppressed (non VL suppressed has no effect) # only if no current TB infection if (df.at[person_id, "hv_art"] == "on_VL_suppressed") and ( - df.at[person_id, "tb_inf"] != "active"): + df.at[person_id, "tb_inf"] != "active" + ): return # off ART, no TB infection if (df.at[person_id, "hv_art"] != "on_VL_suppressed") and ( - df.at[person_id, "tb_inf"] != "active"): + df.at[person_id, "tb_inf"] != "active" + ): # cause is HIV (no TB) self.sim.modules["Demography"].do_death( individual_id=person_id, @@ -1930,11 +2133,14 @@ def apply(self, person_id): if not df.at[person_id, "is_alive"]: return - if df.at[person_id, 'tb_on_treatment']: + if df.at[person_id, "tb_on_treatment"]: prob = self.module.rng.rand() # treatment adjustment reduces probability of death - if prob < self.sim.modules["Hiv"].parameters["aids_tb_treatment_adjustment"]: + if ( + prob + < self.sim.modules["Hiv"].parameters["aids_tb_treatment_adjustment"] + ): self.sim.modules["Demography"].do_death( individual_id=person_id, cause="AIDS_TB", @@ -1943,19 +2149,39 @@ def apply(self, person_id): else: # if they survive, reschedule the aids death event # module calling rescheduled AIDS death should be Hiv (not TB) - date_of_aids_death = self.sim.date + self.sim.modules["Hiv"].get_time_from_aids_to_death() + date_of_aids_death = ( + self.sim.date + + self.sim.modules["Hiv"].get_time_from_aids_to_death() + ) self.sim.schedule_event( event=HivAidsDeathEvent( person_id=person_id, module=self.sim.modules["Hiv"], - cause="AIDS_non_TB" + cause="AIDS_non_TB", ), date=date_of_aids_death, ) + # schedule hospital stay + beddays = self.module.rng.randint(low=14, high=20) + date_admission = date_of_aids_death - pd.DateOffset(days=beddays) + self.sim.modules["HealthSystem"].schedule_hsi_event( + hsi_event=HSI_Hiv_EndOfLifeCare( + person_id=person_id, + module=self.sim.modules["Hiv"], + beddays=beddays, + ), + priority=0, + topen=( + date_admission + if (date_admission >= self.sim.date) + else self.sim.date + ), + tclose=date_of_aids_death, + ) # aids-tb and not on tb treatment - elif not df.at[person_id, 'tb_on_treatment']: + elif not df.at[person_id, "tb_on_treatment"]: # Cause the death to happen immediately, cause defined by TB status self.sim.modules["Demography"].do_death( individual_id=person_id, cause="AIDS_TB", originating_module=self.module @@ -1976,17 +2202,15 @@ def apply(self, person_id): m = self.module # If the person is no longer alive or has been diagnosed with hiv, they will not continue on PrEP - if ( - (not person["is_alive"]) - or (person["hv_diagnosed"]) - ): + if (not person["is_alive"]) or (person["hv_diagnosed"]): return # Check that there are on PrEP currently: if not person["hv_is_on_prep"]: logger.warning( key="message", - data="This event should not be running: Hiv_DecisionToContinueOnPrEP is for those currently on prep") + data="This event should not be running: Hiv_DecisionToContinueOnPrEP is for those currently on prep", + ) # check still eligible, person must be <30 years old or a fsw if (person["age_years"] > 30) or not person["li_is_sexworker"]: @@ -2030,7 +2254,8 @@ def apply(self, person_id): if person["hv_art"] not in ["on_VL_suppressed", "on_not_VL_suppressed"]: logger.warning( key="message", - data="This event should not be running, Hiv_DecisionToContinueTreatment is for those already on tx") + data="This event should not be running, Hiv_DecisionToContinueTreatment is for those already on tx", + ) # Determine if this appointment is actually attended by the person who has already started on ART if ( @@ -2039,8 +2264,9 @@ def apply(self, person_id): ): # Continue on Treatment - and schedule an HSI for a continuation appointment today self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Hiv_StartOrContinueTreatment(person_id=person_id, module=m, - facility_level_of_this_hsi="1a"), + HSI_Hiv_StartOrContinueTreatment( + person_id=person_id, module=m, facility_level_of_this_hsi="1a" + ), topen=self.sim.date, tclose=self.sim.date + pd.DateOffset(days=14), priority=0, @@ -2052,8 +2278,9 @@ def apply(self, person_id): # refer for another treatment again in 1 month self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Hiv_StartOrContinueTreatment(person_id=person_id, module=m, - facility_level_of_this_hsi="1a"), + HSI_Hiv_StartOrContinueTreatment( + person_id=person_id, module=m, facility_level_of_this_hsi="1a" + ), topen=self.sim.date + pd.DateOffset(months=1), tclose=None, priority=0, @@ -2064,6 +2291,7 @@ def apply(self, person_id): # Health System Interactions (HSI) # --------------------------------------------------------------------------- + class HSI_Hiv_TestAndRefer(HSI_Event, IndividualScopeEventMixin): """ The is the Test-and-Refer HSI. Individuals may seek an HIV test at any time. From this, they can be referred on to @@ -2081,7 +2309,12 @@ class HSI_Hiv_TestAndRefer(HSI_Event, IndividualScopeEventMixin): """ def __init__( - self, module, person_id, do_not_refer_if_neg=False, suppress_footprint=False, referred_from=None, + self, + module, + person_id, + do_not_refer_if_neg=False, + suppress_footprint=False, + referred_from=None, ): super().__init__(module, person_id=person_id) assert isinstance(module, Hiv) @@ -2097,7 +2330,8 @@ def __init__( # Define the necessary information for an HSI self.TREATMENT_ID = "Hiv_Test" self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"VCTNegative": 1}) - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.ACCEPTED_FACILITY_LEVEL = "1a" + self.counter_for_test_not_available = 0 def apply(self, person_id, squeeze_factor): """Do the testing and referring to other services""" @@ -2134,13 +2368,13 @@ def apply(self, person_id, squeeze_factor): # Log the test: line-list of summary information about each test person_details_for_test = { - 'age': person['age_years'], - 'hiv_status': person['hv_inf'], - 'hiv_diagnosed': person['hv_diagnosed'], - 'referred_from': self.referred_from, - 'person_id': person_id + "age": person["age_years"], + "hiv_status": person["hv_inf"], + "hiv_diagnosed": person["hv_diagnosed"], + "referred_from": self.referred_from, + "person_id": person_id, } - logger.info(key='hiv_test', data=person_details_for_test) + logger.info(key="hiv_test", data=person_details_for_test) # Offer services as needed: if test_result: @@ -2154,7 +2388,11 @@ def apply(self, person_id, squeeze_factor): # Screen for tb if they have not been referred from a Tb HSI # and do not currently have TB diagnosis - if "Tb" in self.sim.modules and (self.referred_from != 'Tb') and not person["tb_diagnosed"]: + if ( + "Tb" in self.sim.modules + and (self.referred_from != "Tb") + and not person["tb_diagnosed"] + ): self.sim.modules["HealthSystem"].schedule_hsi_event( tb.HSI_Tb_ScreeningAndRefer( person_id=person_id, module=self.sim.modules["Tb"] @@ -2196,10 +2434,14 @@ def apply(self, person_id, squeeze_factor): (person["sex"] == "F") & person["li_is_sexworker"] & ~person["hv_is_on_prep"] - & (self.sim.date.year >= self.module.parameters["prep_start_year"]) + & ( + self.sim.date.year + >= self.module.parameters["prep_start_year"] + ) ): - if self.module.lm["lm_prep"].predict(df.loc[[person_id]], self.module.rng - ): + if self.module.lm["lm_prep"].predict( + df.loc[[person_id]], self.module.rng + ): self.sim.modules["HealthSystem"].schedule_hsi_event( HSI_Hiv_StartOrContinueOnPrep( person_id=person_id, module=self.module @@ -2212,14 +2454,23 @@ def apply(self, person_id, squeeze_factor): # Test was not possible, set blank footprint and schedule another test ACTUAL_APPT_FOOTPRINT = self.make_appt_footprint({"VCTNegative": 1}) - # repeat appt for HIV test - self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Hiv_TestAndRefer(person_id=person_id, module=self.module, referred_from='HIV_test'), - topen=self.sim.date + pd.DateOffset(months=1), - tclose=None, - priority=0, + # set cap for number of repeat tests + self.counter_for_test_not_available += ( + 1 # The current appointment is included in the count. ) + if ( + self.counter_for_test_not_available + <= self.module.parameters["hiv_healthseekingbehaviour_cap"] + ): + # repeat appt for HIV test + self.sim.modules["HealthSystem"].schedule_hsi_event( + self, + topen=self.sim.date + pd.DateOffset(days=7), + tclose=None, + priority=0, + ) + # Return the footprint. If it should be suppressed, return a blank footprint. if self.suppress_footprint: return self.make_appt_footprint({}) @@ -2233,12 +2484,14 @@ def __init__(self, module, person_id): self.TREATMENT_ID = "Hiv_Prevention_Circumcision" self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"MaleCirc": 1}) - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.ACCEPTED_FACILITY_LEVEL = "1a" self.number_of_occurrences = 0 def apply(self, person_id, squeeze_factor): - """ Do the circumcision for this man. If he is already circumcised, this is a follow-up appointment.""" - self.number_of_occurrences += 1 # The current appointment is included in the count. + """Do the circumcision for this man. If he is already circumcised, this is a follow-up appointment.""" + self.number_of_occurrences += ( + 1 # The current appointment is included in the count. + ) df = self.sim.population.props # shortcut to the dataframe person = df.loc[person_id] @@ -2251,7 +2504,9 @@ def apply(self, person_id, squeeze_factor): if not person["li_is_circ"]: # Check/log use of consumables, if materials available, do circumcision and schedule follow-up appts # If materials not available, repeat the HSI, i.e., first appt. - if self.get_consumables(item_codes=self.module.item_codes_for_consumables_required['circ']): + if self.get_consumables( + item_codes=self.module.item_codes_for_consumables_required["circ"] + ): # Update circumcision state df.at[person_id, "li_is_circ"] = True @@ -2272,7 +2527,10 @@ def apply(self, person_id, squeeze_factor): ) else: # schedule repeating appt when consumables not available - if self.number_of_occurrences <= 3: + if ( + self.number_of_occurrences + <= self.module.parameters["hiv_healthseekingbehaviour_cap"] + ): self.sim.modules["HealthSystem"].schedule_hsi_event( self, topen=self.sim.date + DateOffset(weeks=1), @@ -2287,8 +2545,10 @@ def __init__(self, module, person_id, referred_from, repeat_visits): assert isinstance(module, Hiv) self.TREATMENT_ID = "Hiv_Prevention_Infant" - self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"Peds": 1, "VCTNegative": 1}) - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint( + {"Peds": 1, "VCTNegative": 1} + ) + self.ACCEPTED_FACILITY_LEVEL = "1a" self.referred_from = referred_from self.repeat_visits = repeat_visits @@ -2306,12 +2566,15 @@ def apply(self, person_id, squeeze_factor): return self.sim.modules["HealthSystem"].get_blank_appt_footprint() # if breastfeeding has ceased or child >18 months, no further prophylaxis required - if (df.at[person_id, "nb_breastfeeding_status"] == "none")\ - or (df.at[person_id, "age_years"] >= 1.5): + if (df.at[person_id, "nb_breastfeeding_status"] == "none") or ( + df.at[person_id, "age_years"] >= 1.5 + ): return self.sim.modules["HealthSystem"].get_blank_appt_footprint() # Check that infant prophylaxis is available and if it is, initiate: - if self.get_consumables(item_codes=self.module.item_codes_for_consumables_required['infant_prep']): + if self.get_consumables( + item_codes=self.module.item_codes_for_consumables_required["infant_prep"] + ): df.at[person_id, "hv_is_on_prep"] = True # Schedule follow-up visit for 3 months time @@ -2320,17 +2583,21 @@ def apply(self, person_id, squeeze_factor): person_id=person_id, module=self.module, referred_from="repeat3months", - repeat_visits=0), + repeat_visits=0, + ), priority=1, topen=self.sim.date + DateOffset(months=3), tclose=None, ) else: - if self.repeat_visits <= 4: - # infant does not get NVP now but has repeat visit scheduled up to 5 times - df.at[person_id, "hv_is_on_prep"] = False + # infant does not get NVP now but has repeat visit scheduled up to 5 times + df.at[person_id, "hv_is_on_prep"] = False + if ( + self.repeat_visits + <= self.module.parameters["hiv_healthseekingbehaviour_cap"] + ): self.repeat_visits += 1 # Schedule repeat visit for one week's time @@ -2339,7 +2606,8 @@ def apply(self, person_id, squeeze_factor): person_id=person_id, module=self.module, referred_from="repeatNoCons", - repeat_visits=self.repeat_visits), + repeat_visits=self.repeat_visits, + ), priority=1, topen=self.sim.date + pd.DateOffset(days=7), tclose=None, @@ -2357,8 +2625,11 @@ def __init__(self, module, person_id): assert isinstance(module, Hiv) self.TREATMENT_ID = "Hiv_Prevention_Prep" - self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"PharmDispensing": 1, "VCTNegative": 1}) - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint( + {"PharmDispensing": 1, "VCTNegative": 1} + ) + self.ACCEPTED_FACILITY_LEVEL = "1a" + self.counter_for_drugs_not_available = 0 def apply(self, person_id, squeeze_factor): """Start PrEP for this person; or continue them on PrEP for 3 more months""" @@ -2367,10 +2638,7 @@ def apply(self, person_id, squeeze_factor): person = df.loc[person_id] # Do not run if the person is not alive or is diagnosed with hiv - if ( - (not person["is_alive"]) - or (person["hv_diagnosed"]) - ): + if (not person["is_alive"]) or (person["hv_diagnosed"]): return # Run an HIV test @@ -2391,7 +2659,9 @@ def apply(self, person_id, squeeze_factor): return self.make_appt_footprint({"Over5OPD": 1, "VCTPositive": 1}) # Check that PrEP is available and if it is, initiate or continue PrEP: - if self.get_consumables(item_codes=self.module.item_codes_for_consumables_required['prep']): + if self.get_consumables( + item_codes=self.module.item_codes_for_consumables_required["prep"] + ): df.at[person_id, "hv_is_on_prep"] = True # Schedule 'decision about whether to continue on PrEP' for 3 months time @@ -2404,6 +2674,23 @@ def apply(self, person_id, squeeze_factor): # If PrEP is not available, the person will default and not be on PrEP df.at[person_id, "hv_is_on_prep"] = False + self.counter_for_drugs_not_available += ( + 1 # The current appointment is included in the count. + ) + + if ( + self.counter_for_drugs_not_available + <= self.module.parameters["hiv_healthseekingbehaviour_cap"] + ): + + # Schedule repeat visit for one week's time + self.sim.modules["HealthSystem"].schedule_hsi_event( + self, + priority=1, + topen=self.sim.date + pd.DateOffset(days=7), + tclose=None, + ) + def never_ran(self): """This is called if this HSI was never run. Default the person to being off PrEP""" @@ -2446,7 +2733,8 @@ def apply(self, person_id, squeeze_factor): # Try to continue the person on ART: drugs_were_available = self.do_at_continuation(person_id) - if drugs_were_available: + # if ART is available (1st item in drugs_were_available dict) + if list(drugs_were_available.values())[0]: # If person has been placed/continued on ART, schedule 'decision about whether to continue on Treatment self.sim.schedule_event( Hiv_DecisionToContinueTreatment( @@ -2457,25 +2745,29 @@ def apply(self, person_id, squeeze_factor): else: # logger for drugs not available person_details_for_tx = { - 'age': person['age_years'], - 'facility_level': self.ACCEPTED_FACILITY_LEVEL, - 'number_appts': self.counter_for_drugs_not_available, - 'district': person['district_of_residence'], - 'person_id': person_id, - 'drugs_available': drugs_were_available, + "age": person["age_years"], + "facility_level": self.ACCEPTED_FACILITY_LEVEL, + "number_appts": self.counter_for_drugs_not_available, + "district": person["district_of_residence"], + "person_id": person_id, + "drugs_available": drugs_were_available, } - logger.info(key='hiv_arv_NA', data=person_details_for_tx) + logger.info(key="hiv_arv_NA", data=person_details_for_tx) # As drugs were not available, the person will default to being off ART (...if they were on ART at the # beginning of the HSI.) # NB. If the person was not on ART at the beginning of the HSI, then there is no need to stop them (which # causes a new AIDSOnsetEvent to be scheduled.) - self.counter_for_drugs_not_available += 1 # The current appointment is included in the count. + self.counter_for_drugs_not_available += ( + 1 # The current appointment is included in the count. + ) if art_status_at_beginning_of_hsi != "not": self.module.stops_treatment(person_id) - p = self.module.parameters["probability_of_seeking_further_art_appointment_if_drug_not_available"] + p = self.module.parameters[ + "probability_of_seeking_further_art_appointment_if_drug_not_available" + ] if self.module.rng.random_sample() >= p: @@ -2483,7 +2775,8 @@ def apply(self, person_id, squeeze_factor): # if defaulting, seek another treatment appointment in 6 months self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_StartOrContinueTreatment( - person_id=person_id, module=self.module, + person_id=person_id, + module=self.module, facility_level_of_this_hsi="1a", ), topen=self.sim.date + pd.DateOffset(months=6), @@ -2499,10 +2792,7 @@ def apply(self, person_id, squeeze_factor): if self.counter_for_drugs_not_available <= 2: # repeat attempt for ARVs at level 1a self.sim.modules["HealthSystem"].schedule_hsi_event( - hsi_event=HSI_Hiv_StartOrContinueTreatment( - person_id=person_id, module=self.module, - facility_level_of_this_hsi="1a" - ), + self, topen=self.sim.date + pd.DateOffset(months=1), priority=0, ) @@ -2511,8 +2801,9 @@ def apply(self, person_id, squeeze_factor): # refer to higher facility level self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_StartOrContinueTreatment( - person_id=person_id, module=self.module, - facility_level_of_this_hsi="2" + person_id=person_id, + module=self.module, + facility_level_of_this_hsi="2", ), topen=self.sim.date + pd.DateOffset(days=1), priority=0, @@ -2534,16 +2825,16 @@ def do_at_initiation(self, person_id): df = self.sim.population.props person = df.loc[person_id] - # Check if drugs are available, and provide drugs: + # Check if drugs are available, and provide drugs + # this will return a dict where the first item is ART and the second is cotrimoxazole drugs_available = self.get_drugs(age_of_person=person["age_years"]) - if drugs_available: - # Assign person to be have suppressed or un-suppressed viral load + # ART is first item in drugs_available dict + if list(drugs_available.values())[0]: + # Assign person to have suppressed or un-suppressed viral load # (If person is VL suppressed This will prevent the Onset of AIDS, or an AIDS death if AIDS has already - # onset,) - vl_status = self.determine_vl_status( - age_of_person=person["age_years"] - ) + # onset) + vl_status = self.determine_vl_status(age_of_person=person["age_years"]) df.at[person_id, "hv_art"] = vl_status df.at[person_id, "hv_date_treated"] = self.sim.date @@ -2554,6 +2845,10 @@ def do_at_initiation(self, person_id): person_id=person_id, disease_module=self.module ) + # if cotrimoxazole is available + if list(drugs_available.values())[1]: + df.at[person_id, "hv_on_cotrimoxazole"] = True + # Consider if TB treatment should start self.consider_tb(person_id) @@ -2565,13 +2860,22 @@ def do_at_continuation(self, person_id): df = self.sim.population.props person = df.loc[person_id] + # default to person stopping cotrimoxazole + df.at[person_id, "hv_on_cotrimoxazole"] = False + # Viral Load Monitoring # NB. This does not have a direct effect on outcomes for the person. - _ = self.get_consumables(item_codes=self.module.item_codes_for_consumables_required['vl_measurement']) + _ = self.get_consumables( + item_codes=self.module.item_codes_for_consumables_required["vl_measurement"] + ) # Check if drugs are available, and provide drugs: drugs_available = self.get_drugs(age_of_person=person["age_years"]) + # if cotrimoxazole is available, update person's property + if list(drugs_available.values())[1]: + df.at[person_id, "hv_on_cotrimoxazole"] = True + return drugs_available def determine_vl_status(self, age_of_person): @@ -2587,31 +2891,46 @@ def determine_vl_status(self, age_of_person): ) def get_drugs(self, age_of_person): - """Helper function to get the ART according to the age of the person being treated. Returns bool to indicate - whether drugs were available""" + """Helper function to get the ART according to the age of the person being treated. Returns dict to indicate + whether individual drugs were available""" p = self.module.parameters if age_of_person < p["ART_age_cutoff_young_child"]: # Formulation for young children drugs_available = self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required['First line ART regimen: young child'], + item_codes=self.module.item_codes_for_consumables_required[ + "First line ART regimen: young child" + ], optional_item_codes=self.module.item_codes_for_consumables_required[ - 'First line ART regimen: young child: cotrimoxazole']) + "First line ART regimen: young child: cotrimoxazole" + ], + return_individual_results=True, + ) elif age_of_person <= p["ART_age_cutoff_older_child"]: # Formulation for older children drugs_available = self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required['First line ART regimen: older child'], + item_codes=self.module.item_codes_for_consumables_required[ + "First line ART regimen: older child" + ], optional_item_codes=self.module.item_codes_for_consumables_required[ - 'First line ART regimen: older child: cotrimoxazole']) + "First line ART regimen: older child: cotrimoxazole" + ], + return_individual_results=True, + ) else: # Formulation for adults drugs_available = self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required['First-line ART regimen: adult'], + item_codes=self.module.item_codes_for_consumables_required[ + "First-line ART regimen: adult" + ], optional_item_codes=self.module.item_codes_for_consumables_required[ - 'First-line ART regimen: adult: cotrimoxazole']) + "First-line ART regimen: adult: cotrimoxazole" + ], + return_individual_results=True, + ) return drugs_available @@ -2636,13 +2955,18 @@ def never_ran(self): self.module.stops_treatment(person_id) # sample whether person will seek further appt - if self.module.rng.random_sample() < self.module.parameters[ - "probability_of_seeking_further_art_appointment_if_appointment_not_available" - ]: + if ( + self.module.rng.random_sample() + < self.module.parameters[ + "probability_of_seeking_further_art_appointment_if_appointment_not_available" + ] + ): # schedule HSI self.sim.modules["HealthSystem"].schedule_hsi_event( HSI_Hiv_StartOrContinueTreatment( - person_id=person_id, module=self.module, facility_level_of_this_hsi="1a" + person_id=person_id, + module=self.module, + facility_level_of_this_hsi="1a", ), topen=self.sim.date + pd.DateOffset(days=14), tclose=self.sim.date + pd.DateOffset(days=21), @@ -2652,22 +2976,66 @@ def never_ran(self): @property def EXPECTED_APPT_FOOTPRINT(self): """Returns the appointment footprint for this person according to their current status: - * `NewAdult` for an adult, newly starting treatment - * `EstNonCom` for an adult, re-starting treatment or already on treatment - (NB. This is an appointment type that assumes that the patient does not have complications.) - * `Peds` for a child - whether newly starting or already on treatment + * `NewAdult` for an adult, newly starting treatment + * `EstNonCom` for an adult, re-starting treatment or already on treatment + (NB. This is an appointment type that assumes that the patient does not have complications.) + * `Peds` for a child - whether newly starting or already on treatment """ person_id = self.target - if self.sim.population.props.at[person_id, 'age_years'] < 15: + if self.sim.population.props.at[person_id, "age_years"] < 15: return self.make_appt_footprint({"Peds": 1}) # Child - if (self.sim.population.props.at[person_id, 'hv_art'] == "not") & ( - pd.isna(self.sim.population.props.at[person_id, 'hv_date_treated']) + if (self.sim.population.props.at[person_id, "hv_art"] == "not") & ( + pd.isna(self.sim.population.props.at[person_id, "hv_date_treated"]) ): - return self.make_appt_footprint({"NewAdult": 1}) # Adult newly starting treatment + return self.make_appt_footprint( + {"NewAdult": 1} + ) # Adult newly starting treatment else: - return self.make_appt_footprint({"EstNonCom": 1}) # Adult already on treatment + return self.make_appt_footprint( + {"EstNonCom": 1} + ) # Adult already on treatment + + +class HSI_Hiv_EndOfLifeCare(HSI_Event, IndividualScopeEventMixin): + """ + this is a hospital stay for terminally-ill patients with AHD + it does not affect disability weight or probability of death + no consumables are logged but health system capacity (HR) is allocated + there are no consequences if hospital bed is not available as person has scheduled death + already within 2 weeks + """ + + def __init__(self, module, person_id, beddays): + super().__init__(module, person_id=person_id) + assert isinstance(module, Hiv) + + self.TREATMENT_ID = "Hiv_PalliativeCare" + self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({}) + self.ACCEPTED_FACILITY_LEVEL = "2" + + self.beddays = beddays + self.BEDDAYS_FOOTPRINT = ( + self.make_beddays_footprint({"general_bed": self.beddays}) + if self.beddays + else self.make_beddays_footprint({"general_bed": 17}) + ) + + def apply(self, person_id, squeeze_factor): + df = self.sim.population.props + hs = self.sim.modules["HealthSystem"] + + if not df.at[person_id, "is_alive"]: + return hs.get_blank_appt_footprint() + + if df.at[person_id, "hv_art"] == "virally_suppressed": + return hs.get_blank_appt_footprint() + + logger.debug( + key="message", + data=f"HSI_Hiv_EndOfLifeCare: inpatient admission for {person_id}", + ) # --------------------------------------------------------------------------- @@ -2677,8 +3045,7 @@ def EXPECTED_APPT_FOOTPRINT(self): class HivLoggingEvent(RegularEvent, PopulationScopeEventMixin): def __init__(self, module): - """ Log Current status of the population, every year - """ + """Log Current status of the population, every year""" self.repeat = 12 super().__init__(module, frequency=DateOffset(months=self.repeat)) @@ -2690,9 +3057,7 @@ def apply(self, population): # ------------------------------------ SUMMARIES ------------------------------------ # population - pop_male_15plus = len( - df[df.is_alive & (df.age_years >= 15) & (df.sex == "M")] - ) + pop_male_15plus = len(df[df.is_alive & (df.age_years >= 15) & (df.sex == "M")]) pop_female_15plus = len( df[df.is_alive & (df.age_years >= 15) & (df.sex == "F")] ) @@ -2708,20 +3073,27 @@ def apply(self, population): total_plhiv = len(df[df.hv_inf & df.is_alive]) # adult prevalence - adult_prev_15plus = len( - df[df.hv_inf & df.is_alive & (df.age_years >= 15)] - ) / len(df[df.is_alive & (df.age_years >= 15)]) if len(df[df.is_alive & (df.age_years >= 15)]) else 0 + adult_prev_15plus = ( + len(df[df.hv_inf & df.is_alive & (df.age_years >= 15)]) + / len(df[df.is_alive & (df.age_years >= 15)]) + if len(df[df.is_alive & (df.age_years >= 15)]) + else 0 + ) - adult_prev_1549 = len( - df[df.hv_inf & df.is_alive & df.age_years.between(15, 49)] - ) / len(df[df.is_alive & df.age_years.between(15, 49)]) if len( - df[df.is_alive & df.age_years.between(15, 49)]) else 0 + adult_prev_1549 = ( + len(df[df.hv_inf & df.is_alive & df.age_years.between(15, 49)]) + / len(df[df.is_alive & df.age_years.between(15, 49)]) + if len(df[df.is_alive & df.age_years.between(15, 49)]) + else 0 + ) # child prevalence - child_prev = len( - df[df.hv_inf & df.is_alive & (df.age_years < 15)] - ) / len(df[df.is_alive & (df.age_years < 15)] - ) if len(df[df.is_alive & (df.age_years < 15)]) else 0 + child_prev = ( + len(df[df.hv_inf & df.is_alive & (df.age_years < 15)]) + / len(df[df.is_alive & (df.age_years < 15)]) + if len(df[df.is_alive & (df.age_years < 15)]) + else 0 + ) # incidence in the period since the last log for 15+ and 15-49 year-olds (denominator is approximate) n_new_infections_adult_15plus = len( @@ -2729,20 +3101,26 @@ def apply(self, population): (df.age_years >= 15) & df.is_alive & (df.hv_date_inf >= (now - DateOffset(months=self.repeat))) - ] + ] ) denom_adults_15plus = len(df[df.is_alive & (df.age_years >= 15)]) - adult_inc_15plus = n_new_infections_adult_15plus / denom_adults_15plus if denom_adults_15plus else 0 + adult_inc_15plus = ( + n_new_infections_adult_15plus / denom_adults_15plus + if denom_adults_15plus + else 0 + ) n_new_infections_adult_1549 = len( df.loc[ df.age_years.between(15, 49) & df.is_alive & (df.hv_date_inf >= (now - DateOffset(months=self.repeat))) - ] + ] ) denom_adults_1549 = len(df[df.is_alive & df.age_years.between(15, 49)]) - adult_inc_1549 = n_new_infections_adult_1549 / denom_adults_1549 if denom_adults_1549 else 0 + adult_inc_1549 = ( + n_new_infections_adult_1549 / denom_adults_1549 if denom_adults_1549 else 0 + ) # incidence in the period since the last log for 0-14 year-olds (denominator is approximate) n_new_infections_children = len( @@ -2750,7 +3128,7 @@ def apply(self, population): (df.age_years < 15) & df.is_alive & (df.hv_date_inf >= (now - DateOffset(months=self.repeat))) - ] + ] ) denom_children = len(df[df.is_alive & (df.age_years < 15)]) child_inc = n_new_infections_children / denom_children if denom_children else 0 @@ -2762,7 +3140,7 @@ def apply(self, population): & df.li_is_sexworker & (df.sex == "F") & df.age_years.between(15, 49) - ] + ] ) prev_hiv_fsw = ( 0 @@ -2774,8 +3152,9 @@ def apply(self, population): & df.li_is_sexworker & (df.sex == "F") & df.age_years.between(15, 49) - ] - ) / n_fsw + ] + ) + / n_fsw ) total_population = len(df.loc[df.is_alive]) @@ -2783,7 +3162,7 @@ def apply(self, population): logger.info( key="summary_inc_and_prev_for_adults_and_children_and_fsw", description="Summary of HIV among adult (15+ and 15-49) and children (0-14s) and female sex workers" - " (15-49)", + " (15-49)", data={ "pop_male_15plus": pop_male_15plus, "pop_female_15plus": pop_female_15plus, @@ -2826,29 +3205,63 @@ def apply(self, population): description="Prevalence of HIV split by age and sex", ) - male_prev_1524 = len( - df[df.hv_inf & df.is_alive & df.age_years.between(15, 24) & (df.sex == "M")] - ) / len(df[df.is_alive & df.age_years.between(15, 24) & (df.sex == "M")]) if len( - df[df.is_alive & df.age_years.between(15, 24) & (df.sex == "M")]) else 0 + male_prev_1524 = ( + len( + df[ + df.hv_inf + & df.is_alive + & df.age_years.between(15, 24) + & (df.sex == "M") + ] + ) + / len(df[df.is_alive & df.age_years.between(15, 24) & (df.sex == "M")]) + if len(df[df.is_alive & df.age_years.between(15, 24) & (df.sex == "M")]) + else 0 + ) - male_prev_2549 = len( - df[df.hv_inf & df.is_alive & df.age_years.between(25, 49) & (df.sex == "M")] - ) / len(df[df.is_alive & df.age_years.between(25, 49) & (df.sex == "M")]) if len( - df[df.is_alive & df.age_years.between(25, 49) & (df.sex == "M")]) else 0 + male_prev_2549 = ( + len( + df[ + df.hv_inf + & df.is_alive + & df.age_years.between(25, 49) + & (df.sex == "M") + ] + ) + / len(df[df.is_alive & df.age_years.between(25, 49) & (df.sex == "M")]) + if len(df[df.is_alive & df.age_years.between(25, 49) & (df.sex == "M")]) + else 0 + ) - female_prev_1524 = len( - df[df.hv_inf & df.is_alive & df.age_years.between(15, 24) & (df.sex == "F")] - ) / len(df[df.is_alive & df.age_years.between(15, 24) & (df.sex == "F")]) if len( - df[df.is_alive & df.age_years.between(15, 24) & (df.sex == "F")]) else 0 + female_prev_1524 = ( + len( + df[ + df.hv_inf + & df.is_alive + & df.age_years.between(15, 24) + & (df.sex == "F") + ] + ) + / len(df[df.is_alive & df.age_years.between(15, 24) & (df.sex == "F")]) + if len(df[df.is_alive & df.age_years.between(15, 24) & (df.sex == "F")]) + else 0 + ) - female_prev_2549 = len( - df[df.hv_inf & df.is_alive & df.age_years.between(25, 49) & (df.sex == "F")] - ) / len(df[df.is_alive & df.age_years.between(25, 49) & (df.sex == "F")]) if len( - df[df.is_alive & df.age_years.between(25, 49) & (df.sex == "F")]) else 0 + female_prev_2549 = ( + len( + df[ + df.hv_inf + & df.is_alive + & df.age_years.between(25, 49) + & (df.sex == "F") + ] + ) + / len(df[df.is_alive & df.age_years.between(25, 49) & (df.sex == "F")]) + if len(df[df.is_alive & df.age_years.between(25, 49) & (df.sex == "F")]) + else 0 + ) - total_prev = len( - df[df.hv_inf & df.is_alive] - ) / len(df[df.is_alive]) + total_prev = len(df[df.hv_inf & df.is_alive]) / len(df[df.is_alive]) # incidence by age-group and sex n_new_infections_male_1524 = len( @@ -2857,13 +3270,14 @@ def apply(self, population): & (df.sex == "M") & df.is_alive & (df.hv_date_inf >= (now - DateOffset(months=self.repeat))) - ] + ] + ) + denom_male_1524 = len( + df[df.is_alive & (df.sex == "M") & df.age_years.between(15, 24)] + ) + male_inc_1524 = ( + n_new_infections_male_1524 / denom_male_1524 if denom_male_1524 else 0 ) - denom_male_1524 = len(df[ - df.is_alive - & (df.sex == "M") - & df.age_years.between(15, 24)]) - male_inc_1524 = n_new_infections_male_1524 / denom_male_1524 if denom_male_1524 else 0 n_new_infections_male_2549 = len( df.loc[ @@ -2871,13 +3285,14 @@ def apply(self, population): & (df.sex == "M") & df.is_alive & (df.hv_date_inf >= (now - DateOffset(months=self.repeat))) - ] + ] + ) + denom_male_2549 = len( + df[df.is_alive & (df.sex == "M") & df.age_years.between(25, 49)] + ) + male_inc_2549 = ( + n_new_infections_male_2549 / denom_male_2549 if denom_male_2549 else 0 ) - denom_male_2549 = len(df[ - df.is_alive - & (df.sex == "M") - & df.age_years.between(25, 49)]) - male_inc_2549 = n_new_infections_male_2549 / denom_male_2549 if denom_male_2549 else 0 n_new_infections_male_1549 = len( df.loc[ @@ -2885,7 +3300,7 @@ def apply(self, population): & (df.sex == "M") & df.is_alive & (df.hv_date_inf >= (now - DateOffset(months=self.repeat))) - ] + ] ) n_new_infections_female_1524 = len( @@ -2894,13 +3309,14 @@ def apply(self, population): & (df.sex == "F") & df.is_alive & (df.hv_date_inf >= (now - DateOffset(months=self.repeat))) - ] + ] + ) + denom_female_1524 = len( + df[df.is_alive & (df.sex == "F") & df.age_years.between(15, 24)] + ) + female_inc_1524 = ( + n_new_infections_female_1524 / denom_female_1524 if denom_female_1524 else 0 ) - denom_female_1524 = len(df[ - df.is_alive - & (df.sex == "F") - & df.age_years.between(15, 24)]) - female_inc_1524 = n_new_infections_female_1524 / denom_female_1524 if denom_female_1524 else 0 n_new_infections_female_2549 = len( df.loc[ @@ -2908,13 +3324,14 @@ def apply(self, population): & (df.sex == "F") & df.is_alive & (df.hv_date_inf >= (now - DateOffset(months=self.repeat))) - ] + ] + ) + denom_female_2549 = len( + df[df.is_alive & (df.sex == "F") & df.age_years.between(25, 49)] + ) + female_inc_2549 = ( + n_new_infections_female_2549 / denom_female_2549 if denom_female_2549 else 0 ) - denom_female_2549 = len(df[ - df.is_alive - & (df.sex == "F") - & df.age_years.between(25, 49)]) - female_inc_2549 = n_new_infections_female_2549 / denom_female_2549 if denom_female_2549 else 0 logger.info( key="infections_by_2age_groups_and_sex", @@ -2952,7 +3369,7 @@ def apply(self, population): & (df.hv_number_tests > 0) & (df.age_years >= 15) & (df.hv_last_test_date >= (now - DateOffset(months=self.repeat))) - ] + ] ) n_pop = len(df.loc[df.is_alive & (df.age_years >= 15)]) tested = n_tested / n_pop if n_pop else 0 @@ -2966,7 +3383,7 @@ def apply(self, population): & (df.hv_number_tests > 0) & (df.age_years >= 15) & (df.hv_last_test_date >= (now - DateOffset(months=self.repeat))) - ] + ] ) n_pop = len(df.loc[(df.sex == sex) & (df.age_years >= 15)]) testing_by_sex[sex] = n_tested / n_pop if n_pop else 0 @@ -2977,17 +3394,21 @@ def apply(self, population): # testing yield: number positive results divided by number tests performed # if person has multiple tests in one year, will only count 1 total_tested = len( - df.loc[(df.hv_number_tests > 0) - & (df.hv_last_test_date >= (now - DateOffset(months=self.repeat))) - ] + df.loc[ + (df.hv_number_tests > 0) + & (df.hv_last_test_date >= (now - DateOffset(months=self.repeat))) + ] ) total_tested_hiv_positive = len( - df.loc[df.hv_inf - & (df.hv_number_tests > 0) - & (df.hv_last_test_date >= (now - DateOffset(months=self.repeat))) - ] + df.loc[ + df.hv_inf + & (df.hv_number_tests > 0) + & (df.hv_last_test_date >= (now - DateOffset(months=self.repeat))) + ] + ) + testing_yield = ( + total_tested_hiv_positive / total_tested if total_tested > 0 else 0 ) - testing_yield = total_tested_hiv_positive / total_tested if total_tested > 0 else 0 # ------------------------------------ TREATMENT ------------------------------------ def treatment_counts(subset): @@ -3020,7 +3441,7 @@ def treatment_counts(subset): & (df.sex == "M") & df.is_alive & (df.hv_art != "not") - ] + ] ) n_on_art_female_15plus = len( @@ -3029,46 +3450,56 @@ def treatment_counts(subset): & (df.sex == "F") & df.is_alive & (df.hv_art != "not") - ] + ] ) n_on_art_children = len( - df.loc[ - (df.age_years < 15) - & df.is_alive - & (df.hv_art != "not") - ] + df.loc[(df.age_years < 15) & df.is_alive & (df.hv_art != "not")] ) - n_on_art_total = n_on_art_male_15plus + n_on_art_female_15plus + n_on_art_children + n_on_art_total = ( + n_on_art_male_15plus + n_on_art_female_15plus + n_on_art_children + ) # ------------------------------------ BEHAVIOUR CHANGE ------------------------------------ # proportion of adults (15+) exposed to behaviour change intervention - prop_adults_exposed_to_behav_intv = len( - df[df.is_alive & df.hv_behaviour_change & (df.age_years >= 15)] - ) / len(df[df.is_alive & (df.age_years >= 15)]) if len(df[df.is_alive & (df.age_years >= 15)]) else 0 + prop_adults_exposed_to_behav_intv = ( + len(df[df.is_alive & df.hv_behaviour_change & (df.age_years >= 15)]) + / len(df[df.is_alive & (df.age_years >= 15)]) + if len(df[df.is_alive & (df.age_years >= 15)]) + else 0 + ) # ------------------------------------ PREP AMONG FSW ------------------------------------ prop_fsw_on_prep = ( - 0 - if n_fsw == 0 - else len( - df[ - df.is_alive - & df.li_is_sexworker - & (df.age_years >= 15) - & df.hv_is_on_prep + ( + 0 + if n_fsw == 0 + else len( + df[ + df.is_alive + & df.li_is_sexworker + & (df.age_years >= 15) + & df.hv_is_on_prep ] - ) / len(df[df.is_alive & df.li_is_sexworker & (df.age_years >= 15)]) - ) if len(df[df.is_alive & df.li_is_sexworker & (df.age_years >= 15)]) else 0 + ) + / len(df[df.is_alive & df.li_is_sexworker & (df.age_years >= 15)]) + ) + if len(df[df.is_alive & df.li_is_sexworker & (df.age_years >= 15)]) + else 0 + ) # ------------------------------------ MALE CIRCUMCISION ------------------------------------ # NB. Among adult men - prop_men_circ = len( - df[df.is_alive & (df.sex == "M") & (df.age_years >= 15) & df.li_is_circ] - ) / len(df[df.is_alive & (df.sex == "M") & (df.age_years >= 15)]) if len( - df[df.is_alive & (df.sex == "M") & (df.age_years >= 15)]) else 0 + prop_men_circ = ( + len( + df[df.is_alive & (df.sex == "M") & (df.age_years >= 15) & df.li_is_circ] + ) + / len(df[df.is_alive & (df.sex == "M") & (df.age_years >= 15)]) + if len(df[df.is_alive & (df.sex == "M") & (df.age_years >= 15)]) + else 0 + ) logger.info( key="hiv_program_coverage", @@ -3103,16 +3534,26 @@ def treatment_counts(subset): # adults # get index of adults starting tx in last time-period - adult_tx_idx = df.loc[(df.age_years >= 16) & - (df.hv_date_treated >= (now - DateOffset(months=self.repeat)))].index + adult_tx_idx = df.loc[ + (df.age_years >= 16) + & (df.hv_date_treated >= (now - DateOffset(months=self.repeat))) + ].index # calculate treatment_date - onset_date for each person in index - adult_tx_delays = (df.loc[adult_tx_idx, "hv_date_treated"] - df.loc[adult_tx_idx, "hv_date_inf"]).dt.days + adult_tx_delays = ( + df.loc[adult_tx_idx, "hv_date_treated"] + - df.loc[adult_tx_idx, "hv_date_inf"] + ).dt.days adult_tx_delays = adult_tx_delays.tolist() # children - child_tx_idx = df.loc[(df.age_years < 16) & - (df.hv_date_treated >= (now - DateOffset(months=self.repeat)))].index - child_tx_delays = (df.loc[child_tx_idx, "hv_date_treated"] - df.loc[child_tx_idx, "hv_date_inf"]).dt.days + child_tx_idx = df.loc[ + (df.age_years < 16) + & (df.hv_date_treated >= (now - DateOffset(months=self.repeat))) + ].index + child_tx_delays = ( + df.loc[child_tx_idx, "hv_date_treated"] + - df.loc[child_tx_idx, "hv_date_inf"] + ).dt.days child_tx_delays = child_tx_delays.tolist() logger.info( @@ -3187,8 +3628,11 @@ class DummyHivModule(Module): PROPERTIES = { "hv_inf": Property(Types.BOOL, "DUMMY version of the property for hv_inf"), - "hv_art": Property(Types.CATEGORICAL, "DUMMY version of the property for hv_art.", - categories=["not", "on_VL_suppressed", "on_not_VL_suppressed"]), + "hv_art": Property( + Types.CATEGORICAL, + "DUMMY version of the property for hv_art.", + categories=["not", "on_VL_suppressed", "on_not_VL_suppressed"], + ), } def __init__(self, name=None, hiv_prev=0.1, art_cov=0.75): @@ -3202,9 +3646,11 @@ def read_parameters(self, data_folder): def initialise_population(self, population): df = population.props df.loc[df.is_alive, "hv_inf"] = self.rng.rand(sum(df.is_alive)) < self.hiv_prev - df.loc[(df.is_alive & df.hv_inf), "hv_art"] = pd.Series( - self.rng.rand(sum(df.is_alive & df.hv_inf)) < self.art_cov).replace( - {True: "on_VL_suppressed", False: "not"}).values + df.loc[(df.is_alive & df.hv_inf), "hv_art"] = ( + pd.Series(self.rng.rand(sum(df.is_alive & df.hv_inf)) < self.art_cov) + .replace({True: "on_VL_suppressed", False: "not"}) + .values + ) def initialise_simulation(self, sim): pass @@ -3214,4 +3660,6 @@ def on_birth(self, mother, child): df.at[child, "hv_inf"] = self.rng.rand() < self.hiv_prev if df.at[child, "hv_inf"]: - df.at[child, "hv_art"] = "on_VL_suppressed" if self.rng.rand() < self.art_cov else "not" + df.at[child, "hv_art"] = ( + "on_VL_suppressed" if self.rng.rand() < self.art_cov else "not" + ) diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 9d9590cffc..a395493c7a 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -10,13 +10,19 @@ import pandas as pd from tlo import DateOffset, Module, Parameter, Property, Types, logging -from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent +from tlo.events import ( + Event, + IndividualScopeEventMixin, + PopulationScopeEventMixin, + RegularEvent, +) from tlo.methods import Metadata from tlo.methods.causes import Cause from tlo.methods.dxmanager import DxTest from tlo.methods.healthsystem import HSI_Event from tlo.methods.symptommanager import Symptom from tlo.util import random_date +from tlo.lm import LinearModel, LinearModelType, Predictor logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -36,220 +42,366 @@ def __init__(self, name=None, resourcefilepath=None): self.itn_irs = None self.all_inc = None self.item_codes_for_consumables_required = dict() + self.lm = dict() INIT_DEPENDENCIES = { - 'Contraception', 'Demography', 'HealthSystem', 'SymptomManager' + "Contraception", + "Demography", + "HealthSystem", + "SymptomManager", } - OPTIONAL_INIT_DEPENDENCIES = {'HealthBurden'} + OPTIONAL_INIT_DEPENDENCIES = {"HealthBurden"} METADATA = { Metadata.DISEASE_MODULE, Metadata.USES_HEALTHSYSTEM, Metadata.USES_HEALTHBURDEN, - Metadata.USES_SYMPTOMMANAGER + Metadata.USES_SYMPTOMMANAGER, } # Declare Causes of Death CAUSES_OF_DEATH = { - 'Malaria': Cause(gbd_causes='Malaria', label='Malaria'), + "Malaria": Cause(gbd_causes="Malaria", label="Malaria"), } # Declare Causes of Disability - CAUSES_OF_DISABILITY = { - 'Malaria': Cause(gbd_causes='Malaria', label='Malaria') - } + CAUSES_OF_DISABILITY = {"Malaria": Cause(gbd_causes="Malaria", label="Malaria")} PARAMETERS = { - 'interv': Parameter(Types.REAL, 'data frame of intervention coverage by year'), - 'clin_inc': Parameter( + "interv": Parameter(Types.REAL, "data frame of intervention coverage by year"), + "clin_inc": Parameter( Types.REAL, - 'data frame of clinical incidence by age, district, intervention coverage', + "data frame of clinical incidence by age, district, intervention coverage", ), - 'inf_inc': Parameter( + "inf_inc": Parameter( Types.REAL, - 'data frame of infection incidence by age, district, intervention coverage', + "data frame of infection incidence by age, district, intervention coverage", ), - 'sev_inc': Parameter( + "sev_inc": Parameter( Types.REAL, - 'data frame of severe case incidence by age, district, intervention coverage', + "data frame of severe case incidence by age, district, intervention coverage", + ), + "itn_district": Parameter( + Types.REAL, "data frame of ITN usage rates by district" + ), + "irs_district": Parameter( + Types.REAL, "data frame of IRS usage rates by district" ), - 'itn_district': Parameter( - Types.REAL, 'data frame of ITN usage rates by district' + "sev_symp_prob": Parameter( + Types.REAL, "probabilities of each symptom for severe malaria cases" ), - 'irs_district': Parameter( - Types.REAL, 'data frame of IRS usage rates by district' + "sensitivity_rdt": Parameter(Types.REAL, "Sensitivity of rdt"), + "cfr": Parameter(Types.REAL, "case-fatality rate for severe malaria"), + "dur_asym": Parameter(Types.REAL, "duration (days) of asymptomatic malaria"), + "dur_clin": Parameter( + Types.REAL, "duration (days) of clinical symptoms of malaria" ), - 'sev_symp_prob': Parameter( - Types.REAL, 'probabilities of each symptom for severe malaria cases' + "dur_clin_para": Parameter( + Types.REAL, "duration (days) of parasitaemia for clinical malaria cases" ), - 'sensitivity_rdt': Parameter(Types.REAL, 'Sensitivity of rdt'), - 'cfr': Parameter(Types.REAL, 'case-fatality rate for severe malaria'), - 'dur_asym': Parameter(Types.REAL, 'duration (days) of asymptomatic malaria'), - 'dur_clin': Parameter( - Types.REAL, 'duration (days) of clinical symptoms of malaria' + "treatment_adjustment": Parameter( + Types.REAL, "probability of death from severe malaria if on treatment" ), - 'dur_clin_para': Parameter( - Types.REAL, 'duration (days) of parasitaemia for clinical malaria cases' + "p_sev_anaemia_preg": Parameter( + Types.REAL, + "probability of severe anaemia in pregnant women with clinical malaria", ), - 'rr_hiv': Parameter( - Types.REAL, 'relative risk of clinical malaria if hiv-positive' + "itn_proj": Parameter( + Types.REAL, "coverage of ITN for projections 2020 onwards" ), - 'treatment_adjustment': Parameter( - Types.REAL, 'probability of death from severe malaria if on treatment' + "mortality_adjust": Parameter( + Types.REAL, "adjustment of case-fatality rate to match WHO/MAP" ), - 'p_sev_anaemia_preg': Parameter( + "data_end": Parameter( Types.REAL, - 'probability of severe anaemia in pregnant women with clinical malaria', + "final year of ICL malaria model outputs, after 2018 = projections", ), - 'itn_proj': Parameter( - Types.REAL, 'coverage of ITN for projections 2020 onwards' + "irs_rates_boundary": Parameter( + Types.REAL, "threshold for indoor residual spraying coverage" ), - 'mortality_adjust': Parameter( - Types.REAL, 'adjustment of case-fatality rate to match WHO/MAP' + "irs_rates_upper": Parameter( + Types.REAL, "indoor residual spraying high coverage" ), - 'data_end': Parameter( - Types.REAL, 'final year of ICL malaria model outputs, after 2018 = projections' + "irs_rates_lower": Parameter( + Types.REAL, "indoor residual spraying low coverage" ), - 'irs_rates_boundary': Parameter( - Types.REAL, 'threshold for indoor residual spraying coverage' + "prob_malaria_case_tests": Parameter( + Types.REAL, "probability that a malaria case will have a scheduled rdt" ), - 'irs_rates_upper': Parameter( - Types.REAL, 'indoor residual spraying high coverage' + "itn": Parameter(Types.REAL, "projected future itn coverage"), + "rdt_testing_rates": Parameter( + Types.REAL, + "per capita rdt testing rate of general population", ), - 'irs_rates_lower': Parameter( - Types.REAL, 'indoor residual spraying low coverage' + "scaling_factor_for_rdt_availability": Parameter( + Types.REAL, + "scaling factor applied to the reports of rdt usage to compensate for" + "non-availability of rdts at some facilities", ), - 'prob_malaria_case_tests': Parameter( - Types.REAL, 'probability that a malaria case will have a scheduled rdt' + "duration_iptp_protection_weeks": Parameter( + Types.REAL, + "duration of protection against clinical malaria conferred by each dose of IPTp", ), - 'itn': Parameter( - Types.REAL, 'projected future itn coverage' + "rr_clinical_malaria_hiv_under5": Parameter( + Types.REAL, + "relative risk of clinical malaria if HIV+ and aged under 5 years", ), - 'rdt_testing_rates': Parameter( + "rr_clinical_malaria_hiv_over5": Parameter( Types.REAL, - 'per capita rdt testing rate of general population', + "relative risk of clinical malaria if HIV+ and aged over 5 years", + ), + "rr_clinical_malaria_hiv_pregnant": Parameter( + Types.REAL, "relative risk of clinical malaria if HIV+ and pregnant" ), - 'scaling_factor_for_rdt_availability': Parameter( + "rr_clinical_malaria_cotrimoxazole": Parameter( + Types.REAL, "relative risk of clinical malaria if on cotrimoxazole" + ), + "rr_clinical_malaria_art": Parameter( Types.REAL, - 'scaling factor applied to the reports of rdt usage to compensate for' - 'non-availability of rdts at some facilities' - ) + "relative risk of clinical malaria if HIV+ and on ART and virally suppressed", + ), + "rr_clinical_malaria_iptp": Parameter( + Types.REAL, "relative risk of clinical malaria with each dose of IPTp" + ), + "rr_severe_malaria_hiv_under5": Parameter( + Types.REAL, "relative risk of severe malaria if HIV+ and aged under 5 years" + ), + "rr_severe_malaria_hiv_over5": Parameter( + Types.REAL, "relative risk of severe malaria if HIV+ and aged over 5 years" + ), + "rr_severe_malaria_hiv_pregnant": Parameter( + Types.REAL, "relative risk of clinical malaria if HIV+ and pregnant" + ), + "rr_severe_malaria_iptp": Parameter( + Types.REAL, "relative risk of severe malaria with each dose of IPTp" + ), + "prob_of_treatment_success": Parameter( + Types.REAL, "probability malaria treatment cures and clears parasitaemia" + ), } PROPERTIES = { - 'ma_is_infected': Property(Types.BOOL, 'Current status of malaria'), - 'ma_date_infected': Property(Types.DATE, 'Date of latest infection'), - 'ma_date_symptoms': Property( - Types.DATE, 'Date of symptom start for clinical infection' + "ma_is_infected": Property( + Types.BOOL, "Current status of malaria, infected with malaria parasitaemia" ), - 'ma_date_death': Property(Types.DATE, 'Date of death due to malaria'), - 'ma_tx': Property(Types.BOOL, 'Currently on anti-malarial treatment'), - 'ma_date_tx': Property( - Types.DATE, 'Date treatment started for most recent malaria episode' + "ma_date_infected": Property(Types.DATE, "Date of latest infection"), + "ma_date_symptoms": Property( + Types.DATE, "Date of symptom start for clinical infection" ), - 'ma_inf_type': Property( + "ma_date_death": Property(Types.DATE, "Date of death due to malaria"), + "ma_tx": Property( Types.CATEGORICAL, - 'specific symptoms with malaria infection', - categories=['none', 'asym', 'clinical', 'severe'], + "Type of anti-malarial treatment person is currently using", + categories=["none", "uncomplicated", "complicated"], + ), + "ma_date_tx": Property( + Types.DATE, "Date treatment started for most recent malaria episode" ), - 'ma_age_edited': Property( - Types.REAL, 'age values redefined to match with malaria data' + "ma_inf_type": Property( + Types.CATEGORICAL, + "specific symptoms with malaria infection", + categories=["none", "asym", "clinical", "severe"], ), - 'ma_clinical_counter': Property( - Types.INT, 'annual counter for malaria clinical episodes' + "ma_age_edited": Property( + Types.REAL, "age values redefined to match with malaria data" ), - 'ma_dx_counter': Property( - Types.INT, 'annual counter for malaria diagnoses' + "ma_clinical_counter": Property( + Types.INT, "annual counter for malaria clinical episodes" ), - 'ma_tx_counter': Property( - Types.INT, 'annual counter for malaria treatment episodes' + "ma_dx_counter": Property(Types.INT, "annual counter for malaria diagnoses"), + "ma_tx_counter": Property( + Types.INT, "annual counter for malaria treatment episodes" ), - 'ma_clinical_preg_counter': Property( - Types.INT, 'annual counter for malaria clinical episodes in pregnant women' + "ma_clinical_preg_counter": Property( + Types.INT, "annual counter for malaria clinical episodes in pregnant women" ), - 'ma_iptp': Property(Types.BOOL, 'if woman has IPTp in current pregnancy'), + "ma_iptp": Property(Types.BOOL, "if woman has IPTp in current pregnancy"), } def read_parameters(self, data_folder): - workbook = pd.read_excel(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria.xlsx', sheet_name=None) - self.load_parameters_from_dataframe(workbook['parameters']) + workbook = pd.read_excel( + self.resourcefilepath / "ResourceFile_malaria.xlsx", sheet_name=None + ) + self.load_parameters_from_dataframe(workbook["parameters"]) p = self.parameters # baseline characteristics - p['interv'] = workbook['interventions'] - p['itn_district'] = workbook['MAP_ITNrates'] - p['irs_district'] = workbook['MAP_IRSrates'] + p["interv"] = workbook["interventions"] + p["itn_district"] = workbook["MAP_ITNrates"] + p["irs_district"] = workbook["MAP_IRSrates"] - p['sev_symp_prob'] = workbook['severe_symptoms'] - p['rdt_testing_rates'] = workbook['WHO_TestData2023'] + p["sev_symp_prob"] = workbook["severe_symptoms"] + p["rdt_testing_rates"] = workbook["WHO_TestData2023"] - p['inf_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_InfInc_expanded.csv') - p['clin_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_ClinInc_expanded.csv') - p['sev_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_SevInc_expanded.csv') + p["inf_inc"] = pd.read_csv( + self.resourcefilepath / "ResourceFile_malaria_InfInc_expanded.csv" + ) + p["clin_inc"] = pd.read_csv( + self.resourcefilepath / "ResourceFile_malaria_ClinInc_expanded.csv" + ) + p["sev_inc"] = pd.read_csv( + self.resourcefilepath / "ResourceFile_malaria_SevInc_expanded.csv" + ) # check itn projected values are <=0.7 and rounded to 1dp for matching to incidence tables - p['itn'] = round(p['itn'], 1) - assert (p['itn'] <= 0.7) + p["itn"] = round(p["itn"], 1) + assert p["itn"] <= 0.7 # =============================================================================== # single dataframe for itn and irs district/year data; set index for fast lookup # =============================================================================== - itn_curr = p['itn_district'] - itn_curr.rename(columns={'itn_rates': 'itn_rate'}, inplace=True) - itn_curr['itn_rate'] = itn_curr['itn_rate'].round(decimals=1) + itn_curr = p["itn_district"] + itn_curr.rename(columns={"itn_rates": "itn_rate"}, inplace=True) + itn_curr["itn_rate"] = itn_curr["itn_rate"].round(decimals=1) # maximum itn is 0.7; see comment https://github.com/UCL/TLOmodel/pull/165#issuecomment-699625290 - itn_curr.loc[itn_curr.itn_rate > 0.7, 'itn_rate'] = 0.7 - itn_curr = itn_curr.set_index(['District', 'Year']) - - irs_curr = p['irs_district'] - irs_curr.rename(columns={'irs_rates': 'irs_rate'}, inplace=True) - irs_curr.drop(['Region'], axis=1, inplace=True) - irs_curr['irs_rate'] = irs_curr['irs_rate'].round(decimals=1) - irs_curr.loc[irs_curr.irs_rate > p['irs_rates_boundary'], 'irs_rate'] = p['irs_rates_upper'] - irs_curr.loc[irs_curr.irs_rate <= p['irs_rates_boundary'], 'irs_rate'] = p['irs_rates_lower'] - irs_curr = irs_curr.set_index(['District', 'Year']) + itn_curr.loc[itn_curr.itn_rate > 0.7, "itn_rate"] = 0.7 + itn_curr = itn_curr.set_index(["District", "Year"]) + + irs_curr = p["irs_district"] + irs_curr.rename(columns={"irs_rates": "irs_rate"}, inplace=True) + irs_curr.drop(["Region"], axis=1, inplace=True) + irs_curr["irs_rate"] = irs_curr["irs_rate"].round(decimals=1) + irs_curr.loc[irs_curr.irs_rate > p["irs_rates_boundary"], "irs_rate"] = p[ + "irs_rates_upper" + ] + irs_curr.loc[irs_curr.irs_rate <= p["irs_rates_boundary"], "irs_rate"] = p[ + "irs_rates_lower" + ] + irs_curr = irs_curr.set_index(["District", "Year"]) itn_irs = pd.concat([itn_curr, irs_curr], axis=1) # Substitute District Num for District Name - mapper_district_name_to_num = \ - {v: k for k, v in self.sim.modules['Demography'].parameters['district_num_to_district_name'].items()} - self.itn_irs = itn_irs.reset_index().assign( - District_Num=lambda x: x['District'].map(mapper_district_name_to_num) - ).drop(columns=['District']).set_index(['District_Num', 'Year']) + mapper_district_name_to_num = { + v: k + for k, v in self.sim.modules["Demography"] + .parameters["district_num_to_district_name"] + .items() + } + self.itn_irs = ( + itn_irs.reset_index() + .assign( + District_Num=lambda x: x["District"].map(mapper_district_name_to_num) + ) + .drop(columns=["District"]) + .set_index(["District_Num", "Year"]) + ) # =============================================================================== # put the all incidence data into single table with month/admin/llin/irs index # =============================================================================== - inf_inc = p['inf_inc'].set_index(['month', 'admin', 'llin', 'irs', 'age']) - inf_inc = inf_inc.loc[:, ['monthly_prob_inf']] + inf_inc = p["inf_inc"].set_index(["month", "admin", "llin", "irs", "age"]) + inf_inc = inf_inc.loc[:, ["monthly_prob_inf"]] - clin_inc = p['clin_inc'].set_index(['month', 'admin', 'llin', 'irs', 'age']) - clin_inc = clin_inc.loc[:, ['monthly_prob_clin']] + clin_inc = p["clin_inc"].set_index(["month", "admin", "llin", "irs", "age"]) + clin_inc = clin_inc.loc[:, ["monthly_prob_clin"]] - sev_inc = p['sev_inc'].set_index(['month', 'admin', 'llin', 'irs', 'age']) - sev_inc = sev_inc.loc[:, ['monthly_prob_sev']] + sev_inc = p["sev_inc"].set_index(["month", "admin", "llin", "irs", "age"]) + sev_inc = sev_inc.loc[:, ["monthly_prob_sev"]] all_inc = pd.concat([inf_inc, clin_inc, sev_inc], axis=1) # we don't want age to be part of index all_inc = all_inc.reset_index() - all_inc['district_num'] = all_inc['admin'].map(mapper_district_name_to_num) - assert not all_inc['district_num'].isna().any() + all_inc["district_num"] = all_inc["admin"].map(mapper_district_name_to_num) + assert not all_inc["district_num"].isna().any() - self.all_inc = all_inc.drop(columns=['admin']).set_index(['month', 'district_num', 'llin', 'irs']) + self.all_inc = all_inc.drop(columns=["admin"]).set_index( + ["month", "district_num", "llin", "irs"] + ) # get the DALY weight that this module will use from the weight database - if 'HealthBurden' in self.sim.modules: - p['daly_wt_clinical'] = self.sim.modules['HealthBurden'].get_daly_weight(218) - p['daly_wt_severe'] = self.sim.modules['HealthBurden'].get_daly_weight(213) + if "HealthBurden" in self.sim.modules: + p["daly_wt_clinical"] = self.sim.modules["HealthBurden"].get_daly_weight( + 218 + ) + p["daly_wt_severe"] = self.sim.modules["HealthBurden"].get_daly_weight(213) # ----------------------------------- DECLARE THE SYMPTOMS ------------------------------------------- - self.sim.modules['SymptomManager'].register_symptom( - Symptom('severe_anaemia'), # nb. will cause care seeking as much as a typical symptom - Symptom.emergency('severe_malaria'), # emergency + self.sim.modules["SymptomManager"].register_symptom( + Symptom( + "severe_anaemia" + ), # nb. will cause care seeking as much as a typical symptom + Symptom.emergency("severe_malaria"), # emergency + ) + + def pre_initialise_population(self): + """ + * Establish the Linear Models + + if HIV is registered, the conditional predictors will apply + otherwise only IPTp will affect risk of clinical/severe malaria + """ + + p = self.parameters + + # ---- LINEAR MODELS ----- + # LinearModel for the relative risk of clinical malaria infection + predictors = [ + Predictor("ma_iptp").when(True, p["rr_clinical_malaria_iptp"]), + ] + + # people with HIV + conditional_predictors = ( + [ + Predictor().when( + "(hv_inf == True) & (age_years <= 5) & (is_pregnant == False)", + p["rr_clinical_malaria_hiv_under5"], + ), + Predictor().when( + "(hv_inf == True) & (age_years > 5) & (is_pregnant == False)", + p["rr_clinical_malaria_hiv_over5"], + ), + Predictor().when( + "(hv_inf == True) & (is_pregnant == True)", + p["rr_clinical_malaria_hiv_pregnant"], + ), + # treatment effects + # assume same effect of cotrim if pregnant + Predictor("hv_art") + .when("on_VL_suppressed", p["rr_clinical_malaria_art"]) + .otherwise(1.0), + Predictor("hv_on_cotrimoxazole").when( + True, p["rr_clinical_malaria_cotrimoxazole"] + ), + ] + if "Hiv" in self.sim.modules + else [] + ) + + self.lm["rr_of_clinical_malaria"] = LinearModel.multiplicative( + *(predictors + conditional_predictors) + ) + + # LinearModel for the relative risk of severe malaria infection + predictors = [ + Predictor("ma_iptp").when(True, p["rr_severe_malaria_iptp"]), + ] + + # people with HIV + conditional_predictors = ( + [ + Predictor().when( + "(hv_inf == True) & (age_years <= 5) & (is_pregnant == False)", + p["rr_severe_malaria_hiv_under5"], + ), + Predictor().when( + "(hv_inf == True) & (age_years > 5) & (is_pregnant == False)", + p["rr_severe_malaria_hiv_over5"], + ), + Predictor().when( + "(hv_inf == True) & (is_pregnant == True)", + p["rr_severe_malaria_hiv_pregnant"], + ), + ] + if "hiv" in self.sim.modules + else [] + ) + + self.lm["rr_of_severe_malaria"] = LinearModel.multiplicative( + *(predictors + conditional_predictors) ) def initialise_population(self, population): @@ -257,20 +409,20 @@ def initialise_population(self, population): # ----------------------------------- INITIALISE THE POPULATION----------------------------------- # Set default for properties - df.loc[df.is_alive, 'ma_is_infected'] = False - df.loc[df.is_alive, 'ma_date_infected'] = pd.NaT - df.loc[df.is_alive, 'ma_date_symptoms'] = pd.NaT - df.loc[df.is_alive, 'ma_date_death'] = pd.NaT - df.loc[df.is_alive, 'ma_tx'] = False - df.loc[df.is_alive, 'ma_date_tx'] = pd.NaT - df.loc[df.is_alive, 'ma_inf_type'] = 'none' - df.loc[df.is_alive, 'ma_age_edited'] = 0.0 - - df.loc[df.is_alive, 'ma_clinical_counter'] = 0 - df.loc[df.is_alive, 'ma_dx_counter'] = 0 - df.loc[df.is_alive, 'ma_tx_counter'] = 0 - df.loc[df.is_alive, 'ma_clinical_preg_counter'] = 0 - df.loc[df.is_alive, 'ma_iptp'] = False + df.loc[df.is_alive, "ma_is_infected"] = False + df.loc[df.is_alive, "ma_date_infected"] = pd.NaT + df.loc[df.is_alive, "ma_date_symptoms"] = pd.NaT + df.loc[df.is_alive, "ma_date_death"] = pd.NaT + df.loc[df.is_alive, "ma_tx"] = "none" + df.loc[df.is_alive, "ma_date_tx"] = pd.NaT + df.loc[df.is_alive, "ma_inf_type"] = "none" + df.loc[df.is_alive, "ma_age_edited"] = 0.0 + + df.loc[df.is_alive, "ma_clinical_counter"] = 0 + df.loc[df.is_alive, "ma_dx_counter"] = 0 + df.loc[df.is_alive, "ma_tx_counter"] = 0 + df.loc[df.is_alive, "ma_clinical_preg_counter"] = 0 + df.loc[df.is_alive, "ma_iptp"] = False def malaria_poll2(self, population): df = population.props @@ -280,64 +432,97 @@ def malaria_poll2(self, population): # ----------------------------------- DISTRICT INTERVENTION COVERAGE ----------------------------------- # fix values for 2018 onwards - current_year = min(now.year, p['data_end']) + current_year = min(now.year, p["data_end"]) # get itn_irs rows for current year; slice multiindex for all districts & current_year itn_irs_curr = self.itn_irs.loc[pd.IndexSlice[:, current_year], :] - itn_irs_curr = itn_irs_curr.reset_index().drop('Year', axis=1) # we don't use the year column - itn_irs_curr.insert(0, 'month', now.month) # add current month for the incidence index lookup + itn_irs_curr = itn_irs_curr.reset_index().drop( + "Year", axis=1 + ) # we don't use the year column + itn_irs_curr.insert( + 0, "month", now.month + ) # add current month for the incidence index lookup # replace itn coverage with projected coverage levels from 2019 onwards - if now.year > p['data_end']: - itn_irs_curr['itn_rate'] = self.parameters['itn'] + if now.year > p["data_end"]: + itn_irs_curr["itn_rate"] = self.parameters["itn"] month_districtnum_itn_irs_lookup = [ - tuple(r) for r in itn_irs_curr.values] # every row is a key in incidence table + tuple(r) for r in itn_irs_curr.values + ] # every row is a key in incidence table # ----------------------------------- DISTRICT INCIDENCE ESTIMATES ----------------------------------- # get all corresponding rows from the incidence table; drop unneeded column; set new index curr_inc = self.all_inc.loc[month_districtnum_itn_irs_lookup] - curr_inc = curr_inc.reset_index().drop(['month', 'llin', 'irs'], axis=1).set_index(['district_num', 'age']) + curr_inc = ( + curr_inc.reset_index() + .drop(["month", "llin", "irs"], axis=1) + .set_index(["district_num", "age"]) + ) # ----------------------------------- DISTRICT NEW INFECTIONS ----------------------------------- def _draw_incidence_for(_col, _where): """a helper function to perform random draw for selected individuals on column of probabilities""" # create an index from the individuals to lookup entries in the current incidence table - district_age_subset = df.loc[_where, ['district_num_of_residence', 'ma_age_edited']] - district_age_subset.set_index(district_age_subset.columns.to_list(), inplace=True) - district_age_lookup = district_age_subset.index + district_age_lookup = ( + df[_where] + .set_index(["district_num_of_residence", "ma_age_edited"]) + .index + ) # get the monthly incidence probabilities for these individuals monthly_prob = curr_inc.loc[district_age_lookup, _col] # update the index so it's the same as the original population dataframe for these individuals monthly_prob = monthly_prob.set_axis(df.index[_where]) - # select individuals for infection - random_draw = rng.random_sample(_where.sum()) < monthly_prob + + # the linear models only apply to clinical and severe malaria risk + if _col == "monthly_prob_inf": + # select individuals for infection + random_draw = rng.random_sample(_where.sum()) < monthly_prob + + else: + linear_model = ( + self.lm["rr_of_clinical_malaria"] + if _col == "monthly_prob_clin" + else self.lm["rr_of_severe_malaria"] + ) + + # apply linear model to get individual risk + individual_risk = linear_model.predict(df.loc[_where]) + + random_draw = ( + rng.random_sample(_where.sum()) < monthly_prob * individual_risk + ) + selected = _where & random_draw + return selected # we don't have incidence data for over 80s alive = df.is_alive & (df.age_years < 80) alive_over_one = alive & (df.age_exact_years >= 1) - df.loc[alive & df.age_exact_years.between(0, 0.5), 'ma_age_edited'] = 0.0 - df.loc[alive & df.age_exact_years.between(0.5, 1), 'ma_age_edited'] = 0.5 - df.loc[alive_over_one, 'ma_age_edited'] = df.loc[alive_over_one, 'age_years'].astype(float) - - # select new infections, (persons on IPTp are not at risk of infection) - alive_uninfected = alive & ~df.ma_is_infected & ~df.ma_iptp - now_infected = _draw_incidence_for('monthly_prob_inf', alive_uninfected) - df.loc[now_infected, 'ma_inf_type'] = 'asym' + df.loc[alive & df.age_exact_years.between(0, 0.5), "ma_age_edited"] = 0.0 + df.loc[alive & df.age_exact_years.between(0.5, 1), "ma_age_edited"] = 0.5 + df.loc[alive_over_one, "ma_age_edited"] = df.loc[ + alive_over_one, "age_years" + ].astype(float) + + # select new infections + # eligible: uninfected or asym + alive_uninfected = alive & df.ma_inf_type.isin(["none", "asym"]) + now_infected = _draw_incidence_for("monthly_prob_inf", alive_uninfected) + df.loc[now_infected, "ma_inf_type"] = "asym" # draw from currently asymptomatic to allocate clinical cases # this can include people who became infected/asym in previous polls - alive_infected_asym = alive & (df.ma_inf_type == 'asym') - now_clinical = _draw_incidence_for('monthly_prob_clin', alive_infected_asym) - df.loc[now_clinical, 'ma_inf_type'] = 'clinical' + alive_infected_asym = alive & (df.ma_inf_type == "asym") + now_clinical = _draw_incidence_for("monthly_prob_clin", alive_infected_asym) + df.loc[now_clinical, "ma_inf_type"] = "clinical" # draw from clinical cases to allocate severe cases - draw from all currently clinical cases - alive_infected_clinical = alive & (df.ma_inf_type == 'clinical') - now_severe = _draw_incidence_for('monthly_prob_sev', alive_infected_clinical) - df.loc[now_severe, 'ma_inf_type'] = 'severe' + alive_infected_clinical = alive & (df.ma_inf_type == "clinical") + now_severe = _draw_incidence_for("monthly_prob_sev", alive_infected_clinical) + df.loc[now_severe, "ma_inf_type"] = "severe" # ----------------------------------- ASSIGN INFECTION DATES ----------------------------------- @@ -349,7 +534,9 @@ def _draw_incidence_for(_col, _where): # create list of all new infections all_new_infections = list(new_infections) - all_new_infections.extend(x for x in new_clinical if x not in all_new_infections) + all_new_infections.extend( + x for x in new_clinical if x not in all_new_infections + ) all_new_infections.extend(x for x in new_severe if x not in all_new_infections) # scatter infection dates across the month @@ -357,20 +544,27 @@ def _draw_incidence_for(_col, _where): # join all indices (some clinical infections drawn from asymptomatic infections from previous months) for idx in all_new_infections: date_of_infection = now + pd.DateOffset(days=self.rng.randint(1, 30)) - df.at[idx, 'ma_date_infected'] = date_of_infection + df.at[idx, "ma_date_infected"] = date_of_infection - assert (df.loc[all_new_infections, 'ma_date_infected'] >= self.sim.date).all() + assert (df.loc[all_new_infections, "ma_date_infected"] >= self.sim.date).all() # assign date of symptom onset - df.loc[new_clinical, 'ma_date_symptoms'] = df.loc[new_clinical, 'ma_date_infected'] + DateOffset(days=7) - df.loc[new_severe, 'ma_date_symptoms'] = df.loc[new_severe, 'ma_date_infected'] + DateOffset(days=7) + df.loc[new_clinical, "ma_date_symptoms"] = df.loc[ + new_clinical, "ma_date_infected" + ] + DateOffset(days=7) + df.loc[new_severe, "ma_date_symptoms"] = df.loc[ + new_severe, "ma_date_infected" + ] + DateOffset(days=7) # ----------------------------------- CLINICAL MALARIA SYMPTOMS ----------------------------------- # check symptom onset occurs in one week if len(new_clinical): - assert (df.loc[new_clinical, 'ma_date_infected'] < df.loc[new_clinical, 'ma_date_symptoms']).all() - assert not pd.isnull(df.loc[new_clinical, 'ma_date_symptoms']).all() + assert ( + df.loc[new_clinical, "ma_date_infected"] + < df.loc[new_clinical, "ma_date_symptoms"] + ).all() + assert not pd.isnull(df.loc[new_clinical, "ma_date_symptoms"]).all() # ----------------------------------- SCHEDULED DEATHS ----------------------------------- # schedule deaths within the next week @@ -378,21 +572,23 @@ def _draw_incidence_for(_col, _where): # the cfr applies to all severe malaria random_draw = rng.random_sample(size=len(new_severe)) - death = df.index[new_severe][random_draw < (p['cfr'] * p['mortality_adjust'])] + death = df.index[new_severe][random_draw < (p["cfr"] * p["mortality_adjust"])] for person in death: - logger.debug(key='message', - data=f'MalariaEvent: scheduling malaria death for person {person}') + logger.debug( + key="message", + data=f"MalariaEvent: scheduling malaria death for person {person}", + ) # death occurs 1-7 days after symptom onset - date_death = df.at[person, 'ma_date_symptoms'] + DateOffset(days=rng.randint(low=1, high=7)) + date_death = df.at[person, "ma_date_symptoms"] + DateOffset( + days=rng.randint(low=1, high=7) + ) death_event = MalariaDeathEvent( - self, individual_id=person, cause='Malaria' + self, individual_id=person, cause="Malaria" ) # make that death event - self.sim.schedule_event( - death_event, date_death - ) # schedule the death + self.sim.schedule_event(death_event, date_death) # schedule the death def general_population_rdt_scheduler(self, population): """ @@ -406,22 +602,24 @@ def general_population_rdt_scheduler(self, population): # extract annual testing rates from NMCP reports # this is the # rdts issued divided by population size - test_rates = p['rdt_testing_rates'].set_index('Year')['Rate_rdt_testing'].dropna() + test_rates = ( + p["rdt_testing_rates"].set_index("Year")["Rate_rdt_testing"].dropna() + ) rdt_rate = test_rates.loc[min(test_rates.index.max(), self.sim.date.year)] / 12 # adjust rdt usage reported rate to reflect consumables availability - rdt_rate = rdt_rate * p['scaling_factor_for_rdt_availability'] + rdt_rate = rdt_rate * p["scaling_factor_for_rdt_availability"] # testing trends independent of any demographic characteristics # no rdt offered if currently on anti-malarials random_draw = rng.random_sample(size=len(df)) - will_test_idx = df.loc[df.is_alive & ~df.ma_tx & (random_draw < rdt_rate)].index + will_test_idx = df.loc[ + df.is_alive & (df.ma_tx == "none") & (random_draw < rdt_rate) + ].index for person_id in will_test_idx: - date_test = self.sim.date + pd.DateOffset( - days=self.rng.randint(0, 30) - ) - self.sim.modules['HealthSystem'].schedule_hsi_event( + date_test = self.sim.date + pd.DateOffset(days=self.rng.randint(0, 30)) + self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Malaria_rdt_community(person_id=person_id, module=self), priority=1, topen=date_test, @@ -437,88 +635,109 @@ def initialise_simulation(self, sim): # 1) ----------------------------------- REGULAR EVENTS ----------------------------------- - sim.schedule_event(MalariaPollingEventDistrict(self), sim.date + DateOffset(days=0)) + sim.schedule_event( + MalariaPollingEventDistrict(self), sim.date + DateOffset(days=0) + ) sim.schedule_event(MalariaUpdateEvent(self), sim.date + DateOffset(days=0)) - sim.schedule_event(MalariaParasiteClearanceEvent(self), sim.date + DateOffset(months=1)) + sim.schedule_event( + MalariaParasiteClearanceEvent(self), sim.date + DateOffset(months=1) + ) - if 'CareOfWomenDuringPregnancy' not in self.sim.modules: + if "CareOfWomenDuringPregnancy" not in self.sim.modules: sim.schedule_event(MalariaIPTp(self), sim.date + DateOffset(days=30.5)) # add logger events sim.schedule_event(MalariaLoggingEvent(self), sim.date + DateOffset(years=1)) sim.schedule_event(MalariaTxLoggingEvent(self), sim.date + DateOffset(years=1)) - sim.schedule_event(MalariaPrevDistrictLoggingEvent(self), sim.date + DateOffset(months=1)) + sim.schedule_event( + MalariaPrevDistrictLoggingEvent(self), sim.date + DateOffset(months=1) + ) # 2) ----------------------------------- DIAGNOSTIC TESTS ----------------------------------- # Create the diagnostic test representing the use of RDT for malaria diagnosis # and registers it with the Diagnostic Test Manager - self.sim.modules['HealthSystem'].dx_manager.register_dx_test( + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( malaria_rdt=DxTest( - property='ma_is_infected', - item_codes=self.sim.modules['HealthSystem'].get_item_code_from_item_name('Malaria test kit (RDT)'), - sensitivity=self.parameters['sensitivity_rdt'], + property="ma_is_infected", + item_codes=self.sim.modules[ + "HealthSystem" + ].get_item_code_from_item_name("Malaria test kit (RDT)"), + sensitivity=self.parameters["sensitivity_rdt"], ) ) # 3) ----------------------------------- CONSUMABLES ----------------------------------- - get_item_code = self.sim.modules['HealthSystem'].get_item_code_from_item_name + get_item_code = self.sim.modules["HealthSystem"].get_item_code_from_item_name # malaria rdt - self.item_codes_for_consumables_required['malaria_rdt'] = get_item_code('Malaria test kit (RDT)') + self.item_codes_for_consumables_required["malaria_rdt"] = get_item_code( + "Malaria test kit (RDT)" + ) # malaria treatment uncomplicated children <15kg - self.item_codes_for_consumables_required['malaria_uncomplicated_young_children'] = get_item_code( - 'Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST') + self.item_codes_for_consumables_required[ + "malaria_uncomplicated_young_children" + ] = get_item_code("Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST") - self.item_codes_for_consumables_required['paracetamol_syrup'] = get_item_code( - 'Paracetamol syrup 120mg/5ml_0.0119047619047619_CMST') + self.item_codes_for_consumables_required["paracetamol_syrup"] = get_item_code( + "Paracetamol syrup 120mg/5ml_0.0119047619047619_CMST" + ) # malaria treatment uncomplicated children >15kg - self.item_codes_for_consumables_required['malaria_uncomplicated_older_children'] = get_item_code( - 'Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST') + self.item_codes_for_consumables_required[ + "malaria_uncomplicated_older_children" + ] = get_item_code("Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST") # malaria treatment uncomplicated adults >36kg - self.item_codes_for_consumables_required['malaria_uncomplicated_adult'] = get_item_code( - 'Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST') + self.item_codes_for_consumables_required["malaria_uncomplicated_adult"] = ( + get_item_code("Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST") + ) - self.item_codes_for_consumables_required['paracetamol'] = get_item_code('Paracetamol 500mg_1000_CMST') + self.item_codes_for_consumables_required["paracetamol"] = get_item_code( + "Paracetamol 500mg_1000_CMST" + ) # malaria treatment complicated - same consumables for adults and children - self.item_codes_for_consumables_required['malaria_complicated'] = get_item_code('Injectable artesunate') - - self.item_codes_for_consumables_required['malaria_complicated_optional_items'] = [ - get_item_code('Malaria test kit (RDT)'), - get_item_code('Cannula iv (winged with injection pot) 18_each_CMST'), - get_item_code('Disposables gloves, powder free, 100 pieces per box'), - get_item_code('Gauze, absorbent 90cm x 40m_each_CMST'), - get_item_code('Water for injection, 10ml_Each_CMST') + self.item_codes_for_consumables_required["malaria_complicated"] = get_item_code( + "Injectable artesunate" + ) + + self.item_codes_for_consumables_required[ + "malaria_complicated_optional_items" + ] = [ + get_item_code("Malaria test kit (RDT)"), + get_item_code("Cannula iv (winged with injection pot) 18_each_CMST"), + get_item_code("Disposables gloves, powder free, 100 pieces per box"), + get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"), + get_item_code("Water for injection, 10ml_Each_CMST"), ] # malaria IPTp for pregnant women - self.item_codes_for_consumables_required['malaria_iptp'] = get_item_code( - 'Sulfamethoxazole + trimethropin, tablet 400 mg + 80 mg') + self.item_codes_for_consumables_required["malaria_iptp"] = get_item_code( + "Sulfamethoxazole + trimethropin, tablet 400 mg + 80 mg" + ) def on_birth(self, mother_id, child_id): df = self.sim.population.props - df.at[child_id, 'ma_is_infected'] = False - df.at[child_id, 'ma_date_infected'] = pd.NaT - df.at[child_id, 'ma_date_symptoms'] = pd.NaT - df.at[child_id, 'ma_date_death'] = pd.NaT - df.at[child_id, 'ma_tx'] = False - df.at[child_id, 'ma_date_tx'] = pd.NaT - df.at[child_id, 'ma_inf_type'] = 'none' - df.at[child_id, 'ma_age_edited'] = 0.0 - df.at[child_id, 'ma_clinical_counter'] = 0 - df.at[child_id, 'ma_clinical_preg_counter'] = 0 - df.at[child_id, 'ma_dx_counter'] = 0 - df.at[child_id, 'ma_tx_counter'] = 0 - df.at[child_id, 'ma_iptp'] = False + df.at[child_id, "ma_is_infected"] = False + df.at[child_id, "ma_date_infected"] = pd.NaT + df.at[child_id, "ma_date_symptoms"] = pd.NaT + df.at[child_id, "ma_date_death"] = pd.NaT + df.at[child_id, "ma_tx"] = "none" + df.at[child_id, "ma_date_tx"] = pd.NaT + df.at[child_id, "ma_inf_type"] = "none" + df.at[child_id, "ma_age_edited"] = 0.0 + df.at[child_id, "ma_clinical_counter"] = 0 + df.at[child_id, "ma_clinical_preg_counter"] = 0 + df.at[child_id, "ma_dx_counter"] = 0 + df.at[child_id, "ma_tx_counter"] = 0 + df.at[child_id, "ma_iptp"] = False # reset mother's IPTp status to False if mother_id >= 0: # exclude direct births - df.at[mother_id, 'ma_iptp'] = False + df.at[mother_id, "ma_iptp"] = False def report_daly_values(self): # This must send back a pd.Series or pd.DataFrame that reports on the average daly-weights that have been @@ -526,21 +745,20 @@ def report_daly_values(self): # The names of the series of columns is taken to be the label of the cause of this disability. # It will be recorded by the healthburden module as _. - logger.debug(key='message', - data='This is malaria reporting my health values') + logger.debug(key="message", data="This is malaria reporting my health values") df = self.sim.population.props # shortcut to population properties dataframe p = self.parameters - health_values = df.loc[df.is_alive, 'ma_inf_type'].map( + health_values = df.loc[df.is_alive, "ma_inf_type"].map( { - 'none': 0, - 'asym': 0, - 'clinical': p['daly_wt_clinical'], - 'severe': p['daly_wt_severe'], + "none": 0, + "asym": 0, + "clinical": p["daly_wt_clinical"], + "severe": p["daly_wt_severe"], } ) - health_values.name = 'Malaria' # label the cause of this disability + health_values.name = "Malaria" # label the cause of this disability return health_values.loc[df.is_alive] # returns the series @@ -548,36 +766,39 @@ def check_if_fever_is_caused_by_malaria(self, person_id, hsi_event): """Run by an HSI when an adult presents with fever""" # Call the DxTest RDT to diagnose malaria - dx_result = self.sim.modules['HealthSystem'].dx_manager.run_dx_test( - dx_tests_to_run='malaria_rdt', - hsi_event=hsi_event + dx_result = self.sim.modules["HealthSystem"].dx_manager.run_dx_test( + dx_tests_to_run="malaria_rdt", hsi_event=hsi_event ) # Log the test: line-list of summary information about each test - fever_present = 'fever' in self.sim.modules["SymptomManager"].has_what(person_id) + fever_present = "fever" in self.sim.modules["SymptomManager"].has_what( + person_id + ) person_details_for_test = { - 'person_id': person_id, - 'age': self.sim.population.props.at[person_id, 'age_years'], - 'fever_present': fever_present, - 'rdt_result': dx_result, - 'facility_level': hsi_event.ACCEPTED_FACILITY_LEVEL, - 'called_by': hsi_event.TREATMENT_ID + "person_id": person_id, + "age": self.sim.population.props.at[person_id, "age_years"], + "fever_present": fever_present, + "rdt_result": dx_result, + "facility_level": hsi_event.ACCEPTED_FACILITY_LEVEL, + "called_by": hsi_event.TREATMENT_ID, } - logger.info(key='rdt_log', data=person_details_for_test) + logger.info(key="rdt_log", data=person_details_for_test) # get facility level from hsi_event - true_malaria_infection_type = self.sim.population.props.at[person_id, 'ma_inf_type'] + true_malaria_infection_type = self.sim.population.props.at[ + person_id, "ma_inf_type" + ] # severe malaria infection always returns positive RDT - if true_malaria_infection_type == 'severe': - return 'severe_malaria' + if true_malaria_infection_type == "severe": + return "severe_malaria" - elif dx_result and true_malaria_infection_type in ('clinical', 'asym'): - return 'clinical_malaria' + elif dx_result and true_malaria_infection_type in ("clinical", "asym"): + return "clinical_malaria" else: - return 'negative_malaria_test' + return "negative_malaria_test" def do_for_suspected_malaria_case(self, person_id, hsi_event): """ @@ -586,34 +807,34 @@ def do_for_suspected_malaria_case(self, person_id, hsi_event): :return: This is called for a person (of any age) that attends non-emergency generic HSI and has - any symptoms suggestive of malaria """ + any symptoms suggestive of malaria""" df = self.sim.population.props - if not df.at[person_id, 'ma_tx']: - malaria_test_result = self.check_if_fever_is_caused_by_malaria(person_id=person_id, hsi_event=hsi_event) + if df.at[person_id, "ma_tx"] == "none": + malaria_test_result = self.check_if_fever_is_caused_by_malaria( + person_id=person_id, hsi_event=hsi_event + ) # Treat / refer based on diagnosis - if malaria_test_result == 'severe_malaria': - df.at[person_id, 'ma_dx_counter'] += 1 - self.sim.modules['HealthSystem'].schedule_hsi_event( - HSI_Malaria_Treatment_Complicated( - person_id=person_id, - module=self), + if malaria_test_result == "severe_malaria": + df.at[person_id, "ma_dx_counter"] += 1 + self.sim.modules["HealthSystem"].schedule_hsi_event( + HSI_Malaria_Treatment_Complicated(person_id=person_id, module=self), priority=0, topen=self.sim.date, - tclose=None) + tclose=None, + ) # return type 'clinical_malaria' includes asymptomatic infection - elif malaria_test_result == 'clinical_malaria': - df.at[person_id, 'ma_dx_counter'] += 1 - self.sim.modules['HealthSystem'].schedule_hsi_event( - HSI_Malaria_Treatment( - person_id=person_id, - module=self), + elif malaria_test_result == "clinical_malaria": + df.at[person_id, "ma_dx_counter"] += 1 + self.sim.modules["HealthSystem"].schedule_hsi_event( + HSI_Malaria_Treatment(person_id=person_id, module=self), priority=1, topen=self.sim.date, - tclose=None) + tclose=None, + ) def do_on_emergency_presentation_with_severe_malaria(self, person_id, hsi_event): """This is called for a person (of any age) that attends an emergency generic HSI and has a fever. @@ -621,19 +842,21 @@ def do_on_emergency_presentation_with_severe_malaria(self, person_id, hsi_event) """ df = self.sim.population.props - if not df.at[person_id, 'ma_tx']: + if df.at[person_id, "ma_tx"] == "none": # Check if malaria parasitaemia: - malaria_test_result = self.check_if_fever_is_caused_by_malaria(person_id=person_id, hsi_event=hsi_event) + malaria_test_result = self.check_if_fever_is_caused_by_malaria( + person_id=person_id, hsi_event=hsi_event + ) # if any symptoms indicative of malaria and they have parasitaemia (would return a positive rdt) - if malaria_test_result in ('severe_malaria', 'clinical_malaria'): - df.at[person_id, 'ma_dx_counter'] += 1 + if malaria_test_result in ("severe_malaria", "clinical_malaria"): + df.at[person_id, "ma_dx_counter"] += 1 # Launch the HSI for treatment for Malaria, HSI_Malaria_Treatment will determine correct treatment - self.sim.modules['HealthSystem'].schedule_hsi_event( + self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Malaria_Treatment_Complicated( - person_id=person_id, - module=self), + person_id=person_id, module=self + ), priority=0, topen=self.sim.date, ) @@ -644,11 +867,15 @@ class MalariaPollingEventDistrict(RegularEvent, PopulationScopeEventMixin): this calls functions to assign new malaria infections and schedules rdt at a community level (non-symptom driven) """ + def __init__(self, module): super().__init__(module, frequency=DateOffset(months=1)) def apply(self, population): - logger.debug(key='message', data='MalariaEvent: tracking the disease progression of the population') + logger.debug( + key="message", + data="MalariaEvent: tracking the disease progression of the population", + ) # assigns new malaria infections self.module.malaria_poll2(population) @@ -669,19 +896,57 @@ def apply(self, population): df = population.props now = self.sim.date - # select currently pregnant women without IPTp, malaria-negative - p1 = df.index[df.is_alive & df.is_pregnant & ~df.ma_is_infected & ~df.ma_iptp] + # select currently pregnant women without IPTp, malaria-negative, not on cotrimoxazole + p1_condition = ( + df.is_alive + & df.is_pregnant + & ~df.ma_is_infected + & ~df.ma_iptp + & ( + ~df.hv_on_cotrimoxazole + if 'Hiv' in self.sim.modules + else True + ) + ) + + p1 = df.index[p1_condition] for person_index in p1: - logger.debug(key='message', - data=f'MalariaIPTp: scheduling HSI_Malaria_IPTp for person {person_index}') + logger.debug( + key="message", + data=f"MalariaIPTp: scheduling HSI_Malaria_IPTp for person {person_index}", + ) event = HSI_MalariaIPTp(self.module, person_id=person_index) - self.sim.modules['HealthSystem'].schedule_hsi_event( + self.sim.modules["HealthSystem"].schedule_hsi_event( event, priority=1, topen=now, tclose=None ) +class MalariaEndIPTpProtection(Event, IndividualScopeEventMixin): + """ + This resets the properties of a person on IPTp + the protective effects ends after 6 weeks and so the property is reset to prevent the + malaria poll assuming that this person still has reduced susceptibility to malaria infection + """ + + def __init__( + self, + module, + person_id, + ): + super().__init__(module, person_id=person_id) + + def apply(self, person_id): + df = self.sim.population.props + + if not df.at[person_id, "is_alive"] or not df.at[person_id, "ma_iptp"]: + return + + # reset the IPTp property + df.at[person_id, "ma_iptp"] = False + + class MalariaDeathEvent(Event, IndividualScopeEventMixin): """ Performs the Death operation on an individual and logs it. @@ -694,38 +959,46 @@ def __init__(self, module, individual_id, cause): def apply(self, individual_id): df = self.sim.population.props - if not df.at[individual_id, 'is_alive'] or (df.at[individual_id, 'ma_inf_type'] == 'none'): + if not df.at[individual_id, "is_alive"] or ( + df.at[individual_id, "ma_inf_type"] == "none" + ): return - # if on treatment, will reduce probability of death + # if on treatment for severe malaria, will reduce probability of death # use random number generator - currently param treatment_adjustment set to 0.5 - if df.at[individual_id, 'ma_tx']: + if df.at[individual_id, "ma_tx"] == "complicated": prob = self.module.rng.rand() # if draw -> death - if prob < self.module.parameters['treatment_adjustment']: - self.sim.modules['Demography'].do_death( - individual_id=individual_id, cause=self.cause, originating_module=self.module) + if prob < self.module.parameters["treatment_adjustment"]: + self.sim.modules["Demography"].do_death( + individual_id=individual_id, + cause=self.cause, + originating_module=self.module, + ) - df.at[individual_id, 'ma_date_death'] = self.sim.date + df.at[individual_id, "ma_date_death"] = self.sim.date # else if draw does not result in death -> cure else: - df.at[individual_id, 'ma_tx'] = False - df.at[individual_id, 'ma_inf_type'] = 'none' - df.at[individual_id, 'ma_is_infected'] = False + df.at[individual_id, "ma_tx"] = "none" + df.at[individual_id, "ma_inf_type"] = "none" + df.at[individual_id, "ma_is_infected"] = False # clear symptoms - self.sim.modules['SymptomManager'].clear_symptoms( + self.sim.modules["SymptomManager"].clear_symptoms( person_id=individual_id, disease_module=self.module ) # if not on treatment - death will occur else: - self.sim.modules['Demography'].do_death( - individual_id=individual_id, cause=self.cause, originating_module=self.module) + self.sim.modules["Demography"].do_death( + individual_id=individual_id, + cause=self.cause, + originating_module=self.module, + ) - df.at[individual_id, 'ma_date_death'] = self.sim.date + df.at[individual_id, "ma_date_death"] = self.sim.date # --------------------------------------------------------------------------------- @@ -739,69 +1012,74 @@ class HSI_Malaria_rdt(HSI_Event, IndividualScopeEventMixin): default facility level is 1a unless specified """ - def __init__(self, module, person_id, facility_level='1a'): + def __init__(self, module, person_id, facility_level="1a"): super().__init__(module, person_id=person_id) assert isinstance(module, Malaria) - self.TREATMENT_ID = 'Malaria_Test' + self.TREATMENT_ID = "Malaria_Test" self.facility_level = facility_level df = self.sim.population.props - person_age_years = df.at[self.target, 'age_years'] - self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({ - 'Under5OPD' if person_age_years < 5 else 'Over5OPD': 1} + person_age_years = df.at[self.target, "age_years"] + self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint( + {"Under5OPD" if person_age_years < 5 else "Over5OPD": 1} ) - self.ACCEPTED_FACILITY_LEVEL = '1a' if (self.facility_level == '1a') else '1b' + self.ACCEPTED_FACILITY_LEVEL = "1a" if (self.facility_level == "1a") else "1b" def apply(self, person_id, squeeze_factor): df = self.sim.population.props - hs = self.sim.modules['HealthSystem'] + hs = self.sim.modules["HealthSystem"] # Ignore this event if the person is no longer alive or already on treatment - if not df.at[person_id, 'is_alive'] or df.at[person_id, 'ma_tx']: + if not df.at[person_id, "is_alive"] or (df.at[person_id, "ma_tx"] != "none"): return hs.get_blank_appt_footprint() - district = df.at[person_id, 'district_num_of_residence'] - logger.debug(key='message', - data=f'HSI_Malaria_rdt: rdt test for person {person_id} ' - f'in district num {district}') + district = df.at[person_id, "district_num_of_residence"] + logger.debug( + key="message", + data=f"HSI_Malaria_rdt: rdt test for person {person_id} " + f"in district num {district}", + ) # call the DxTest RDT to diagnose malaria dx_result = hs.dx_manager.run_dx_test( - dx_tests_to_run='malaria_rdt', - hsi_event=self + dx_tests_to_run="malaria_rdt", hsi_event=self ) # Log the test: line-list of summary information about each test - fever_present = 'fever' in self.sim.modules["SymptomManager"].has_what(person_id) + fever_present = "fever" in self.sim.modules["SymptomManager"].has_what( + person_id + ) person_details_for_test = { - 'person_id': person_id, - 'age': df.at[person_id, 'age_years'], - 'fever_present': fever_present, - 'rdt_result': dx_result, - 'facility_level': self.ACCEPTED_FACILITY_LEVEL, - 'called_by': self.TREATMENT_ID + "person_id": person_id, + "age": df.at[person_id, "age_years"], + "fever_present": fever_present, + "rdt_result": dx_result, + "facility_level": self.ACCEPTED_FACILITY_LEVEL, + "called_by": self.TREATMENT_ID, } - logger.info(key='rdt_log', data=person_details_for_test) + logger.info(key="rdt_log", data=person_details_for_test) if dx_result: # ----------------------------------- SEVERE MALARIA ----------------------------------- - df.at[person_id, 'ma_dx_counter'] += 1 + df.at[person_id, "ma_dx_counter"] += 1 # if severe malaria, treat for complicated malaria - if df.at[person_id, 'ma_inf_type'] == 'severe': + if df.at[person_id, "ma_inf_type"] == "severe": - logger.debug(key='message', - data=f'HSI_Malaria_rdt: scheduling HSI_Malaria_Treatment_Complicated {person_id}' - f'on date {self.sim.date}') + logger.debug( + key="message", + data=f"HSI_Malaria_rdt: scheduling HSI_Malaria_Treatment_Complicated {person_id}" + f"on date {self.sim.date}", + ) treat = HSI_Malaria_Treatment_Complicated( - self.sim.modules['Malaria'], person_id=person_id + self.sim.modules["Malaria"], person_id=person_id ) - self.sim.modules['HealthSystem'].schedule_hsi_event( + self.sim.modules["HealthSystem"].schedule_hsi_event( treat, priority=0, topen=self.sim.date, tclose=None ) @@ -810,20 +1088,24 @@ def apply(self, person_id, squeeze_factor): # clinical malaria - not severe # this will allow those with asym malaria (positive RDT) to also be treated else: - logger.debug(key='message', - data=f'HSI_Malaria_rdt scheduling HSI_Malaria_Treatment for person {person_id}' - f'on date {self.sim.date}') + logger.debug( + key="message", + data=f"HSI_Malaria_rdt scheduling HSI_Malaria_Treatment for person {person_id}" + f"on date {self.sim.date}", + ) treat = HSI_Malaria_Treatment(self.module, person_id=person_id) - self.sim.modules['HealthSystem'].schedule_hsi_event( + self.sim.modules["HealthSystem"].schedule_hsi_event( treat, priority=1, topen=self.sim.date, tclose=None ) elif dx_result is None: # repeat appt for rdt and move to level 1b regardless of current facility level - self.sim.modules['HealthSystem'].schedule_hsi_event( - HSI_Malaria_rdt(person_id=person_id, module=self.module, facility_level='1b'), + self.sim.modules["HealthSystem"].schedule_hsi_event( + HSI_Malaria_rdt( + person_id=person_id, module=self.module, facility_level="1b" + ), topen=self.sim.date + pd.DateOffset(days=1), tclose=None, priority=0, @@ -847,41 +1129,46 @@ def __init__(self, module, person_id): super().__init__(module, person_id=person_id) assert isinstance(module, Malaria) - self.TREATMENT_ID = 'Malaria_Test' - self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({'ConWithDCSA': 1}) - self.ACCEPTED_FACILITY_LEVEL = '0' + self.TREATMENT_ID = "Malaria_Test" + self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"ConWithDCSA": 1}) + self.ACCEPTED_FACILITY_LEVEL = "0" def apply(self, person_id, squeeze_factor): df = self.sim.population.props - hs = self.sim.modules['HealthSystem'] + hs = self.sim.modules["HealthSystem"] # Ignore this event if the person is no longer alive or already on treatment - if not df.at[person_id, 'is_alive'] or df.at[person_id, 'ma_tx']: + if not df.at[person_id, "is_alive"] or not ( + df.at[person_id, "ma_tx"] == "none" + ): return hs.get_blank_appt_footprint() # call the DxTest RDT to diagnose malaria dx_result = hs.dx_manager.run_dx_test( - dx_tests_to_run='malaria_rdt', - hsi_event=self + dx_tests_to_run="malaria_rdt", hsi_event=self ) # Log the test: line-list of summary information about each test - fever_present = 'fever' in self.sim.modules["SymptomManager"].has_what(person_id) + fever_present = "fever" in self.sim.modules["SymptomManager"].has_what( + person_id + ) person_details_for_test = { - 'person_id': person_id, - 'age': df.at[person_id, 'age_years'], - 'fever_present': fever_present, - 'rdt_result': dx_result, - 'facility_level': self.ACCEPTED_FACILITY_LEVEL, - 'called_by': self.TREATMENT_ID + "person_id": person_id, + "age": df.at[person_id, "age_years"], + "fever_present": fever_present, + "rdt_result": dx_result, + "facility_level": self.ACCEPTED_FACILITY_LEVEL, + "called_by": self.TREATMENT_ID, } - logger.info(key='rdt_log', data=person_details_for_test) + logger.info(key="rdt_log", data=person_details_for_test) # if positive, refer for a confirmatory test at level 1a if dx_result: - self.sim.modules['HealthSystem'].schedule_hsi_event( - hsi_event=HSI_Malaria_rdt(person_id=person_id, module=self.module, facility_level='1a'), + self.sim.modules["HealthSystem"].schedule_hsi_event( + hsi_event=HSI_Malaria_rdt( + person_id=person_id, module=self.module, facility_level="1a" + ), priority=1, topen=self.sim.date, tclose=self.sim.date + pd.DateOffset(days=1), @@ -897,47 +1184,61 @@ def __init__(self, module, person_id): super().__init__(module, person_id=person_id) assert isinstance(module, Malaria) - self.TREATMENT_ID = 'Malaria_Treatment' + self.TREATMENT_ID = "Malaria_Treatment" - self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({ - ('Under5OPD' if self.sim.population.props.at[person_id, "age_years"] < 5 else 'Over5OPD'): 1}) - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint( + { + ( + "Under5OPD" + if self.sim.population.props.at[person_id, "age_years"] < 5 + else "Over5OPD" + ): 1 + } + ) + self.ACCEPTED_FACILITY_LEVEL = "1a" def apply(self, person_id, squeeze_factor): df = self.sim.population.props person = df.loc[person_id] - if not person['ma_tx']: + # if not on treatment already - request treatment + if person["ma_tx"] == "none": - logger.debug(key='message', - data=f'HSI_Malaria_Treatment: requesting malaria treatment for {person_id}') + logger.debug( + key="message", + data=f"HSI_Malaria_Treatment: requesting malaria treatment for {person_id}", + ) # Check if drugs are available, and provide drugs: - drugs_available = self.get_drugs(age_of_person=person['age_years']) + drugs_available = self.get_drugs(age_of_person=person["age_years"]) if drugs_available: - logger.debug(key='message', - data=f'HSI_Malaria_Treatment: giving malaria treatment for {person_id}') + logger.debug( + key="message", + data=f"HSI_Malaria_Treatment: giving malaria treatment for {person_id}", + ) - if df.at[person_id, 'is_alive']: - df.at[person_id, 'ma_tx'] = True - df.at[person_id, 'ma_date_tx'] = self.sim.date - df.at[person_id, 'ma_tx_counter'] += 1 + if df.at[person_id, "is_alive"]: + df.at[person_id, "ma_tx"] = "uncomplicated" + df.at[person_id, "ma_date_tx"] = self.sim.date + df.at[person_id, "ma_tx_counter"] += 1 # rdt is offered as part of the treatment package # Log the test: line-list of summary information about each test - fever_present = 'fever' in self.sim.modules["SymptomManager"].has_what(person_id) + fever_present = "fever" in self.sim.modules["SymptomManager"].has_what( + person_id + ) person_details_for_test = { - 'person_id': person_id, - 'age': df.at[person_id, 'age_years'], - 'fever_present': fever_present, - 'rdt_result': True, - 'facility_level': self.ACCEPTED_FACILITY_LEVEL, - 'called_by': self.TREATMENT_ID + "person_id": person_id, + "age": df.at[person_id, "age_years"], + "fever_present": fever_present, + "rdt_result": True, + "facility_level": self.ACCEPTED_FACILITY_LEVEL, + "called_by": self.TREATMENT_ID, } - logger.info(key='rdt_log', data=person_details_for_test) + logger.info(key="rdt_log", data=person_details_for_test) def get_drugs(self, age_of_person): """ @@ -951,32 +1252,47 @@ def get_drugs(self, age_of_person): if age_of_person < 5: # Formulation for young children drugs_available = self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required['malaria_uncomplicated_young_children'], - optional_item_codes=[self.module.item_codes_for_consumables_required['paracetamol_syrup'], - self.module.item_codes_for_consumables_required['malaria_rdt']] + item_codes=self.module.item_codes_for_consumables_required[ + "malaria_uncomplicated_young_children" + ], + optional_item_codes=[ + self.module.item_codes_for_consumables_required[ + "paracetamol_syrup" + ], + self.module.item_codes_for_consumables_required["malaria_rdt"], + ], ) elif 5 <= age_of_person <= 15: # Formulation for older children drugs_available = self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required['malaria_uncomplicated_older_children'], - optional_item_codes=[self.module.item_codes_for_consumables_required['paracetamol_syrup'], - self.module.item_codes_for_consumables_required['malaria_rdt']] + item_codes=self.module.item_codes_for_consumables_required[ + "malaria_uncomplicated_older_children" + ], + optional_item_codes=[ + self.module.item_codes_for_consumables_required[ + "paracetamol_syrup" + ], + self.module.item_codes_for_consumables_required["malaria_rdt"], + ], ) else: # Formulation for adults drugs_available = self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required['malaria_uncomplicated_adult'], - optional_item_codes=[self.module.item_codes_for_consumables_required['paracetamol'], - self.module.item_codes_for_consumables_required['malaria_rdt']] + item_codes=self.module.item_codes_for_consumables_required[ + "malaria_uncomplicated_adult" + ], + optional_item_codes=[ + self.module.item_codes_for_consumables_required["paracetamol"], + self.module.item_codes_for_consumables_required["malaria_rdt"], + ], ) return drugs_available def did_not_run(self): - logger.debug(key='message', - data='HSI_Malaria_Treatment: did not run') + logger.debug(key="message", data="HSI_Malaria_Treatment: did not run") pass @@ -989,50 +1305,69 @@ def __init__(self, module, person_id): super().__init__(module, person_id=person_id) assert isinstance(module, Malaria) - self.TREATMENT_ID = 'Malaria_Treatment_Complicated' - self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({ - ('Under5OPD' if self.sim.population.props.at[person_id, "age_years"] < 5 else 'Over5OPD'): 1}) - self.ACCEPTED_FACILITY_LEVEL = '1b' - self.BEDDAYS_FOOTPRINT = self.make_beddays_footprint({'general_bed': 5}) + self.TREATMENT_ID = "Malaria_Treatment_Complicated" + self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint( + { + ( + "Under5OPD" + if self.sim.population.props.at[person_id, "age_years"] < 5 + else "Over5OPD" + ): 1 + } + ) + self.ACCEPTED_FACILITY_LEVEL = "1b" + self.BEDDAYS_FOOTPRINT = self.make_beddays_footprint({"general_bed": 5}) def apply(self, person_id, squeeze_factor): df = self.sim.population.props - if not df.at[person_id, 'ma_tx'] and df.at[person_id, 'is_alive']: - logger.debug(key='message', - data=f'HSI_Malaria_Treatment_Complicated: requesting complicated malaria treatment for ' - f' {person_id}') + # if person is not on treatment and still alive + if (df.at[person_id, "ma_tx"] == "none") and df.at[person_id, "is_alive"]: + + logger.debug( + key="message", + data=f"HSI_Malaria_Treatment_Complicated: requesting complicated malaria treatment for " + f" {person_id}", + ) if self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required['malaria_complicated'], + item_codes=self.module.item_codes_for_consumables_required[ + "malaria_complicated" + ], optional_item_codes=self.module.item_codes_for_consumables_required[ - 'malaria_complicated_optional_items'] + "malaria_complicated_optional_items" + ], ): - logger.debug(key='message', - data=f'HSI_Malaria_Treatment_Complicated: giving complicated malaria treatment for ' - f' {person_id}') + logger.debug( + key="message", + data=f"HSI_Malaria_Treatment_Complicated: giving complicated malaria treatment for " + f" {person_id}", + ) - df.at[person_id, 'ma_tx'] = True - df.at[person_id, 'ma_date_tx'] = self.sim.date - df.at[person_id, 'ma_tx_counter'] += 1 + df.at[person_id, "ma_tx"] = "complicated" + df.at[person_id, "ma_date_tx"] = self.sim.date + df.at[person_id, "ma_tx_counter"] += 1 # rdt is offered as part of the treatment package # Log the test: line-list of summary information about each test - fever_present = 'fever' in self.sim.modules["SymptomManager"].has_what(person_id) + fever_present = "fever" in self.sim.modules["SymptomManager"].has_what( + person_id + ) person_details_for_test = { - 'person_id': person_id, - 'age': df.at[person_id, 'age_years'], - 'fever_present': fever_present, - 'rdt_result': True, - 'facility_level': self.ACCEPTED_FACILITY_LEVEL, - 'called_by': self.TREATMENT_ID + "person_id": person_id, + "age": df.at[person_id, "age_years"], + "fever_present": fever_present, + "rdt_result": True, + "facility_level": self.ACCEPTED_FACILITY_LEVEL, + "called_by": self.TREATMENT_ID, } - logger.info(key='rdt_log', data=person_details_for_test) + logger.info(key="rdt_log", data=person_details_for_test) def did_not_run(self): - logger.debug(key='message', - data='HSI_Malaria_Treatment_Complicated: did not run') + logger.debug( + key="message", data="HSI_Malaria_Treatment_Complicated: did not run" + ) class HSI_MalariaIPTp(HSI_Event, IndividualScopeEventMixin): @@ -1044,42 +1379,57 @@ def __init__(self, module, person_id): super().__init__(module, person_id=person_id) assert isinstance(module, Malaria) - self.TREATMENT_ID = 'Malaria_Prevention_Iptp' - self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({'Over5OPD': 1}) - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.TREATMENT_ID = "Malaria_Prevention_Iptp" + self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"Over5OPD": 1}) + self.ACCEPTED_FACILITY_LEVEL = "1a" def apply(self, person_id, squeeze_factor): df = self.sim.population.props + p = self.module.parameters - if not df.at[person_id, 'is_alive'] or df.at[person_id, 'ma_tx']: + if not df.at[person_id, "is_alive"] or (df.at[person_id, "ma_tx"] != "none"): return - else: + # IPTp contra-indicated if currently on cotrimoxazole + if 'Hiv' in self.sim.modules and df.at[person_id, "hv_on_cotrimoxazole"]: + return - logger.debug(key='message', - data=f'HSI_MalariaIPTp: requesting IPTp for person {person_id}') + logger.debug( + key="message", + data=f"HSI_MalariaIPTp: requesting IPTp for person {person_id}", + ) - # request the treatment - if self.get_consumables(self.module.item_codes_for_consumables_required['malaria_iptp']): - logger.debug(key='message', - data=f'HSI_MalariaIPTp: giving IPTp for person {person_id}') + # request the treatment + if self.get_consumables( + self.module.item_codes_for_consumables_required["malaria_iptp"] + ): + logger.debug( + key="message", + data=f"HSI_MalariaIPTp: giving IPTp for person {person_id}", + ) - df.at[person_id, 'ma_iptp'] = True + df.at[person_id, "ma_iptp"] = True - # if currently infected, IPTp will clear the infection - df.at[person_id, 'ma_is_infected'] = False - df.at[person_id, 'ma_inf_type'] = 'none' + # if currently infected, IPTp will clear the infection + df.at[person_id, "ma_is_infected"] = False + df.at[person_id, "ma_inf_type"] = "none" - # clear any symptoms - self.sim.modules['SymptomManager'].clear_symptoms( - person_id=person_id, disease_module=self.module - ) + # clear any symptoms + self.sim.modules["SymptomManager"].clear_symptoms( + person_id=person_id, disease_module=self.module + ) + + # If person has been placed/continued on IPTp, schedule end of protective period + self.sim.schedule_event( + MalariaEndIPTpProtection(person_id=person_id, module=self.module), + self.sim.date + + pd.DateOffset(days=7 * p["duration_iptp_protection_weeks"]), + ) def did_not_run(self): - logger.debug(key='message', - data='HSI_MalariaIPTp: did not run') + logger.debug(key="message", data="HSI_MalariaIPTp: did not run") pass @@ -1096,11 +1446,11 @@ def apply(self, population): * assigns symptoms * schedules rdt * cures people currently on treatment for malaria - * clears symptoms for those not on treatment + * clears symptoms for those not on treatment but self-cured * clears parasites if treated """ - logger.debug(key='message', data='MalariaUpdateEvent') + logger.debug(key="message", data="MalariaUpdateEvent") df = self.sim.population.props p = self.module.parameters @@ -1109,103 +1459,126 @@ def apply(self, population): # assign symptoms # find those with schedule date of symptoms = today new_symptomatic_clinical = df.loc[ - df.is_alive - & (df.ma_inf_type == 'clinical') - & (df.ma_date_symptoms == now)].index + df.is_alive & (df.ma_inf_type == "clinical") & (df.ma_date_symptoms == now) + ].index new_symptomatic_severe = df.loc[ - df.is_alive - & (df.ma_inf_type == 'severe') - & (df.ma_date_symptoms == now)].index + df.is_alive & (df.ma_inf_type == "severe") & (df.ma_date_symptoms == now) + ].index new_symptomatic_pregnant = df.loc[ df.is_alive - & ((df.ma_inf_type == 'clinical') | (df.ma_inf_type == 'severe')) + & ((df.ma_inf_type == "clinical") | (df.ma_inf_type == "severe")) & df.is_pregnant - & (df.ma_date_symptoms == now)].index + & (df.ma_date_symptoms == now) + ].index # assign clinical symptoms - self.sim.modules['SymptomManager'].change_symptom( + self.sim.modules["SymptomManager"].change_symptom( person_id=new_symptomatic_clinical, - symptom_string=['fever', 'headache', 'vomiting', 'stomachache'], - add_or_remove='+', + symptom_string=["fever", "headache", "vomiting", "stomachache"], + add_or_remove="+", disease_module=self.module, date_of_onset=now, duration_in_days=None, # remove duration as symptoms cleared by MalariaCureEvent ) # assign symptoms if pregnant - self.sim.modules['SymptomManager'].change_symptom( + self.sim.modules["SymptomManager"].change_symptom( person_id=new_symptomatic_pregnant, - symptom_string='severe_anaemia', - add_or_remove='+', + symptom_string="severe_anaemia", + add_or_remove="+", disease_module=self.module, date_of_onset=now, duration_in_days=None, # remove duration as symptoms cleared by MalariaCureEvent ) # assign severe symptom - self.sim.modules['SymptomManager'].change_symptom( + self.sim.modules["SymptomManager"].change_symptom( person_id=new_symptomatic_severe, - symptom_string='severe_malaria', - add_or_remove='+', + symptom_string="severe_malaria", + add_or_remove="+", disease_module=self.module, date_of_onset=now, duration_in_days=None, # remove duration as symptoms cleared by MalariaCureEvent ) # create list of all new symptomatic cases - all_new_infections = sorted(set(new_symptomatic_clinical).union(new_symptomatic_severe)) + all_new_infections = sorted( + set(new_symptomatic_clinical).union(new_symptomatic_severe) + ) # clinical counter - df.loc[all_new_infections, 'ma_clinical_counter'] += 1 - df.loc[all_new_infections, 'ma_is_infected'] = True + df.loc[all_new_infections, "ma_clinical_counter"] += 1 + df.loc[all_new_infections, "ma_is_infected"] = True # sample those scheduled for rdt eligible_for_rdt = df.loc[df.is_alive & (df.ma_date_symptoms == now)].index - selected_for_rdt = self.module.rng.random_sample(size=len(eligible_for_rdt)) < p['prob_malaria_case_tests'] + selected_for_rdt = ( + self.module.rng.random_sample(size=len(eligible_for_rdt)) + < p["prob_malaria_case_tests"] + ) for idx in eligible_for_rdt[selected_for_rdt]: - self.sim.modules['HealthSystem'].schedule_hsi_event( - HSI_Malaria_rdt(self.module, person_id=idx, facility_level='1a'), + self.sim.modules["HealthSystem"].schedule_hsi_event( + HSI_Malaria_rdt(self.module, person_id=idx, facility_level="1a"), priority=1, - topen=random_date(now + DateOffset(days=1), - now + DateOffset(days=4), - self.module.rng), - tclose=None + topen=random_date( + now + DateOffset(days=1), now + DateOffset(days=4), self.module.rng + ), + tclose=None, ) # TREATED - # select people with malaria and treatment for at least 5 days + # select people with clinical malaria and treatment for at least 5 days # if treated, will clear symptoms and parasitaemia # this will also clear parasitaemia for asymptomatic cases picked up by routine rdt - infected_and_treated = df.index[df.is_alive & - (df.ma_date_tx < (self.sim.date - DateOffset(days=5))) & - (df.ma_inf_type != 'severe')] + random_draw = self.module.rng.random_sample(size=len(df)) + + clinical_and_treated = df.index[ + df.is_alive + & (df.ma_date_tx < (self.sim.date - DateOffset(days=5))) + & (df.ma_inf_type == "clinical") + & (random_draw < p["prob_of_treatment_success"]) + ] + + # select people with severe malaria and treatment for at least 7 days + severe_and_treated = df.index[ + df.is_alive + & (df.ma_date_tx < (self.sim.date - DateOffset(days=7))) + & (df.ma_inf_type == "severe") + & (random_draw < p["prob_of_treatment_success"]) + ] - self.sim.modules['SymptomManager'].clear_symptoms( - person_id=infected_and_treated, disease_module=self.module + # create list of all cases to be resolved through treatment + infections_to_clear = sorted( + set(clinical_and_treated).union(severe_and_treated) + ) + + self.sim.modules["SymptomManager"].clear_symptoms( + person_id=infections_to_clear, disease_module=self.module ) # change properties - df.loc[infected_and_treated, 'ma_tx'] = False - df.loc[infected_and_treated, 'ma_is_infected'] = False - df.loc[infected_and_treated, 'ma_inf_type'] = 'none' + df.loc[infections_to_clear, "ma_tx"] = "none" + df.loc[infections_to_clear, "ma_is_infected"] = False + df.loc[infections_to_clear, "ma_inf_type"] = "none" - # UNTREATED - # if not treated, self-cure occurs after 6 days of symptoms + # UNTREATED or TREATMENT FAILURE + # if not treated or treatment failed, self-cure occurs after 6 days of symptoms # but parasites remain in blood - clinical_not_treated = df.index[df.is_alive & - (df.ma_inf_type == 'clinical') & - (df.ma_date_symptoms < (self.sim.date - DateOffset(days=6))) & - ~df.ma_tx] + clinical_not_treated = df.index[ + df.is_alive + & (df.ma_inf_type == "clinical") + & (df.ma_date_symptoms < (self.sim.date - DateOffset(days=6))) + ] - self.sim.modules['SymptomManager'].clear_symptoms( + self.sim.modules["SymptomManager"].clear_symptoms( person_id=clinical_not_treated, disease_module=self.module ) # change properties - df.loc[clinical_not_treated, 'ma_inf_type'] = 'asym' + df.loc[clinical_not_treated, "ma_inf_type"] = "asym" class MalariaParasiteClearanceEvent(RegularEvent, PopulationScopeEventMixin): @@ -1213,24 +1586,30 @@ def __init__(self, module): super().__init__(module, frequency=DateOffset(days=30.5)) def apply(self, population): - logger.debug(key='message', data='MalariaParasiteClearanceEvent: parasite clearance for malaria cases') + logger.debug( + key="message", + data="MalariaParasiteClearanceEvent: parasite clearance for malaria cases", + ) df = self.sim.population.props p = self.module.parameters # select people infected at least a period ago equal to the duration of asymptomatic infection - asym_inf = df.index[df.is_alive & - (df.ma_inf_type == 'asym') & - (df.ma_date_infected < (self.sim.date - DateOffset(days=p['dur_asym'])))] + asym_inf = df.index[ + df.is_alive + & (df.ma_inf_type == "asym") + & (df.ma_date_infected < (self.sim.date - DateOffset(days=p["dur_asym"]))) + ] - df.loc[asym_inf, 'ma_inf_type'] = 'none' - df.loc[asym_inf, 'ma_is_infected'] = False + df.loc[asym_inf, "ma_inf_type"] = "none" + df.loc[asym_inf, "ma_is_infected"] = False # --------------------------------------------------------------------------------- # Logging # --------------------------------------------------------------------------------- + class MalariaLoggingEvent(RegularEvent, PopulationScopeEventMixin): def __init__(self, module): self.repeat = 12 @@ -1258,7 +1637,7 @@ def apply(self, population): df.loc[ (df.age_years.between(2, 10)) & (df.ma_date_symptoms > (now - DateOffset(months=self.repeat))) - ] + ] ) pop2_10 = len(df[df.is_alive & (df.age_years.between(2, 10))]) @@ -1269,44 +1648,48 @@ def apply(self, population): # using clinical counter # sum all the counters for previous year clin_episodes = df[ - 'ma_clinical_counter' + "ma_clinical_counter" ].sum() # clinical episodes (inc severe) inc_counter_1000py = (clin_episodes / pop) * 1000 clin_preg_episodes = df[ - 'ma_clinical_preg_counter' + "ma_clinical_preg_counter" ].sum() # clinical episodes in pregnant women (inc severe) summary = { - 'number_new_cases': tmp, - 'population': pop, - 'inc_1000py': inc_1000py, - 'inc_1000py_hiv': inc_1000py_hiv, - 'new_cases_2_10': tmp2, - 'population2_10': pop2_10, - 'inc_1000py_2_10': inc_1000py_2_10, - 'inc_clin_counter': inc_counter_1000py, - 'clinical_preg_counter': clin_preg_episodes, + "number_new_cases": tmp, + "population": pop, + "inc_1000py": inc_1000py, + "inc_1000py_hiv": inc_1000py_hiv, + "new_cases_2_10": tmp2, + "population2_10": pop2_10, + "inc_1000py_2_10": inc_1000py_2_10, + "inc_clin_counter": inc_counter_1000py, + "clinical_preg_counter": clin_preg_episodes, } - logger.info(key='incidence', - data=summary, - description='Summary of incident malaria cases') + logger.info( + key="incidence", + data=summary, + description="Summary of incident malaria cases", + ) # ------------------------------------ RUNNING COUNTS ------------------------------------ - counts = {'none': 0, 'asym': 0, 'clinical': 0, 'severe': 0} - counts.update(df.loc[df.is_alive, 'ma_inf_type'].value_counts().to_dict()) + counts = {"none": 0, "asym": 0, "clinical": 0, "severe": 0} + counts.update(df.loc[df.is_alive, "ma_inf_type"].value_counts().to_dict()) - logger.info(key='status_counts', - data=counts, - description='Running counts of incident malaria cases') + logger.info( + key="status_counts", + data=counts, + description="Running counts of incident malaria cases", + ) # ------------------------------------ PARASITE PREVALENCE BY AGE ------------------------------------ # includes all parasite positive cases: some may have low parasitaemia (undetectable) child2_10_inf = len( - df[df.is_alive & (df.ma_inf_type != 'none') & (df.age_years.between(2, 10))] + df[df.is_alive & (df.ma_inf_type != "none") & (df.age_years.between(2, 10))] ) # population size - children @@ -1319,20 +1702,49 @@ def apply(self, population): total_clin = len( df[ df.is_alive - & ((df.ma_inf_type == 'clinical') | (df.ma_inf_type == 'severe')) - ] + & ((df.ma_inf_type == "clinical") | (df.ma_inf_type == "severe")) + ] ) pop2 = len(df[df.is_alive]) prev_clin = total_clin / pop2 prev = { - 'child2_10_prev': child_prev, - 'clinical_prev': prev_clin, + "child2_10_prev": child_prev, + "clinical_prev": prev_clin, } - logger.info(key='prevalence', - data=prev, - description='Prevalence malaria cases') + logger.info(key="prevalence", data=prev, description="Prevalence malaria cases") + + # ------------------------------------ CO-INFECTION PREVALENCE ------------------------------------ + if "Hiv" in self.sim.modules: + # number of people with both HIV and clinical/severe malaria + # output is malaria prevalence in HIV pop + coinfection_num = len( + df[df.is_alive & (df.ma_inf_type != "none") & df.hv_inf] + ) + + # hiv population + hiv_infected = len(df[df.is_alive & df.hv_inf]) + + # prevalence of malaria in HIV population + prev_malaria_in_hiv_population = coinfection_num / hiv_infected + + # proportion of malaria cases with concurrent HIV infection + malaria_infected = len(df[df.is_alive & (df.ma_inf_type != "none")]) + + prop_malaria_cases_with_hiv = coinfection_num / malaria_infected + + coinfection_prevalence = { + "coinfection_num": coinfection_num, + "prev_malaria_in_hiv_population": prev_malaria_in_hiv_population, + "prop_malaria_cases_with_hiv": prop_malaria_cases_with_hiv, + } + + logger.info( + key="coinfection_prevalence", + data=coinfection_prevalence, + description="Co-infection prevalence", + ) class MalariaTxLoggingEvent(RegularEvent, PopulationScopeEventMixin): @@ -1348,33 +1760,34 @@ def apply(self, population): # prop clinical episodes which had treatment, all ages # sum all the counters for previous year - dx = df['ma_dx_counter'].sum() # treatment (inc severe) - tx = df['ma_tx_counter'].sum() # treatment (inc severe) - clin = df['ma_clinical_counter'].sum() # clinical episodes (inc severe) + dx = df["ma_dx_counter"].sum() # treatment (inc severe) + tx = df["ma_tx_counter"].sum() # treatment (inc severe) + clin = df["ma_clinical_counter"].sum() # clinical episodes (inc severe) dx_coverage = dx / clin if clin else 0 tx_coverage = tx / clin if clin else 0 treatment = { - 'number_diagnosed': dx, - 'number_treated': tx, - 'number_clinical episodes': clin, - 'proportion_diagnosed': dx_coverage, - 'treatment_coverage': tx_coverage, + "number_diagnosed": dx, + "number_treated": tx, + "number_clinical episodes": clin, + "proportion_diagnosed": dx_coverage, + "treatment_coverage": tx_coverage, } - logger.info(key='tx_coverage', - data=treatment, - description='Treatment of malaria cases') + logger.info( + key="tx_coverage", data=treatment, description="Treatment of malaria cases" + ) # reset all counters - logger.debug(key='message', - data=f'Resetting the malaria counter {self.sim.date}') + logger.debug( + key="message", data=f"Resetting the malaria counter {self.sim.date}" + ) - df['ma_clinical_counter'] = 0 - df['ma_tx_counter'] = 0 - df['ma_dx_counter'] = 0 - df['ma_clinical_preg_counter'] = 0 + df["ma_clinical_counter"] = 0 + df["ma_tx_counter"] = 0 + df["ma_dx_counter"] = 0 + df["ma_clinical_preg_counter"] = 0 class MalariaPrevDistrictLoggingEvent(RegularEvent, PopulationScopeEventMixin): @@ -1388,18 +1801,24 @@ def apply(self, population): # ------------------------------------ PREVALENCE OF INFECTION ------------------------------------ infected = ( - df[df.is_alive & df.ma_is_infected].groupby('district_num_of_residence').size() + df[df.is_alive & df.ma_is_infected] + .groupby("district_num_of_residence") + .size() ) - pop = df[df.is_alive].groupby('district_num_of_residence').size() + pop = df[df.is_alive].groupby("district_num_of_residence").size() prev = infected / pop prev_ed = prev.fillna(0) assert prev_ed.all() >= 0 # checks assert prev_ed.all() <= 1 - logger.info(key='prev_district', - data=prev_ed.to_dict(), - description='District estimates of malaria prevalence') + logger.info( + key="prev_district", + data=prev_ed.to_dict(), + description="District estimates of malaria prevalence", + ) - logger.info(key='pop_district', - data=pop.to_dict(), - description='District population sizes') + logger.info( + key="pop_district", + data=pop.to_dict(), + description="District population sizes", + ) diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 79afd6fa5f..292a29675a 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -10,7 +10,12 @@ import pandas as pd from tlo import DateOffset, Module, Parameter, Property, Types, logging -from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent +from tlo.events import ( + Event, + IndividualScopeEventMixin, + PopulationScopeEventMixin, + RegularEvent, +) from tlo.lm import LinearModel, LinearModelType, Predictor from tlo.methods import Metadata, hiv from tlo.methods.causes import Cause @@ -41,14 +46,17 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): self.run_with_checks = run_with_checks # tb outputs needed for calibration/ - keys = ["date", - "num_new_active_tb", - "tbPrevLatent" - ] + keys = ["date", "num_new_active_tb", "tbPrevLatent"] # initialise empty dict with set keys self.tb_outputs = {k: [] for k in keys} - INIT_DEPENDENCIES = {"Demography", "HealthSystem", "Lifestyle", "SymptomManager", "Epi"} + INIT_DEPENDENCIES = { + "Demography", + "HealthSystem", + "Lifestyle", + "SymptomManager", + "Epi", + } OPTIONAL_INIT_DEPENDENCIES = {"HealthBurden", "Hiv"} @@ -123,10 +131,9 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "none", "tb_tx_adult", "tb_tx_child", - "tb_tx_child_shorter", "tb_retx_adult", "tb_retx_child", - "tb_mdrtx" + "tb_mdrtx", ], description="current tb treatment regimen", ), @@ -269,13 +276,17 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): ), # ------------------ diagnostic tests ------------------ # "sens_xpert_smear_negative": Parameter( - Types.REAL, "sensitivity of Xpert test in smear negative TB cases"), + Types.REAL, "sensitivity of Xpert test in smear negative TB cases" + ), "sens_xpert_smear_positive": Parameter( - Types.REAL, "sensitivity of Xpert test in smear positive TB cases"), + Types.REAL, "sensitivity of Xpert test in smear positive TB cases" + ), "spec_xpert_smear_negative": Parameter( - Types.REAL, "specificity of Xpert test in smear negative TB cases"), + Types.REAL, "specificity of Xpert test in smear negative TB cases" + ), "spec_xpert_smear_positive": Parameter( - Types.REAL, "specificity of Xpert test in smear positive TB cases"), + Types.REAL, "specificity of Xpert test in smear positive TB cases" + ), "sens_sputum_smear_positive": Parameter( Types.REAL, "sensitivity of sputum smear microscopy in sputum positive cases", @@ -315,9 +326,6 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "prob_tx_success_5_14": Parameter( Types.REAL, "Probability of treatment success for children aged 5-14 years" ), - "prob_tx_success_shorter": Parameter( - Types.REAL, "Probability of treatment success for children aged <16 years on shorter regimen" - ), # ------------------ testing rates ------------------ # "rate_testing_general_pop": Parameter( Types.REAL, @@ -339,9 +347,6 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "mdr_treatment_length": Parameter( Types.REAL, "length of treatment for mdr-tb in months" ), - "child_shorter_treatment_length": Parameter( - Types.REAL, "length of treatment for shorter paediatric regimen in months" - ), "prob_retained_ipt_6_months": Parameter( Types.REAL, "probability of being retained on IPT every 6 months if still eligible", @@ -354,34 +359,19 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): Types.INT, "year from which IPT is available for paediatric contacts of diagnosed active TB cases", ), - "scenario": Parameter( - Types.INT, - "integer value labelling the scenario to be run: default is 0" - ), - "scenario_start_date": Parameter( - Types.DATE, - "date from which different scenarios are run" - ), "first_line_test": Parameter( - Types.STRING, - "name of first test to be used for TB diagnosis" + Types.STRING, "name of first test to be used for TB diagnosis" ), "second_line_test": Parameter( - Types.STRING, - "name of second test to be used for TB diagnosis" - ), - "probability_access_to_xray": Parameter( - Types.REAL, - "probability a person will have access to chest x-ray" + Types.STRING, "name of second test to be used for TB diagnosis" ), - "prob_tb_referral_in_generic_hsi": Parameter( - Types.REAL, - "probability of referral to TB screening HSI if presenting with TB-related symptoms" + "tb_healthseekingbehaviour_cap": Parameter( + Types.INT, + "number of repeat visits assumed for healthcare services", ), - # ------------------ scale-up parameters for scenario analysis ------------------ # - "scaleup_parameters": Parameter( - Types.DATA_FRAME, - "list of parameters and values changed in scenario analysis", + "data_end": Parameter( + Types.INT, + "last year for which data are available", ), } @@ -413,8 +403,6 @@ def read_parameters(self, data_folder): p["ipt_coverage"] = workbook["ipt_coverage"] - p["scaleup_parameters"] = workbook["scaleup_parameters"] - self.district_list = ( self.sim.modules["Demography"] .parameters["pop_2010"]["District"] @@ -473,94 +461,94 @@ def pre_initialise_population(self): Predictor("age_years").when("<=15", p["rr_tb_child"]), # -------------- LIFESTYLE -------------- # Predictor().when( - 'va_bcg_all_doses &' - '(hv_inf == False) &' - '(age_years <10)', - p["rr_tb_bcg"] # child with bcg + "va_bcg_all_doses &" "(hv_inf == False) &" "(age_years <10)", + p["rr_tb_bcg"], # child with bcg ), Predictor("li_bmi").when(">=4", p["rr_tb_obese"]), Predictor("li_ex_alc").when(True, p["rr_tb_alcohol"]), Predictor("li_tob").when(True, p["rr_tb_smoking"]), # -------------- IPT -------------- # Predictor().when( - '~hv_inf &' - 'tb_on_ipt & ' - 'age_years <= 15', - p["rr_ipt_child"]), # hiv- child on ipt + "~hv_inf &" "tb_on_ipt & " "age_years <= 15", p["rr_ipt_child"] + ), # hiv- child on ipt Predictor().when( - '~hv_inf &' - 'tb_on_ipt & ' - 'age_years > 15', - p["rr_ipt_adult"]), # hiv- adult on ipt + "~hv_inf &" "tb_on_ipt & " "age_years > 15", p["rr_ipt_adult"] + ), # hiv- adult on ipt # -------------- PLHIV -------------- # Predictor("hv_inf").when(True, p["rr_tb_hiv"]), Predictor("sy_aids_symptoms").when(">0", p["rr_tb_aids"]), # on ART, no IPT Predictor().when( - 'hv_inf & ' + "hv_inf & " '(hv_art == "on_VL_suppressed") &' - '~tb_on_ipt & ' - 'age_years <= 15', - p["rr_tb_art_child"]), # hiv+ child on ART + "~tb_on_ipt & " + "age_years <= 15", + p["rr_tb_art_child"], + ), # hiv+ child on ART Predictor().when( - 'hv_inf & ' + "hv_inf & " '(hv_art == "on_VL_suppressed") &' - '~tb_on_ipt & ' - 'age_years > 15', - p["rr_tb_art_adult"]), # hiv+ adult on ART + "~tb_on_ipt & " + "age_years > 15", + p["rr_tb_art_adult"], + ), # hiv+ adult on ART # on ART, on IPT Predictor().when( - 'tb_on_ipt & ' - 'hv_inf & ' - 'age_years <= 15 &' + "tb_on_ipt & " + "hv_inf & " + "age_years <= 15 &" '(hv_art == "on_VL_suppressed")', (p["rr_tb_art_child"] * p["rr_ipt_art_child"]), # hiv+ child on ART+IPT ), Predictor().when( - 'tb_on_ipt & ' - 'hv_inf & ' - 'age_years > 15 &' + "tb_on_ipt & " + "hv_inf & " + "age_years > 15 &" '(hv_art == "on_VL_suppressed")', (p["rr_tb_art_adult"] * p["rr_ipt_art_adult"]), # hiv+ adult on ART+IPT ), # not on ART, on IPT Predictor().when( - 'tb_on_ipt & ' - 'hv_inf & ' - 'age_years <= 15 &' + "tb_on_ipt & " + "hv_inf & " + "age_years <= 15 &" '(hv_art != "on_VL_suppressed")', p["rr_ipt_child_hiv"], # hiv+ child IPT only ), Predictor().when( - 'tb_on_ipt & ' - 'hv_inf & ' - 'age_years > 15 &' + "tb_on_ipt & " + "hv_inf & " + "age_years > 15 &" '(hv_art != "on_VL_suppressed")', p["rr_ipt_adult_hiv"], # hiv+ adult IPT only ), ] - conditional_predictors = [ - Predictor("nc_diabetes").when(True, p['rr_tb_diabetes1']), - ] if "cardio_metabolic_disorders" in self.sim.modules else [] + conditional_predictors = ( + [ + Predictor("nc_diabetes").when(True, p["rr_tb_diabetes1"]), + ] + if "cardio_metabolic_disorders" in self.sim.modules + else [] + ) self.lm["active_tb"] = LinearModel.multiplicative( - *(predictors + conditional_predictors)) + *(predictors + conditional_predictors) + ) # risk of relapse <2 years following treatment self.lm["risk_relapse_2yrs"] = LinearModel( LinearModelType.MULTIPLICATIVE, p["monthly_prob_relapse_tx_complete"], Predictor("hv_inf").when(True, p["rr_relapse_hiv"]), - Predictor("tb_treatment_failure") - .when(True, (p["monthly_prob_relapse_tx_incomplete"] / p["monthly_prob_relapse_tx_complete"])), - Predictor().when( - 'tb_on_ipt & ' - 'age_years <= 15', - p["rr_ipt_child"]), - Predictor().when( - 'tb_on_ipt & ' - 'age_years > 15', - p["rr_ipt_adult"]), + Predictor("tb_treatment_failure").when( + True, + ( + p["monthly_prob_relapse_tx_incomplete"] + / p["monthly_prob_relapse_tx_complete"] + ), + ), + Predictor().when("tb_on_ipt & " "age_years <= 15", p["rr_ipt_child"]), + Predictor().when("tb_on_ipt & " "age_years > 15", p["rr_ipt_adult"]), ) # risk of relapse if >=2 years post treatment @@ -568,14 +556,8 @@ def pre_initialise_population(self): LinearModelType.MULTIPLICATIVE, p["monthly_prob_relapse_2yrs"], Predictor("hv_inf").when(True, p["rr_relapse_hiv"]), - Predictor().when( - 'tb_on_ipt & ' - 'age_years <= 15', - p["rr_ipt_child"]), - Predictor().when( - 'tb_on_ipt & ' - 'age_years > 15', - p["rr_ipt_adult"]), + Predictor().when("tb_on_ipt & " "age_years <= 15", p["rr_ipt_child"]), + Predictor().when("tb_on_ipt & " "age_years > 15", p["rr_ipt_adult"]), ) # probability of death @@ -583,28 +565,23 @@ def pre_initialise_population(self): LinearModelType.MULTIPLICATIVE, 1, Predictor().when( - "(tb_on_treatment == True) & " - "(age_years <=4)", + "(tb_on_treatment == True) & " "(age_years <=4)", p["death_rate_child0_4_treated"], ), Predictor().when( - "(tb_on_treatment == True) & " - "(age_years <=14)", + "(tb_on_treatment == True) & " "(age_years <=14)", p["death_rate_child5_14_treated"], ), Predictor().when( - "(tb_on_treatment == True) & " - "(age_years >=15)", + "(tb_on_treatment == True) & " "(age_years >=15)", p["death_rate_adult_treated"], ), Predictor().when( - "(tb_on_treatment == False) & " - "(tb_smear == True)", + "(tb_on_treatment == False) & " "(tb_smear == True)", p["death_rate_smear_pos_untreated"], ), Predictor().when( - "(tb_on_treatment == False) & " - "(tb_smear == False)", + "(tb_on_treatment == False) & " "(tb_smear == False)", p["death_rate_smear_neg_untreated"], ), ) @@ -624,12 +601,14 @@ def send_for_screening_general(self, population): & ~df.tb_diagnosed & ~df.tb_on_treatment & (random_draw < p["rate_testing_general_pop"]) - ] + ] for person in screen_idx: self.sim.modules["HealthSystem"].schedule_hsi_event( HSI_Tb_ScreeningAndRefer(person_id=person, module=self), - topen=random_date(self.sim.date, self.sim.date + DateOffset(months=1), self.rng), + topen=random_date( + self.sim.date, self.sim.date + DateOffset(months=1), self.rng + ), tclose=None, priority=0, ) @@ -646,7 +625,11 @@ def select_tb_test(self, person_id): # assume sputum smear always available # previously diagnosed/treated or hiv+ -> xpert - if person["tb_ever_treated"] or person["hv_diagnosed"] or (p["first_line_test"] == 'xpert'): + if ( + person["tb_ever_treated"] + or person["hv_diagnosed"] + or (p["first_line_test"] == "xpert") + ): return "xpert" else: return "sputum" @@ -658,31 +641,33 @@ def get_consumables_for_dx_and_tx(self): # TB Sputum smear test # assume that if smear-positive, sputum smear test is 100% specific and sensitive - self.item_codes_for_consumables_required['sputum_test'] = \ + self.item_codes_for_consumables_required["sputum_test"] = ( hs.get_item_codes_from_package_name("Microscopy Test") + ) - self.sim.modules['HealthSystem'].dx_manager.register_dx_test( + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( tb_sputum_test_smear_positive=DxTest( - property='tb_inf', + property="tb_inf", target_categories=["active"], sensitivity=p["sens_sputum_smear_positive"], specificity=p["spec_sputum_smear_positive"], - item_codes=self.item_codes_for_consumables_required['sputum_test'] + item_codes=self.item_codes_for_consumables_required["sputum_test"], ) ) - self.sim.modules['HealthSystem'].dx_manager.register_dx_test( + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( tb_sputum_test_smear_negative=DxTest( - property='tb_inf', + property="tb_inf", target_categories=["active"], sensitivity=0.0, specificity=1.0, - item_codes=self.item_codes_for_consumables_required['sputum_test'] + item_codes=self.item_codes_for_consumables_required["sputum_test"], ) ) # TB GeneXpert - self.item_codes_for_consumables_required['xpert_test'] = \ + self.item_codes_for_consumables_required["xpert_test"] = ( hs.get_item_codes_from_package_name("Xpert test") + ) # sensitivity/specificity set for smear status of cases self.sim.modules["HealthSystem"].dx_manager.register_dx_test( @@ -691,7 +676,7 @@ def get_consumables_for_dx_and_tx(self): target_categories=["active"], sensitivity=p["sens_xpert_smear_positive"], specificity=p["spec_xpert_smear_positive"], - item_codes=self.item_codes_for_consumables_required['xpert_test'] + item_codes=self.item_codes_for_consumables_required["xpert_test"], ) ) self.sim.modules["HealthSystem"].dx_manager.register_dx_test( @@ -700,13 +685,14 @@ def get_consumables_for_dx_and_tx(self): target_categories=["active"], sensitivity=p["sens_xpert_smear_negative"], specificity=p["spec_xpert_smear_negative"], - item_codes=self.item_codes_for_consumables_required['xpert_test'] + item_codes=self.item_codes_for_consumables_required["xpert_test"], ) ) # TB Chest x-ray - self.item_codes_for_consumables_required['chest_xray'] = { - hs.get_item_code_from_item_name("X-ray"): 1} + self.item_codes_for_consumables_required["chest_xray"] = { + hs.get_item_code_from_item_name("X-ray"): 1 + } # sensitivity/specificity set for smear status of cases self.sim.modules["HealthSystem"].dx_manager.register_dx_test( @@ -715,7 +701,7 @@ def get_consumables_for_dx_and_tx(self): target_categories=["active"], sensitivity=p["sens_xray_smear_positive"], specificity=p["spec_xray_smear_positive"], - item_codes=self.item_codes_for_consumables_required['chest_xray'] + item_codes=self.item_codes_for_consumables_required["chest_xray"], ) ) self.sim.modules["HealthSystem"].dx_manager.register_dx_test( @@ -724,7 +710,7 @@ def get_consumables_for_dx_and_tx(self): target_categories=["active"], sensitivity=p["sens_xray_smear_negative"], specificity=p["spec_xray_smear_negative"], - item_codes=self.item_codes_for_consumables_required['chest_xray'] + item_codes=self.item_codes_for_consumables_required["chest_xray"], ) ) @@ -735,38 +721,40 @@ def get_consumables_for_dx_and_tx(self): target_categories=["active"], sensitivity=p["sens_clinical"], specificity=p["spec_clinical"], - item_codes=[] + item_codes=[], ) ) # 4) -------- Define the treatment options -------- # adult treatment - primary - self.item_codes_for_consumables_required['tb_tx_adult'] = \ + self.item_codes_for_consumables_required["tb_tx_adult"] = ( hs.get_item_code_from_item_name("Cat. I & III Patient Kit A") + ) # child treatment - primary - self.item_codes_for_consumables_required['tb_tx_child'] = \ - hs.get_item_code_from_item_name("Cat. I & III Patient Kit B") - - # child treatment - primary, shorter regimen - self.item_codes_for_consumables_required['tb_tx_child_shorter'] = \ + self.item_codes_for_consumables_required["tb_tx_child"] = ( hs.get_item_code_from_item_name("Cat. I & III Patient Kit B") + ) # adult treatment - secondary - self.item_codes_for_consumables_required['tb_retx_adult'] = \ + self.item_codes_for_consumables_required["tb_retx_adult"] = ( hs.get_item_code_from_item_name("Cat. II Patient Kit A1") + ) # child treatment - secondary - self.item_codes_for_consumables_required['tb_retx_child'] = \ + self.item_codes_for_consumables_required["tb_retx_child"] = ( hs.get_item_code_from_item_name("Cat. II Patient Kit A2") + ) # mdr treatment - self.item_codes_for_consumables_required['tb_mdrtx'] = { - hs.get_item_code_from_item_name("Treatment: second-line drugs"): 1} + self.item_codes_for_consumables_required["tb_mdrtx"] = { + hs.get_item_code_from_item_name("Treatment: second-line drugs"): 1 + } # ipt - self.item_codes_for_consumables_required['tb_ipt'] = { - hs.get_item_code_from_item_name("Isoniazid/Pyridoxine, tablet 300 mg"): 1} + self.item_codes_for_consumables_required["tb_ipt"] = { + hs.get_item_code_from_item_name("Isoniazid/Pyridoxine, tablet 300 mg"): 1 + } def initialise_population(self, population): @@ -812,21 +800,19 @@ def initialise_population(self, population): # WHO estimates of active TB for 2010 to get infected initial population # don't need to scale or include treated proportion as no-one on treatment yet inc_estimates = p["who_incidence_estimates"] - incidence_year = (inc_estimates.loc[ - (inc_estimates.year == self.sim.date.year), "incidence_per_100k" - ].values[0]) / 100_000 + incidence_year = ( + inc_estimates.loc[ + (inc_estimates.year == self.sim.date.year), "incidence_per_100k" + ].values[0] + ) / 100_000 incidence_year = incidence_year * p["scaling_factor_WHO"] - self.assign_active_tb( - population, - strain="ds", - incidence=incidence_year) + self.assign_active_tb(population, strain="ds", incidence=incidence_year) self.assign_active_tb( - population, - strain="mdr", - incidence=incidence_year * p['prop_mdr2010']) + population, strain="mdr", incidence=incidence_year * p["prop_mdr2010"] + ) self.send_for_screening_general( population @@ -841,16 +827,13 @@ def initialise_simulation(self, sim): # 1) Regular events sim.schedule_event(TbActiveEvent(self), sim.date) - sim.schedule_event(TbTreatmentAndRelapseEvents(self), sim.date) + sim.schedule_event(TbRegularEvents(self), sim.date) sim.schedule_event(TbSelfCureEvent(self), sim.date) sim.schedule_event(TbActiveCasePoll(self), sim.date + DateOffset(years=1)) - # log at the end of the year + # 2) log at the end of the year sim.schedule_event(TbLoggingEvent(self), sim.date + DateOffset(years=1)) - # 2) Scenario change - sim.schedule_event(ScenarioSetupEvent(self), self.parameters["scenario_start_date"]) - # 3) Define the DxTests and get the consumables required self.get_consumables_for_dx_and_tx() @@ -900,7 +883,9 @@ def on_birth(self, mother_id, child_id): # Not interested in whether true or direct birth # give IPT to child of TB diagnosed mother if 2014 or later - if df.at[abs(mother_id), "tb_diagnosed"] and (now.year >= self.parameters["ipt_start_date"]): + if df.at[abs(mother_id), "tb_diagnosed"] and ( + now.year >= self.parameters["ipt_start_date"] + ): event = HSI_Tb_Start_or_Continue_Ipt(self, person_id=child_id) self.sim.modules["HealthSystem"].schedule_hsi_event( event, @@ -924,29 +909,21 @@ def report_daly_values(self): # hiv-negative health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "ds") - & ~df_tmp.hv_inf - ] = self.daly_wts["daly_tb"] + (df_tmp.tb_inf == "active") & (df_tmp.tb_strain == "ds") & ~df_tmp.hv_inf + ] = self.daly_wts["daly_tb"] health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "mdr") - & ~df_tmp.hv_inf - ] = self.daly_wts["daly_tb"] + (df_tmp.tb_inf == "active") & (df_tmp.tb_strain == "mdr") & ~df_tmp.hv_inf + ] = self.daly_wts["daly_tb"] # hiv-positive health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "ds") - & df_tmp.hv_inf - ] = self.daly_wts["daly_tb_hiv"] + (df_tmp.tb_inf == "active") & (df_tmp.tb_strain == "ds") & df_tmp.hv_inf + ] = self.daly_wts["daly_tb_hiv"] health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "mdr") - & df_tmp.hv_inf - ] = self.daly_wts["daly_mdr_tb_hiv"] + (df_tmp.tb_inf == "active") & (df_tmp.tb_strain == "mdr") & df_tmp.hv_inf + ] = self.daly_wts["daly_mdr_tb_hiv"] return health_values.loc[df.is_alive] @@ -959,25 +936,37 @@ def calculate_untreated_proportion(self, population, strain): df = population.props # sum active tb cases - num_active_tb_cases = len(df[(df.tb_inf == "active") & - (df.tb_strain == strain) & - df.is_alive]) + num_active_tb_cases = len( + df[(df.tb_inf == "active") & (df.tb_strain == strain) & df.is_alive] + ) # sum treated active tb cases # if mdr-tb must be on mdr treatment, otherwise consider as untreated case if strain == "mdr": - num_treated_tb_cases = len(df[(df.tb_inf == "active") & - (df.tb_strain == strain) & - df.tb_on_treatment & - (df.tb_treatment_regimen == "tb_mdrtx") & - df.is_alive]) + num_treated_tb_cases = len( + df[ + (df.tb_inf == "active") + & (df.tb_strain == strain) + & df.tb_on_treatment + & (df.tb_treatment_regimen == "tb_mdrtx") + & df.is_alive + ] + ) else: - num_treated_tb_cases = len(df[(df.tb_inf == "active") & - (df.tb_strain == strain) & - df.tb_on_treatment & - df.is_alive]) + num_treated_tb_cases = len( + df[ + (df.tb_inf == "active") + & (df.tb_strain == strain) + & df.tb_on_treatment + & df.is_alive + ] + ) - prop_untreated = 1 - (num_treated_tb_cases / num_active_tb_cases) if num_active_tb_cases else 1 + prop_untreated = ( + 1 - (num_treated_tb_cases / num_active_tb_cases) + if num_active_tb_cases + else 1 + ) return prop_untreated @@ -994,33 +983,24 @@ def assign_active_tb(self, population, strain, incidence): now = self.sim.date # identify eligible people, not currently with active tb infection - eligible = df.loc[ - df.is_alive - & (df.tb_inf != "active") - ].index + eligible = df.loc[df.is_alive & (df.tb_inf != "active")].index # weight risk by individual characteristics # Compute chance that each susceptible person becomes infected: - rr_of_infection = self.lm["active_tb"].predict( - df.loc[eligible] - ) + rr_of_infection = self.lm["active_tb"].predict(df.loc[eligible]) # probability of infection - p_infection = (rr_of_infection * incidence) + p_infection = rr_of_infection * incidence # New infections: - will_be_infected = ( - self.rng.random_sample(len(p_infection)) < p_infection - ) + will_be_infected = self.rng.random_sample(len(p_infection)) < p_infection idx_new_infection = will_be_infected[will_be_infected].index df.loc[idx_new_infection, "tb_strain"] = strain # schedule onset of active tb, time now up to 1 year for person_id in idx_new_infection: - date_progression = now + pd.DateOffset( - days=rng.randint(0, 365) - ) + date_progression = now + pd.DateOffset(days=rng.randint(0, 365)) # set date of active tb - properties will be updated at TbActiveEvent poll daily df.at[person_id, "tb_scheduled_date_active"] = date_progression @@ -1076,10 +1056,12 @@ def relapse_event(self, population): # risk of relapse if <2 years post treatment start, includes risk if HIV+ risk_of_relapse_early = self.lm["risk_relapse_2yrs"].predict( - df.loc[df.is_alive - & df.tb_ever_treated - & (df.tb_inf == "latent") - & (now < (df.tb_date_treated + pd.DateOffset(years=2)))] + df.loc[ + df.is_alive + & df.tb_ever_treated + & (df.tb_inf == "latent") + & (now < (df.tb_date_treated + pd.DateOffset(years=2))) + ] ) will_relapse = ( @@ -1089,10 +1071,12 @@ def relapse_event(self, population): # risk of relapse if >=2 years post treatment start, includes risk if HIV+ risk_of_relapse_later = self.lm["risk_relapse_late"].predict( - df.loc[df.is_alive - & df.tb_ever_treated - & (df.tb_inf == "latent") - & (now >= (df.tb_date_treated + pd.DateOffset(years=2)))] + df.loc[ + df.is_alive + & df.tb_ever_treated + & (df.tb_inf == "latent") + & (now >= (df.tb_date_treated + pd.DateOffset(years=2))) + ] ) will_relapse_later = ( @@ -1111,10 +1095,10 @@ def relapse_event(self, population): def end_treatment(self, population): """ - * check for those eligible to finish treatment - * sample for treatment failure and refer for follow-up screening/testing - * if treatment has finished, change individual properties - """ + * check for those eligible to finish treatment + * sample for treatment failure and refer for follow-up screening/testing + * if treatment has finished, change individual properties + """ df = population.props rng = self.rng @@ -1129,19 +1113,25 @@ def end_treatment(self, population): end_ds_tx_idx = df.loc[ df.is_alive & df.tb_on_treatment - & ((df.tb_treatment_regimen == "tb_tx_adult") | (df.tb_treatment_regimen == "tb_tx_child")) + & ( + (df.tb_treatment_regimen == "tb_tx_adult") + | (df.tb_treatment_regimen == "tb_tx_child") + ) & ( now > (df.tb_date_treated + pd.DateOffset(months=p["ds_treatment_length"])) ) - ].index + ].index # ---------------------- treatment end: retreatment ds-tb (7 months) ---------------------- # # end treatment for retreatment cases end_ds_retx_idx = df.loc[ df.is_alive & df.tb_on_treatment - & ((df.tb_treatment_regimen == "tb_retx_adult") | (df.tb_treatment_regimen == "tb_retx_child")) + & ( + (df.tb_treatment_regimen == "tb_retx_adult") + | (df.tb_treatment_regimen == "tb_retx_child") + ) & ( now > ( @@ -1149,7 +1139,7 @@ def end_treatment(self, population): + pd.DateOffset(months=p["ds_retreatment_length"]) ) ) - ].index + ].index # ---------------------- treatment end: mdr-tb (24 months) ---------------------- # # end treatment for mdr-tb cases @@ -1161,24 +1151,11 @@ def end_treatment(self, population): now > (df.tb_date_treated + pd.DateOffset(months=p["mdr_treatment_length"])) ) - ].index - - # ---------------------- treatment end: shorter paediatric regimen ---------------------- # - # end treatment for paediatric cases on 4 month regimen - end_tx_shorter_idx = df.loc[ - df.is_alive - & df.tb_on_treatment - & (df.tb_treatment_regimen == "tb_tx_child_shorter") - & ( - now - > (df.tb_date_treated + pd.DateOffset(months=p["child_shorter_treatment_length"])) - ) - ].index + ].index # join indices end_tx_idx = end_ds_tx_idx.union(end_ds_retx_idx) end_tx_idx = end_tx_idx.union(end_mdr_tx_idx) - end_tx_idx = end_tx_idx.union(end_tx_shorter_idx) # ---------------------- treatment failure ---------------------- # # sample some to have treatment failure @@ -1190,42 +1167,33 @@ def end_treatment(self, population): (df.index.isin(end_ds_tx_idx)) & (df.age_years < 5) & (random_var < (1 - p["prob_tx_success_0_4"])) - ].index + ].index # children aged 5-14 ds-tb ds_tx_failure5_14_idx = df.loc[ (df.index.isin(end_ds_tx_idx)) & (df.age_years.between(5, 14)) & (random_var < (1 - p["prob_tx_success_5_14"])) - ].index - - # children aged <16 and on shorter regimen - ds_tx_failure_shorter_idx = df.loc[ - (df.index.isin(end_tx_shorter_idx)) - & (df.age_years < 16) - & (random_var < (1 - p["prob_tx_success_shorter"])) - ].index + ].index # adults ds-tb ds_tx_failure_adult_idx = df.loc[ (df.index.isin(end_ds_tx_idx)) & (df.age_years >= 15) & (random_var < (1 - p["prob_tx_success_ds"])) - ].index + ].index # all mdr cases on ds tx will fail failure_in_mdr_with_ds_tx_idx = df.loc[ - (df.index.isin(end_ds_tx_idx)) - & (df.tb_strain == "mdr") - ].index + (df.index.isin(end_ds_tx_idx)) & (df.tb_strain == "mdr") + ].index # some mdr cases on mdr treatment will fail failure_due_to_mdr_idx = df.loc[ (df.index.isin(end_mdr_tx_idx)) & (df.tb_strain == "mdr") & (random_var < (1 - p["prob_tx_success_mdr"])) - - ].index + ].index # join indices of failing cases together tx_failure = reduce( @@ -1233,22 +1201,21 @@ def end_treatment(self, population): ( ds_tx_failure0_4_idx, ds_tx_failure5_14_idx, - ds_tx_failure_shorter_idx, ds_tx_failure_adult_idx, failure_in_mdr_with_ds_tx_idx, failure_due_to_mdr_idx, - ) + ), ) if not tx_failure.empty: df.loc[tx_failure, "tb_treatment_failure"] = True - df.loc[ - tx_failure, "tb_ever_treated" - ] = True # ensure classed as retreatment case + df.loc[tx_failure, "tb_ever_treated"] = ( + True # ensure classed as retreatment case + ) for person in tx_failure: self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Tb_ScreeningAndRefer(person_id=person, module=self), + HSI_Tb_ScreeningAndRefer(person_id=person, module=self, facility_level='2'), topen=self.sim.date, tclose=None, priority=0, @@ -1278,11 +1245,7 @@ def end_treatment(self, population): # if HIV+ and on ART (virally suppressed), remove AIDS symptoms if cured of TB hiv_tb_infected = cure_idx.intersection( - df.loc[ - df.is_alive - & df.hv_inf - & (df.hv_art == "on_VL_suppressed") - ].index + df.loc[df.is_alive & df.hv_inf & (df.hv_art == "on_VL_suppressed")].index ) self.sim.modules["SymptomManager"].clear_symptoms( @@ -1315,7 +1278,8 @@ def is_subset(col_for_set, col_for_subset): # Check that the core TB properties are 'nested' in the way expected. assert is_subset( - col_for_set=(df_alive.tb_inf != "uninfected"), col_for_subset=df_alive.tb_diagnosed + col_for_set=(df_alive.tb_inf != "uninfected"), + col_for_subset=df_alive.tb_diagnosed, ) assert is_subset( col_for_set=df_alive.tb_diagnosed, col_for_subset=df_alive.tb_on_treatment @@ -1328,118 +1292,6 @@ def is_subset(col_for_set, col_for_subset): # # --------------------------------------------------------------------------- # # TB infection event # # --------------------------------------------------------------------------- -class ScenarioSetupEvent(RegularEvent, PopulationScopeEventMixin): - """ This event exists to change parameters or functions - depending on the scenario for projections which has been set - * scenario 0 is the default which uses baseline parameters - * scenario 1 achieves all program targets with consumables constraints - * scenario 2 achieves all program targets without consumables constraints - It only occurs once at param: scenario_start_date, - called by initialise_simulation - """ - - def __init__(self, module): - super().__init__(module, frequency=DateOffset(years=100)) - - def apply(self, population): - - p = self.module.parameters - scenario = p["scenario"] - scaled_params = p["scaleup_parameters"] - - logger.debug( - key="message", data=f"ScenarioSetupEvent: scenario {scenario}" - ) - - # baseline scenario 0: no change to parameters/functions - if scenario == 0: - return - - # scenario 1 or 2 scale-up all HIV/TB program activities - if scenario > 0: - # HIV - # reduce risk of HIV - applies to whole adult population - self.sim.modules["Hiv"].parameters["beta"] = self.sim.modules["Hiv"].parameters["beta"] * scaled_params.loc[ - scaled_params.parameter == "hiv_beta", "value"].values[0] - - # increase PrEP coverage for FSW after HIV test - self.sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] = scaled_params.loc[ - scaled_params.parameter == "prob_prep_for_fsw_after_hiv_test", "value"].values[0] - - # prep poll for AGYW - target to the highest risk - # increase retention to 75% for FSW and AGYW - self.sim.modules["Hiv"].parameters["prob_prep_for_agyw"] = scaled_params.loc[ - scaled_params.parameter == "prob_prep_for_agyw", "value"].values[0] - self.sim.modules["Hiv"].parameters[ - "probability_of_being_retained_on_prep_every_3_months"] = scaled_params.loc[ - scaled_params.parameter == "probability_of_being_retained_on_prep_every_3_months", "value"].values[0] - - # increase probability of VMMC after hiv test - self.sim.modules["Hiv"].parameters["prob_circ_after_hiv_test"] = scaled_params.loc[ - scaled_params.parameter == "prob_circ_after_hiv_test", "value"].values[0] - - # increase testing/diagnosis rates, default 2020 0.03/0.25 -> 93% dx - self.sim.modules["Hiv"].parameters["hiv_testing_rates"]["annual_testing_rate_children"] = scaled_params.loc[ - scaled_params.parameter == "annual_testing_rate_children", "value"].values[0] - self.sim.modules["Hiv"].parameters["hiv_testing_rates"]["annual_testing_rate_adults"] = scaled_params.loc[ - scaled_params.parameter == "annual_testing_rate_adults", "value"].values[0] - - # ANC testing - value for mothers and infants testing - self.sim.modules["Hiv"].parameters["prob_hiv_test_at_anc_or_delivery"] = scaled_params.loc[ - scaled_params.parameter == "prob_hiv_test_at_anc_or_delivery", "value"].values[0] - self.sim.modules["Hiv"].parameters["prob_hiv_test_for_newborn_infant"] = scaled_params.loc[ - scaled_params.parameter == "prob_hiv_test_for_newborn_infant", "value"].values[0] - - # prob ART start if dx, this is already 95% at 2020 - # self.sim.modules["Hiv"].parameters["prob_start_art_after_hiv_test"] = scaled_params.loc[ - # scaled_params.parameter == - # "prob_start_art_after_hiv_test", "value"].values[0] - - # viral suppression rates - # adults already at 95% by 2020 - # change all column values - self.sim.modules["Hiv"].parameters["prob_start_art_or_vs"]["virally_suppressed_on_art"] = scaled_params.loc[ - scaled_params.parameter == "virally_suppressed_on_art", "value"].values[0] - - # TB - # use NTP treatment rates - self.sim.modules["Tb"].parameters["rate_testing_active_tb"]["treatment_coverage"] = scaled_params.loc[ - scaled_params.parameter == "tb_treatment_coverage", "value"].values[0] - - # increase tb treatment success rates - self.sim.modules["Tb"].parameters["prob_tx_success_ds"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_ds", "value"].values[0] - self.sim.modules["Tb"].parameters["prob_tx_success_mdr"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_mdr", "value"].values[0] - self.sim.modules["Tb"].parameters["prob_tx_success_0_4"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_0_4", "value"].values[0] - self.sim.modules["Tb"].parameters["prob_tx_success_5_14"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_5_14", "value"].values[0] - self.sim.modules["Tb"].parameters["prob_tx_success_shorter"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_shorter", "value"].values[0] - - # change first-line testing for TB to xpert - p["first_line_test"] = scaled_params.loc[ - scaled_params.parameter == "first_line_test", "value"].values[0] - p["second_line_test"] = scaled_params.loc[ - scaled_params.parameter == "second_line_test", "value"].values[0] - - # increase coverage of IPT - p["ipt_coverage"]["coverage_plhiv"] = scaled_params.loc[ - scaled_params.parameter == "ipt_coverage_plhiv", "value"].values[0] - p["ipt_coverage"]["coverage_paediatric"] = scaled_params.loc[ - scaled_params.parameter == "ipt_coverage_paediatric", "value"].values[0] - - # remove consumables constraints, all cons available - if scenario == 2: - # list only things that change: constraints on consumables - new_parameters = { - 'cons_availability': 'all', - } - self.sim.schedule_event( - HealthSystemChangeParameters( - self.sim.modules['HealthSystem'], parameters=new_parameters), - self.sim.date) class TbActiveCasePoll(RegularEvent, PopulationScopeEventMixin): @@ -1454,30 +1306,46 @@ def __init__(self, module): def apply(self, population): p = self.module.parameters - inc_estimates = p["who_incidence_estimates"] - incidence_year = (inc_estimates.loc[ - (inc_estimates.year == self.sim.date.year), "incidence_per_100k" - ].values[0]) / 100000 - prop_untreated_ds = self.module.calculate_untreated_proportion(population, strain="ds") - prop_untreated_mdr = self.module.calculate_untreated_proportion(population, strain="mdr") + current_year = min(self.sim.date.year, p["data_end"]) + + inc_estimates = p["who_incidence_estimates"] + incidence_year = ( + inc_estimates.loc[ + (inc_estimates.year == current_year), "incidence_per_100k" + ].values[0] + ) / 100000 + + prop_untreated_ds = self.module.calculate_untreated_proportion( + population, strain="ds" + ) + prop_untreated_mdr = self.module.calculate_untreated_proportion( + population, strain="mdr" + ) - scaled_incidence_ds = incidence_year * \ - p["scaling_factor_WHO"] * prop_untreated_ds - scaled_incidence_mdr = incidence_year * \ - p["prop_mdr2010"] * \ - p["scaling_factor_WHO"] * \ - prop_untreated_mdr + scaled_incidence_ds = ( + incidence_year * p["scaling_factor_WHO"] * prop_untreated_ds + ) + scaled_incidence_mdr = ( + incidence_year + * p["prop_mdr2010"] + * p["scaling_factor_WHO"] + * prop_untreated_mdr + ) # transmission ds-tb - self.module.assign_active_tb(population, strain="ds", incidence=scaled_incidence_ds) + self.module.assign_active_tb( + population, strain="ds", incidence=scaled_incidence_ds + ) # transmission mdr-tb, around 1% of total tb incidence - self.module.assign_active_tb(population, strain="mdr", incidence=scaled_incidence_mdr) + self.module.assign_active_tb( + population, strain="mdr", incidence=scaled_incidence_mdr + ) -class TbTreatmentAndRelapseEvents(RegularEvent, PopulationScopeEventMixin): - """ This event runs each month and calls three functions: +class TbRegularEvents(RegularEvent, PopulationScopeEventMixin): + """This event runs each month and calls three functions: * scheduling TB screening for the general population * ending treatment if end of treatment regimen has been reached * determining who will relapse after a primary infection @@ -1525,7 +1393,7 @@ def apply(self, population): & (df.tb_scheduled_date_active >= now) & ~df.tb_on_ipt & ~df.tb_on_treatment - ].index + ].index if active_idx.empty: return @@ -1545,10 +1413,9 @@ def apply(self, population): ) # -------- 3) if HIV+ assign smear status and schedule AIDS onset -------- - active_and_hiv = df.loc[ - (df.index.isin(active_idx) & df.hv_inf)].index + active_and_hiv = df.loc[(df.index.isin(active_idx) & df.hv_inf)].index - # higher probability of being smear positive than HIV- + # lower probability of being smear positive than HIV- smear_pos = ( rng.random_sample(len(active_and_hiv)) < p["prop_smear_positive_hiv"] ) @@ -1567,10 +1434,12 @@ def apply(self, population): # if Hiv not registered, give HIV+ person same time to death as HIV- for person_id in active_and_hiv: date_of_tb_death = self.sim.date + pd.DateOffset( - months=int(rng.uniform(low=1, high=6)) + months=int(rng.uniform(low=1, high=5)) ) self.sim.schedule_event( - event=TbDeathEvent(person_id=person_id, module=self.module, cause="AIDS_TB"), + event=TbDecideDeathEvent( + person_id=person_id, module=self.module, cause="AIDS_TB" + ), date=date_of_tb_death, ) @@ -1585,7 +1454,9 @@ def apply(self, population): months=int(rng.uniform(low=1, high=6)) ) self.sim.schedule_event( - event=TbDeathEvent(person_id=person_id, module=self.module, cause="TB"), + event=TbDecideDeathEvent( + person_id=person_id, module=self.module, cause="TB" + ), date=date_of_tb_death, ) @@ -1596,11 +1467,12 @@ def apply(self, population): active_testing_rates = p["rate_testing_active_tb"] # change to NTP testing rates - current_active_testing_rate = active_testing_rates.loc[ - ( - active_testing_rates.year == year), - "treatment_coverage"].values[ - 0] / 100 + current_active_testing_rate = ( + active_testing_rates.loc[ + (active_testing_rates.year == year), "treatment_coverage" + ].values[0] + / 100 + ) # multiply testing rate by average treatment availability to match treatment coverage current_active_testing_rate = current_active_testing_rate * (1 / 0.6) @@ -1611,7 +1483,8 @@ def apply(self, population): # would only be screened if have symptoms for >= 14 days # sample some of active_idx to go for screening screen_active_idx = df.loc[ - (df.index.isin(active_idx) & (random_draw < current_active_testing_rate))].index + (df.index.isin(active_idx) & (random_draw < current_active_testing_rate)) + ].index # TB screening checks for symptoms lasting at least 14 days, so add delay for person in screen_active_idx: @@ -1653,7 +1526,7 @@ def apply(self, population): & ~df.hv_inf & (df.tb_date_active < now) & (random_draw < prob_self_cure) - ].index + ].index # hiv-positive, on art and virally suppressed self_cure_art = df.loc[ @@ -1663,7 +1536,7 @@ def apply(self, population): & (df.hv_art == "on_VL_suppressed") & (df.tb_date_active < now) & (random_draw < prob_self_cure) - ].index + ].index # resolve symptoms and change properties all_self_cure = [*self_cure, *self_cure_art] @@ -1711,16 +1584,19 @@ class HSI_Tb_ScreeningAndRefer(HSI_Event, IndividualScopeEventMixin): * give IPT for paediatric contacts of diagnosed case """ - def __init__(self, module, person_id, suppress_footprint=False): + def __init__( + self, module, person_id, suppress_footprint=False, facility_level="1a" + ): super().__init__(module, person_id=person_id) assert isinstance(module, Tb) + self.facility_level = facility_level assert isinstance(suppress_footprint, bool) self.suppress_footprint = suppress_footprint self.TREATMENT_ID = "Tb_Test_Screening" self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"Over5OPD": 1}) - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.ACCEPTED_FACILITY_LEVEL = "1a" if (self.facility_level == "1a") else "2" def apply(self, person_id, squeeze_factor): """Do the screening and referring to next tests""" @@ -1740,8 +1616,8 @@ def apply(self, person_id, squeeze_factor): smear_status = person["tb_smear"] - # If the person is already on treatment and not failing, do nothing do not occupy any resources - if person["tb_on_treatment"] and not person["tb_treatment_failure"]: + # If the person is already on treatment, do nothing do not occupy any resources + if person["tb_on_treatment"]: return self.sim.modules["HealthSystem"].get_blank_appt_footprint() # ------------------------- screening ------------------------- # @@ -1760,22 +1636,26 @@ def apply(self, person_id, squeeze_factor): # refer for HIV testing: all ages # do not run if already HIV diagnosed or had test in last week - if not person["hv_diagnosed"] or (person["hv_last_test_date"] >= (now - DateOffset(days=7))): + if not person["hv_diagnosed"] or ( + person["hv_last_test_date"] >= (now - DateOffset(days=7)) + ): self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=hiv.HSI_Hiv_TestAndRefer( - person_id=person_id, module=self.sim.modules["Hiv"], referred_from='Tb' + person_id=person_id, + module=self.sim.modules["Hiv"], + referred_from="Tb", ), priority=1, topen=now, tclose=None, ) + # ------------------------- x-ray for children ------------------------- # + # child under 5 -> chest x-ray, but access is limited # if xray not available, HSI_Tb_Xray_level1b will refer if person["age_years"] < 5: - ACTUAL_APPT_FOOTPRINT = self.make_appt_footprint( - {"Under5OPD": 1} - ) + ACTUAL_APPT_FOOTPRINT = self.make_appt_footprint({"Under5OPD": 1}) # this HSI will choose relevant sensitivity/specificity depending on person's smear status self.sim.modules["HealthSystem"].schedule_hsi_event( @@ -1786,6 +1666,8 @@ def apply(self, person_id, squeeze_factor): ) test_result = False # to avoid calling a clinical diagnosis + # ------------------------- select test for adults ------------------------- # + # for all presumptive cases over 5 years of age else: # this selects a test for the person @@ -1800,35 +1682,58 @@ def apply(self, person_id, squeeze_factor): # relevant test depends on smear status (changes parameters on sensitivity/specificity if smear_status: - test_result = self.sim.modules["HealthSystem"].dx_manager.run_dx_test( + test_result = self.sim.modules[ + "HealthSystem" + ].dx_manager.run_dx_test( dx_tests_to_run="tb_sputum_test_smear_positive", hsi_event=self ) else: # if smear-negative, sputum smear should always return negative # run the dx test to log the consumable - test_result = self.sim.modules["HealthSystem"].dx_manager.run_dx_test( + test_result = self.sim.modules[ + "HealthSystem" + ].dx_manager.run_dx_test( dx_tests_to_run="tb_sputum_test_smear_negative", hsi_event=self ) # if negative, check for presence of all symptoms (clinical diagnosis) if all(x in self.module.symptom_list for x in persons_symptoms): - test_result = self.sim.modules["HealthSystem"].dx_manager.run_dx_test( + test_result = self.sim.modules[ + "HealthSystem" + ].dx_manager.run_dx_test( dx_tests_to_run="tb_clinical", hsi_event=self ) elif test == "xpert": - ACTUAL_APPT_FOOTPRINT = self.make_appt_footprint( - {"Over5OPD": 1} - ) - # relevant test depends on smear status (changes parameters on sensitivity/specificity - if smear_status: - test_result = self.sim.modules["HealthSystem"].dx_manager.run_dx_test( - dx_tests_to_run="tb_xpert_test_smear_positive", hsi_event=self - ) - # for smear-negative people - else: - test_result = self.sim.modules["HealthSystem"].dx_manager.run_dx_test( - dx_tests_to_run="tb_xpert_test_smear_negative", hsi_event=self + + # this can only be performed at level 1b/2, refer if necessary + if self.facility_level == "1a": + self.sim.modules["HealthSystem"].schedule_hsi_event( + hsi_event=HSI_Tb_ScreeningAndRefer( + person_id=person_id, module=self.module, facility_level="2" + ), + topen=self.sim.date + DateOffset(days=1), + tclose=None, + priority=0, ) + return self.make_appt_footprint({"Over5OPD": 1}) + + elif self.facility_level != "1a": + # relevant test depends on smear status (changes parameters on sensitivity/specificity + if smear_status: + test_result = self.sim.modules[ + "HealthSystem" + ].dx_manager.run_dx_test( + dx_tests_to_run="tb_xpert_test_smear_positive", + hsi_event=self, + ) + # for smear-negative people + else: + test_result = self.sim.modules[ + "HealthSystem" + ].dx_manager.run_dx_test( + dx_tests_to_run="tb_xpert_test_smear_negative", + hsi_event=self, + ) # ------------------------- testing referrals ------------------------- # @@ -1900,14 +1805,12 @@ def apply(self, person_id, squeeze_factor): & ~df.tb_diagnosed & df.is_alive & (df.district_of_residence == district) - ].index + ].index if ipt_eligible.any(): # select persons at highest risk of tb - rr_of_tb = self.module.lm["active_tb"].predict( - df.loc[ipt_eligible] - ) + rr_of_tb = self.module.lm["active_tb"].predict(df.loc[ipt_eligible]) # choose top 5 highest risk contacts ipt_sample = rr_of_tb.sort_values(ascending=False).head(5).index @@ -1955,10 +1858,10 @@ def __init__(self, module, person_id, suppress_footprint=False): self.TREATMENT_ID = "Tb_Test_Clinical" self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"Under5OPD": 0.5}) - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.ACCEPTED_FACILITY_LEVEL = "1a" def apply(self, person_id, squeeze_factor): - """ Do the screening and referring process """ + """Do the screening and referring process""" df = self.sim.population.props now = self.sim.date @@ -1998,7 +1901,9 @@ def apply(self, person_id, squeeze_factor): ) self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Tb_StartTreatment(person_id=person_id, module=self.module), + HSI_Tb_StartTreatment( + person_id=person_id, module=self.module, facility_level="1a" + ), topen=now, tclose=None, priority=0, @@ -2022,7 +1927,7 @@ def __init__(self, module, person_id, suppress_footprint=False): self.TREATMENT_ID = "Tb_Test_Xray" self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"DiagRadio": 1}) - self.ACCEPTED_FACILITY_LEVEL = '1b' + self.ACCEPTED_FACILITY_LEVEL = "1b" def apply(self, person_id, squeeze_factor): @@ -2064,7 +1969,9 @@ def apply(self, person_id, squeeze_factor): df.at[person_id, "tb_date_diagnosed"] = self.sim.date self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Tb_StartTreatment(person_id=person_id, module=self.module), + HSI_Tb_StartTreatment( + person_id=person_id, module=self.module, facility_level="1a" + ), topen=self.sim.date, tclose=None, priority=0, @@ -2093,7 +2000,7 @@ def __init__(self, module, person_id, suppress_footprint=False): self.TREATMENT_ID = "Tb_Test_Xray" self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"DiagRadio": 1}) - self.ACCEPTED_FACILITY_LEVEL = '2' + self.ACCEPTED_FACILITY_LEVEL = "2" def apply(self, person_id, squeeze_factor): @@ -2119,7 +2026,6 @@ def apply(self, person_id, squeeze_factor): # if consumables not available, rely on clinical diagnosis # return blank footprint as xray was not available if test_result is None: - ACTUAL_APPT_FOOTPRINT = self.make_appt_footprint({}) self.sim.modules["HealthSystem"].schedule_hsi_event( @@ -2135,7 +2041,9 @@ def apply(self, person_id, squeeze_factor): df.at[person_id, "tb_date_diagnosed"] = self.sim.date self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Tb_StartTreatment(person_id=person_id, module=self.module), + HSI_Tb_StartTreatment( + person_id=person_id, module=self.module, facility_level="1a" + ), topen=self.sim.date, tclose=None, priority=0, @@ -2156,13 +2064,15 @@ def apply(self, person_id, squeeze_factor): class HSI_Tb_StartTreatment(HSI_Event, IndividualScopeEventMixin): - def __init__(self, module, person_id): + def __init__(self, module, person_id, facility_level="1a"): super().__init__(module, person_id=person_id) assert isinstance(module, Tb) + self.facility_level = facility_level + self.TREATMENT_ID = "Tb_Treatment" - self.ACCEPTED_FACILITY_LEVEL = '1a' self.number_of_occurrences = 0 + self.ACCEPTED_FACILITY_LEVEL = "1a" if (self.facility_level == "1a") else "2" @property def EXPECTED_APPT_FOOTPRINT(self): @@ -2170,9 +2080,9 @@ def EXPECTED_APPT_FOOTPRINT(self): Return the expected appt footprint based on whether the HSI has been rescheduled due to unavailable treatment. """ if self.number_of_occurrences == 0: - return self.make_appt_footprint({'TBNew': 1}) + return self.make_appt_footprint({"TBNew": 1}) else: - return self.make_appt_footprint({'PharmDispensing': 1}) + return self.make_appt_footprint({"PharmDispensing": 1}) def apply(self, person_id, squeeze_factor): """This is a Health System Interaction Event - start TB treatment @@ -2193,9 +2103,23 @@ def apply(self, person_id, squeeze_factor): treatment_regimen = self.select_treatment(person_id) treatment_available = self.get_consumables( - item_codes=self.module.item_codes_for_consumables_required[treatment_regimen] + item_codes=self.module.item_codes_for_consumables_required[ + treatment_regimen + ] ) + # if require MDR treatment, and not currently at level 2, refer to level 2 + if (treatment_regimen == "tb_mdrtx") and (self.facility_level != "2"): + self.sim.modules["HealthSystem"].schedule_hsi_event( + hsi_event=HSI_Tb_StartTreatment( + person_id=person_id, module=self.module, facility_level="2" + ), + topen=self.sim.date + DateOffset(days=1), + tclose=None, + priority=0, + ) + return self.sim.modules["HealthSystem"].get_blank_appt_footprint() + if treatment_available: # start person on tb treatment - update properties df.at[person_id, "tb_on_treatment"] = True @@ -2207,16 +2131,15 @@ def apply(self, person_id, squeeze_factor): df.at[person_id, "tb_date_treated_mdr"] = now # schedule first follow-up appointment - follow_up_date = self.sim.date + DateOffset(months=1) logger.debug( key="message", data=f"HSI_Tb_StartTreatment: scheduling first follow-up " - f"for person {person_id} on {follow_up_date}", + f"for person {person_id}", ) self.sim.modules["HealthSystem"].schedule_hsi_event( HSI_Tb_FollowUp(person_id=person_id, module=self.module), - topen=follow_up_date, + topen=self.sim.date + DateOffset(months=1), tclose=None, priority=0, ) @@ -2224,17 +2147,20 @@ def apply(self, person_id, squeeze_factor): # if treatment not available, return for treatment start in 1 week # cap repeated visits at 5 else: - if self.number_of_occurrences <= 5: + + if ( + self.number_of_occurrences + <= self.module.parameters["tb_healthseekingbehaviour_cap"] + ): + print(self.number_of_occurrences) + self.sim.modules["HealthSystem"].schedule_hsi_event( - hsi_event=self, + self, topen=self.sim.date + DateOffset(weeks=1), tclose=None, priority=0, ) - def post_apply_hook(self): - self.number_of_occurrences += 1 - def select_treatment(self, person_id): """ helper function to select appropriate treatment and check whether @@ -2278,15 +2204,6 @@ def select_treatment(self, person_id): # treatment for reinfection ds-tb: child treatment_regimen = "tb_retx_child" - # -------- SHINE Trial shorter paediatric regimen -------- # - # shorter treatment for child with minimal tb - if (self.module.parameters["scenario"] == 5) \ - & (self.sim.date >= self.module.parameters["scenario_start_date"]) \ - & (person["age_years"] <= 16) \ - & ~(person["tb_smear"]) \ - & ~person["tb_ever_treated"] \ - & ~person["tb_diagnosed_mdr"]: - treatment_regimen = "tb_tx_child_shorter" return treatment_regimen @@ -2309,7 +2226,7 @@ def __init__(self, module, person_id): self.TREATMENT_ID = "Tb_Test_FollowUp" self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"TBFollowUp": 1}) - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.ACCEPTED_FACILITY_LEVEL = "1a" def apply(self, person_id, squeeze_factor): p = self.module.parameters @@ -2340,8 +2257,9 @@ def apply(self, person_id, squeeze_factor): treatment_length = p["ds_treatment_length"] # if previously treated: - if ((person["tb_treatment_regimen"] == "tb_retx_adult") or - (person["tb_treatment_regimen"] == "tb_retx_child")): + if (person["tb_treatment_regimen"] == "tb_retx_adult") or ( + person["tb_treatment_regimen"] == "tb_retx_child" + ): # if strain is ds and person previously treated: sputum_fup = follow_up_times["ds_retreatment_sputum"].dropna() @@ -2353,11 +2271,6 @@ def apply(self, person_id, squeeze_factor): sputum_fup = follow_up_times["mdr_sputum"].dropna() treatment_length = p["mdr_treatment_length"] - # if person on shorter paediatric regimen - elif person["tb_treatment_regimen"] == "tb_tx_child_shorter": - sputum_fup = follow_up_times["shine_sputum"].dropna() - treatment_length = p["shine_treatment_length"] - # check schedule for sputum test and perform if necessary if months_since_tx in sputum_fup: ACTUAL_APPT_FOOTPRINT = self.make_appt_footprint( @@ -2380,11 +2293,15 @@ def apply(self, person_id, squeeze_factor): {"TBFollowUp": 1, "LabTBMicro": 1, "LabMolec": 1} ) if person["tb_smear"]: - xperttest_result = self.sim.modules["HealthSystem"].dx_manager.run_dx_test( + xperttest_result = self.sim.modules[ + "HealthSystem" + ].dx_manager.run_dx_test( dx_tests_to_run="tb_xpert_test_smear_positive", hsi_event=self ) else: - xperttest_result = self.sim.modules["HealthSystem"].dx_manager.run_dx_test( + xperttest_result = self.sim.modules[ + "HealthSystem" + ].dx_manager.run_dx_test( dx_tests_to_run="tb_xpert_test_smear_negative", hsi_event=self ) @@ -2409,7 +2326,7 @@ def apply(self, person_id, squeeze_factor): logger.debug( key="message", data=f"HSI_Tb_FollowUp: scheduling next follow-up " - f"for person {person_id} on {follow_up_date}", + f"for person {person_id} on {follow_up_date}", ) self.sim.modules["HealthSystem"].schedule_hsi_event( @@ -2440,7 +2357,7 @@ def __init__(self, module, person_id): super().__init__(module, person_id=person_id) self.TREATMENT_ID = "Tb_Prevention_Ipt" self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"Over5OPD": 1}) - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.ACCEPTED_FACILITY_LEVEL = "1a" self.number_of_occurrences = 0 def apply(self, person_id, squeeze_factor): @@ -2453,11 +2370,7 @@ def apply(self, person_id, squeeze_factor): person = df.loc[person_id] # Do not run if the person is not alive or already on IPT or diagnosed active infection - if ( - (not person["is_alive"]) - or person["tb_on_ipt"] - or person["tb_diagnosed"] - ): + if (not person["is_alive"]) or person["tb_on_ipt"] or person["tb_diagnosed"]: return # if currently have symptoms of TB, refer for screening/testing @@ -2488,8 +2401,11 @@ def apply(self, person_id, squeeze_factor): ) else: - # Reschedule this HSI to occur again, up to a 3 times in total - if self.number_of_occurrences < 3: + # Reschedule this HSI to occur again, up to a 5 times in total + if ( + self.number_of_occurrences + <= self.module.parameters["tb_healthseekingbehaviour_cap"] + ): self.sim.modules["HealthSystem"].schedule_hsi_event( self, topen=self.sim.date + pd.DateOffset(days=1), @@ -2498,6 +2414,46 @@ def apply(self, person_id, squeeze_factor): ) +class HSI_Tb_EndOfLifeCare(HSI_Event, IndividualScopeEventMixin): + """ + this is a hospital stay for terminally-ill patients with TB + it does not affect disability weight or probability of death + no consumables are logged but health system capacity (HR) is allocated + there are no consequences if hospital bed is not available as person has scheduled death + already within 2 weeks + """ + + def __init__(self, module, person_id, beddays): + super().__init__(module, person_id=person_id) + assert isinstance(module, Tb) + + self.TREATMENT_ID = "Tb_PalliativeCare" + self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({}) + self.ACCEPTED_FACILITY_LEVEL = "2" + + self.beddays = beddays + self.BEDDAYS_FOOTPRINT = ( + self.make_beddays_footprint({"general_bed": self.beddays}) + if self.beddays + else self.make_beddays_footprint({"general_bed": 7.5}) + ) + + def apply(self, person_id, squeeze_factor): + df = self.sim.population.props + hs = self.sim.modules["HealthSystem"] + + if not df.at[person_id, "is_alive"]: + return hs.get_blank_appt_footprint() + + if df.at[person_id, "hv_art"] == "virally_suppressed": + return hs.get_blank_appt_footprint() + + logger.debug( + key="message", + data=f"HSI_Tb_EndOfLifeCare: inpatient admission for {person_id}", + ) + + class Tb_DecisionToContinueIPT(Event, IndividualScopeEventMixin): """Helper event that is used to 'decide' if someone on IPT should continue or end This event is scheduled by 'HSI_Tb_Start_or_Continue_Ipt' after 6 months @@ -2524,7 +2480,9 @@ def apply(self, person_id): if ( person["hv_diagnosed"] and (not person["tb_diagnosed"]) - and (person["tb_date_ipt"] < (self.sim.date - pd.DateOffset(days=36 * 30.5))) + and ( + person["tb_date_ipt"] < (self.sim.date - pd.DateOffset(days=36 * 30.5)) + ) and (m.rng.random_sample() < m.parameters["prob_retained_ipt_6_months"]) ): self.sim.modules["HealthSystem"].schedule_hsi_event( @@ -2540,11 +2498,13 @@ def apply(self, person_id): # --------------------------------------------------------------------------- -class TbDeathEvent(Event, IndividualScopeEventMixin): +class TbDecideDeathEvent(Event, IndividualScopeEventMixin): """ - The scheduled death for a tb case - check whether this death should occur using a linear model + The scheduled hospitalisation and subsequent death for a tb case + check whether death should occur using a linear model will depend on treatment status, smear status and age + then schedule a hospital stay prior to that death + hospital stay will not affect outcomes """ def __init__(self, module, person_id, cause): @@ -2562,7 +2522,7 @@ def apply(self, person_id): logger.debug( key="message", - data=f"TbDeathEvent: checking whether death should occur for person {person_id}", + data=f"TbDecideDeathEvent: checking whether death should occur for person {person_id}", ) # use linear model to determine whether this person will die: @@ -2570,18 +2530,57 @@ def apply(self, person_id): result = self.module.lm["death_rate"].predict(df.loc[[person_id]], rng=rng) if result: - logger.debug( - key="message", - data=f"TbDeathEvent: cause this death for person {person_id}", + # schedule hospital stay for this person + # schedule hospital stay + beddays = self.module.rng.randint(low=5, high=10) + self.sim.modules["HealthSystem"].schedule_hsi_event( + hsi_event=HSI_Tb_EndOfLifeCare( + person_id=person_id, module=self.sim.modules["Tb"], beddays=beddays + ), + priority=0, + topen=self.sim.date, + tclose=None, ) - self.sim.modules["Demography"].do_death( - individual_id=person_id, - cause=self.cause, - originating_module=self.module, + # schedule death for this person after hospital stay + self.sim.schedule_event( + event=TbDeathEvent(person_id=person_id, module=self.module, cause="TB"), + date=self.sim.date + pd.DateOffset(days=beddays), ) +class TbDeathEvent(Event, IndividualScopeEventMixin): + """ + The scheduled death for a tb case + check whether this death should occur using a linear model + will depend on treatment status, smear status and age + """ + + def __init__(self, module, person_id, cause): + super().__init__(module, person_id=person_id) + self.cause = cause + + def apply(self, person_id): + df = self.sim.population.props + + if not df.at[person_id, "is_alive"]: + return + + if not df.at[person_id, "tb_inf"] == "active": + return + + logger.debug( + key="message", + data=f"TbDeathEvent: cause this death for person {person_id}", + ) + + self.sim.modules["Demography"].do_death( + individual_id=person_id, + cause=self.cause, + originating_module=self.module, + ) + + # --------------------------------------------------------------------------- # Logging # --------------------------------------------------------------------------- @@ -2618,7 +2617,7 @@ def apply(self, population): df[ (df.tb_date_active >= (now - DateOffset(months=self.repeat))) & df.hv_inf - ] + ] ) # proportion of active TB cases in the last year who are HIV-positive @@ -2652,22 +2651,22 @@ def apply(self, population): num_active_adult = len( df[(df.tb_inf == "active") & (df.age_years >= 15) & df.is_alive] ) - prev_active_adult = num_active_adult / len( - df[(df.age_years >= 15) & df.is_alive] - ) if len( - df[(df.age_years >= 15) & df.is_alive] - ) else 0 + prev_active_adult = ( + num_active_adult / len(df[(df.age_years >= 15) & df.is_alive]) + if len(df[(df.age_years >= 15) & df.is_alive]) + else 0 + ) assert prev_active_adult <= 1 # prevalence of active TB in children num_active_child = len( df[(df.tb_inf == "active") & (df.age_years < 15) & df.is_alive] ) - prev_active_child = num_active_child / len( - df[(df.age_years < 15) & df.is_alive] - ) if len( - df[(df.age_years < 15) & df.is_alive] - ) else 0 + prev_active_child = ( + num_active_child / len(df[(df.age_years < 15) & df.is_alive]) + if len(df[(df.age_years < 15) & df.is_alive]) + else 0 + ) assert prev_active_child <= 1 # LATENT @@ -2680,22 +2679,22 @@ def apply(self, population): num_latent_adult = len( df[(df.tb_inf == "latent") & (df.age_years >= 15) & df.is_alive] ) - prev_latent_adult = num_latent_adult / len( - df[(df.age_years >= 15) & df.is_alive] - ) if len( - df[(df.age_years >= 15) & df.is_alive] - ) else 0 + prev_latent_adult = ( + num_latent_adult / len(df[(df.age_years >= 15) & df.is_alive]) + if len(df[(df.age_years >= 15) & df.is_alive]) + else 0 + ) assert prev_latent_adult <= 1 # proportion of population with latent TB - children num_latent_child = len( df[(df.tb_inf == "latent") & (df.age_years < 15) & df.is_alive] ) - prev_latent_child = num_latent_child / len( - df[(df.age_years < 15) & df.is_alive] - ) if len( - df[(df.age_years < 15) & df.is_alive] - ) else 0 + prev_latent_child = ( + num_latent_child / len(df[(df.age_years < 15) & df.is_alive]) + if len(df[(df.age_years < 15) & df.is_alive]) + else 0 + ) assert prev_latent_child <= 1 logger.info( @@ -2720,7 +2719,7 @@ def apply(self, population): df[ (df.tb_strain == "mdr") & (df.tb_date_active >= (now - DateOffset(months=self.repeat))) - ] + ] ) if new_mdr_cases: @@ -2742,7 +2741,8 @@ def apply(self, population): new_tb_diagnosis = len( df[ (df.tb_date_active >= (now - DateOffset(months=self.repeat))) - & (df.tb_date_diagnosed >= (now - DateOffset(months=self.repeat)))] + & (df.tb_date_diagnosed >= (now - DateOffset(months=self.repeat))) + ] ) if new_tb_diagnosis: @@ -2756,7 +2756,7 @@ def apply(self, population): df[ (df.tb_date_active >= (now - DateOffset(months=self.repeat))) & (df.tb_date_treated >= (now - DateOffset(months=self.repeat))) - ] + ] ) # treatment coverage: if became active and was treated in last timeperiod @@ -2767,11 +2767,7 @@ def apply(self, population): tx_coverage = 0 # ipt coverage - new_tb_ipt = len( - df[ - (df.tb_date_ipt >= (now - DateOffset(months=self.repeat))) - ] - ) + new_tb_ipt = len(df[(df.tb_date_ipt >= (now - DateOffset(months=self.repeat)))]) # this will give ipt among whole population - not just eligible pop if new_tb_ipt: @@ -2799,17 +2795,27 @@ def apply(self, population): # adults # get index of adults starting tx in last time-period # note tb onset may have been up to 3 years prior to treatment - adult_tx_idx = df.loc[(df.age_years >= 16) & - (df.tb_date_treated >= (now - DateOffset(months=self.repeat)))].index + adult_tx_idx = df.loc[ + (df.age_years >= 16) + & (df.tb_date_treated >= (now - DateOffset(months=self.repeat))) + ].index # calculate treatment_date - onset_date for each person in index - adult_tx_delays = (df.loc[adult_tx_idx, "tb_date_treated"] - df.loc[adult_tx_idx, "tb_date_active"]).dt.days + adult_tx_delays = ( + df.loc[adult_tx_idx, "tb_date_treated"] + - df.loc[adult_tx_idx, "tb_date_active"] + ).dt.days adult_tx_delays = adult_tx_delays.tolist() # children - child_tx_idx = df.loc[(df.age_years < 16) & - (df.tb_date_treated >= (now - DateOffset(months=self.repeat)))].index - child_tx_delays = (df.loc[child_tx_idx, "tb_date_treated"] - df.loc[child_tx_idx, "tb_date_active"]).dt.days + child_tx_idx = df.loc[ + (df.age_years < 16) + & (df.tb_date_treated >= (now - DateOffset(months=self.repeat))) + ].index + child_tx_delays = ( + df.loc[child_tx_idx, "tb_date_treated"] + - df.loc[child_tx_idx, "tb_date_active"] + ).dt.days child_tx_delays = child_tx_delays.tolist() logger.info( @@ -2833,7 +2839,7 @@ def apply(self, population): ~(df.tb_date_active >= (now - DateOffset(months=36))) & (df.tb_date_treated >= (now - DateOffset(months=self.repeat))) & (df.age_years >= 16) - ] + ] ) # these are all new adults treated, regardless of tb status @@ -2841,7 +2847,7 @@ def apply(self, population): df[ (df.tb_date_treated >= (now - DateOffset(months=self.repeat))) & (df.age_years >= 16) - ] + ] ) # proportion of adults starting on treatment who are false positive @@ -2856,7 +2862,7 @@ def apply(self, population): ~(df.tb_date_active >= (now - DateOffset(months=36))) & (df.tb_date_treated >= (now - DateOffset(months=self.repeat))) & (df.age_years < 16) - ] + ] ) # these are all new children treated, regardless of tb status @@ -2864,7 +2870,7 @@ def apply(self, population): df[ (df.tb_date_treated >= (now - DateOffset(months=self.repeat))) & (df.age_years < 16) - ] + ] ) # proportion of children starting on treatment who are false positive @@ -2933,14 +2939,15 @@ def initialise_population(self, population): df = population.props tb_idx = df.index[ - df.is_alive & (self.rng.random_sample(len(df.is_alive)) < self.active_tb_prev) - ] + df.is_alive + & (self.rng.random_sample(len(df.is_alive)) < self.active_tb_prev) + ] df.loc[tb_idx, "tb_inf"] = "active" def initialise_simulation(self, sim): pass def on_birth(self, mother, child): - child_infected = (self.rng.random_sample() < self.active_tb_prev) + child_infected = self.rng.random_sample() < self.active_tb_prev if child_infected: self.sim.population.props.at[child, "tb_inf"] = "active" diff --git a/tests/test_malaria.py b/tests/test_malaria.py index c7eb88b796..9fa36f1070 100644 --- a/tests/test_malaria.py +++ b/tests/test_malaria.py @@ -102,7 +102,7 @@ def test_sims(sim): assert not df.at[person, "ma_inf_type"] == "none" # if on treatment, must have treatment start date - for person in df.index[df.ma_tx]: + for person in df.index[df.ma_tx.isin(["uncomplicated", "complicated"])]: assert not pd.isnull(df.at[person, "ma_date_tx"]) @@ -183,7 +183,7 @@ def test_schedule_rdt_for_all(sim): df = sim.population.props # check no treatment unless infected - for person in df.index[df.ma_tx]: + for person in df.index[df.ma_tx.isin(["uncomplicated", "complicated"])]: assert not pd.isnull(df.at[person, "ma_date_infected"]) # check clinical counter is working @@ -222,7 +222,7 @@ def apply(self, person_id, squeeze_factor): def test_dx_algorithm_for_malaria_outcomes_clinical(sim): """ - Create a person with clinical malaria and check if the functions in + Create a person with clinical malaria and check if the functions in dx_algorithm_child return the correct diagnosis. """ # Set up the simulation: @@ -257,7 +257,7 @@ def test_dx_algorithm_for_malaria_outcomes_clinical(sim): def test_dx_algorithm_for_malaria_outcomes_severe(sim): """ - Create a person with severe malaria and check if the functions in + Create a person with severe malaria and check if the functions in dx_algorithm_child return the correct diagnosis. """ # Set up the simulation: @@ -393,7 +393,7 @@ def test_severe_malaria_deaths_perfect_treatment(sim): treatment_appt = malaria.HSI_Malaria_Treatment_Complicated(person_id=person_id, module=sim.modules['Malaria']) treatment_appt.apply(person_id=person_id, squeeze_factor=0.0) - assert df.at[person_id, 'ma_tx'] + assert df.at[person_id, 'ma_tx'] != 'none' assert df.at[person_id, "ma_date_tx"] == sim.date assert df.at[person_id, "ma_tx_counter"] > 0 @@ -428,7 +428,7 @@ def test_severe_malaria_deaths_treatment_failure(sim): treatment_appt = malaria.HSI_Malaria_Treatment_Complicated(person_id=person_id, module=sim.modules['Malaria']) treatment_appt.apply(person_id=person_id, squeeze_factor=0.0) - assert df.at[person_id, 'ma_tx'] + assert df.at[person_id, 'ma_tx'] != 'none' assert df.at[person_id, "ma_date_tx"] == sim.date assert df.at[person_id, "ma_tx_counter"] > 0 @@ -447,7 +447,7 @@ def test_severe_malaria_deaths_treatment_failure(sim): person_id = 1 df.loc[person_id, ["ma_is_infected", "ma_inf_type"]] = (True, "severe") - assert not df.at[person_id, 'ma_tx'] + assert df.at[person_id, 'ma_tx'] == 'none' assert df.at[person_id, "ma_date_tx"] is pd.NaT assert df.at[person_id, "ma_tx_counter"] == 0 @@ -546,7 +546,7 @@ def test_individual_testing_and_treatment(sim): tx_event.run(squeeze_factor=0.0) assert df.at[person_id, "ma_tx_counter"] == 1 - assert df.at[person_id, "ma_tx"] + assert df.at[person_id, "ma_tx"] != 'none' # -------- asymptomatic infection person_id = 1 @@ -589,7 +589,7 @@ def test_individual_testing_and_treatment(sim): tx_appt.apply(person_id=person_id, squeeze_factor=0.0) assert df.at[person_id, "ma_tx_counter"] == 1 - assert df.at[person_id, "ma_tx"] + assert df.at[person_id, "ma_tx"] != 'none' # -------- severe infection person_id = 2 @@ -636,7 +636,7 @@ def test_individual_testing_and_treatment(sim): tx_appt.apply(person_id=person_id, squeeze_factor=0.0) assert df.at[person_id, "ma_tx_counter"] == 1 - assert df.at[person_id, "ma_tx"] + assert df.at[person_id, "ma_tx"] != 'none' def test_population_testing_and_treatment(sim): diff --git a/tests/test_tb.py b/tests/test_tb.py index 5c2943b310..8ff363708d 100644 --- a/tests/test_tb.py +++ b/tests/test_tb.py @@ -421,6 +421,7 @@ def get_appt_footprints(_consumables_availability): # 1) If consumables available, the HSI will only be run once and the appt footprint should be TBNew: assert [{'TBNew': 1}] == get_appt_footprints(_consumables_availability='all') + # 2) If consumables not available, there should be multiple footprints where the first is TBNew # and the rest is PharmDispensing appt_list = get_appt_footprints(_consumables_availability='none') @@ -431,8 +432,8 @@ def get_appt_footprints(_consumables_availability): def test_treatment_failure(seed): """ - test treatment failure occurs and - retreatment properties / follow-up occurs correctly + test treatment failure occurs and properties set correctly + treatment failure will schedule referral for xpert test at level 2 """ popsize = 10 @@ -504,26 +505,30 @@ def test_treatment_failure(seed): # check referral for screening/testing again # screen and test person_id screening_appt = tb.HSI_Tb_ScreeningAndRefer(person_id=person_id, - module=sim.modules['Tb']) + module=sim.modules['Tb'], + facility_level="1a") test = screening_appt.apply(person_id=person_id, squeeze_factor=0.0) - # should schedule xpert - if available - # check that the event returned a footprint for an xpert test + # should schedule a referral for xpert testing at facility level 2 + # check that the event returned a footprint Over5OPD assert test == screening_appt.make_appt_footprint({'Over5OPD': 1}) - # start treatment - tx_start = tb.HSI_Tb_StartTreatment(person_id=person_id, - module=sim.modules['Tb']) - tx_start.apply(person_id=person_id, squeeze_factor=0.0) - - # clinical monitoring - # check tb.HSI_Tb_FollowUp scheduled - followup_event = [ + # check tb.HSI_Tb_ScreeningAndRefer scheduled + followup_test = [ ev for ev in sim.modules['HealthSystem'].find_events_for_person(person_id) if - isinstance(ev[1], tb.HSI_Tb_FollowUp) + isinstance(ev[1], tb.HSI_Tb_ScreeningAndRefer) ][-1] - assert followup_event[0] > sim.date + assert followup_test[0] > sim.date + + # schedule follow-up test at level 2 to get xpert + screening_appt = tb.HSI_Tb_ScreeningAndRefer(person_id=person_id, + module=sim.modules['Tb'], + facility_level="2") + test = screening_appt.apply(person_id=person_id, squeeze_factor=0.0) + + # assert now should be diagnosed as active TB again + assert df.at[person_id, 'tb_diagnosed'] def test_children_referrals(seed): @@ -783,7 +788,8 @@ def test_mdr(seed): # next screening should pick up case as retreatment / mdr screening_appt = tb.HSI_Tb_ScreeningAndRefer(person_id=person_id, - module=sim.modules['Tb']) + module=sim.modules['Tb'], + facility_level='2') screening_appt.apply(person_id=person_id, squeeze_factor=0.0) assert df.at[person_id, 'tb_diagnosed_mdr'] @@ -791,7 +797,8 @@ def test_mdr(seed): # schedule mdr treatment start # this calls clinical_monitoring which should schedule all follow-up appts tx_start = tb.HSI_Tb_StartTreatment(person_id=person_id, - module=sim.modules['Tb']) + module=sim.modules['Tb'], + facility_level='2') tx_start.apply(person_id=person_id, squeeze_factor=0.0) # check treatment appropriate for mdr-tb @@ -1124,13 +1131,24 @@ def test_hsi_scheduling(seed): hsi_event = tb.HSI_Tb_ScreeningAndRefer(person_id=person_id, module=sim.modules['Tb']) hsi_event.run(squeeze_factor=0) + # person is HIV+ and will be referred for xpert - only available at level 2 + # Check person_id has a further testing event scheduled + date_event, event = [ + ev for ev in sim.modules['HealthSystem'].find_events_for_person(person_id) if + isinstance(ev[1], tb.HSI_Tb_ScreeningAndRefer) + ][0] + assert date_event >= sim.date + + # run testing event for xpert + hsi_event = tb.HSI_Tb_ScreeningAndRefer(person_id=person_id, + module=sim.modules['Tb'], + facility_level='2') + hsi_event.run(squeeze_factor=0) + + # person should now have treatment event scheduled # Check person_id has a treatment event scheduled date_event, event = [ ev for ev in sim.modules['HealthSystem'].find_events_for_person(person_id) if isinstance(ev[1], tb.HSI_Tb_StartTreatment) ][0] assert date_event == sim.date - - # check this is the only event scheduled - tmp = sim.modules['HealthSystem'].find_events_for_person(person_id) - assert len(tmp) == 1 From 22c10f06fd65ef8f13e92d1f11a3f0599d0181bc Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 9 Feb 2024 13:57:40 +0000 Subject: [PATCH 002/131] remove unused import statements --- src/tlo/methods/malaria.py | 2 +- src/tlo/methods/tb.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index a395493c7a..173c8f14f9 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -22,7 +22,7 @@ from tlo.methods.healthsystem import HSI_Event from tlo.methods.symptommanager import Symptom from tlo.util import random_date -from tlo.lm import LinearModel, LinearModelType, Predictor +from tlo.lm import LinearModel, Predictor logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 292a29675a..76ca2b9fd5 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -20,7 +20,7 @@ from tlo.methods import Metadata, hiv from tlo.methods.causes import Cause from tlo.methods.dxmanager import DxTest -from tlo.methods.healthsystem import HealthSystemChangeParameters, HSI_Event +from tlo.methods.healthsystem import HSI_Event from tlo.methods.symptommanager import Symptom from tlo.util import random_date From e8612a307ff6d182eef93c1923ac855051c9a25b Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 9 Feb 2024 14:03:33 +0000 Subject: [PATCH 003/131] fix imports --- src/tlo/methods/hiv.py | 7 +------ src/tlo/methods/malaria.py | 9 ++------- src/tlo/methods/tb.py | 7 +------ 3 files changed, 4 insertions(+), 19 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 0b192a5235..3021f27f50 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -30,12 +30,7 @@ import pandas as pd from tlo import DAYS_IN_YEAR, DateOffset, Module, Parameter, Property, Types, logging -from tlo.events import ( - Event, - IndividualScopeEventMixin, - PopulationScopeEventMixin, - RegularEvent, -) +from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent from tlo.lm import LinearModel, LinearModelType, Predictor from tlo.methods import Metadata, demography, tb from tlo.methods.causes import Cause diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 173c8f14f9..e1b739e571 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -10,19 +10,14 @@ import pandas as pd from tlo import DateOffset, Module, Parameter, Property, Types, logging -from tlo.events import ( - Event, - IndividualScopeEventMixin, - PopulationScopeEventMixin, - RegularEvent, -) +from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent +from tlo.lm import LinearModel, Predictor from tlo.methods import Metadata from tlo.methods.causes import Cause from tlo.methods.dxmanager import DxTest from tlo.methods.healthsystem import HSI_Event from tlo.methods.symptommanager import Symptom from tlo.util import random_date -from tlo.lm import LinearModel, Predictor logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 76ca2b9fd5..c515ad5ea5 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -10,12 +10,7 @@ import pandas as pd from tlo import DateOffset, Module, Parameter, Property, Types, logging -from tlo.events import ( - Event, - IndividualScopeEventMixin, - PopulationScopeEventMixin, - RegularEvent, -) +from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent from tlo.lm import LinearModel, LinearModelType, Predictor from tlo.methods import Metadata, hiv from tlo.methods.causes import Cause From c6802c98d2b22381f7c23299d056978330adb842 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 9 Feb 2024 14:08:17 +0000 Subject: [PATCH 004/131] update filepath for malaria resource file --- src/tlo/methods/malaria.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index e1b739e571..f274f9cdd8 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -215,9 +215,8 @@ def __init__(self, name=None, resourcefilepath=None): } def read_parameters(self, data_folder): - workbook = pd.read_excel( - self.resourcefilepath / "ResourceFile_malaria.xlsx", sheet_name=None - ) + workbook = pd.read_excel(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria.xlsx', sheet_name=None) + self.load_parameters_from_dataframe(workbook["parameters"]) p = self.parameters @@ -230,15 +229,9 @@ def read_parameters(self, data_folder): p["sev_symp_prob"] = workbook["severe_symptoms"] p["rdt_testing_rates"] = workbook["WHO_TestData2023"] - p["inf_inc"] = pd.read_csv( - self.resourcefilepath / "ResourceFile_malaria_InfInc_expanded.csv" - ) - p["clin_inc"] = pd.read_csv( - self.resourcefilepath / "ResourceFile_malaria_ClinInc_expanded.csv" - ) - p["sev_inc"] = pd.read_csv( - self.resourcefilepath / "ResourceFile_malaria_SevInc_expanded.csv" - ) + p['inf_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_InfInc_expanded.csv') + p['clin_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_ClinInc_expanded.csv') + p['sev_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_SevInc_expanded.csv') # check itn projected values are <=0.7 and rounded to 1dp for matching to incidence tables p["itn"] = round(p["itn"], 1) From 8014f02a80b76595f5c83bd08d823c9370395374 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 9 Feb 2024 14:13:16 +0000 Subject: [PATCH 005/131] remove test_hiv_tb_scenarios.py --- tests/test_hiv_tb_scenarios.py | 270 --------------------------------- 1 file changed, 270 deletions(-) delete mode 100644 tests/test_hiv_tb_scenarios.py diff --git a/tests/test_hiv_tb_scenarios.py b/tests/test_hiv_tb_scenarios.py deleted file mode 100644 index e872eab63f..0000000000 --- a/tests/test_hiv_tb_scenarios.py +++ /dev/null @@ -1,270 +0,0 @@ -""" Tests for setting up the HIV and TB scenarios used for projections """ - -import os -from pathlib import Path - -import pandas as pd -import pytest - -from tlo import Date, Simulation -from tlo.methods import ( - demography, - enhanced_lifestyle, - epi, - healthburden, - healthseekingbehaviour, - healthsystem, - hiv, - simplified_births, - symptommanager, - tb, -) - -try: - resourcefilepath = Path(os.path.dirname(__file__)) / '../resources' -except NameError: - # running interactively - resourcefilepath = 'resources' - - -def get_sim(seed): - """ - register all necessary modules for the tests to run - """ - - start_date = Date(2010, 1, 1) - sim = Simulation(start_date=start_date, seed=seed) - - # Register the appropriate modules - sim.register( - demography.Demography(resourcefilepath=resourcefilepath), - simplified_births.SimplifiedBirths(resourcefilepath=resourcefilepath), - enhanced_lifestyle.Lifestyle(resourcefilepath=resourcefilepath), - healthsystem.HealthSystem( - resourcefilepath=resourcefilepath, - service_availability=["*"], # all treatment allowed - mode_appt_constraints=0, # mode of constraints to do with officer numbers and time - cons_availability="all", # mode for consumable constraints (if ignored, all consumables available) - ignore_priority=True, # do not use the priority information in HSI event to schedule - capabilities_coefficient=1.0, # multiplier for the capabilities of health officers - disable=False, # disables the healthsystem (no constraints and no logging) and every HSI runs - disable_and_reject_all=False, # disable healthsystem and no HSI runs - ), - symptommanager.SymptomManager(resourcefilepath=resourcefilepath), - healthseekingbehaviour.HealthSeekingBehaviour(resourcefilepath=resourcefilepath), - healthburden.HealthBurden(resourcefilepath=resourcefilepath), - epi.Epi(resourcefilepath=resourcefilepath), - hiv.Hiv(resourcefilepath=resourcefilepath), - tb.Tb(resourcefilepath=resourcefilepath), - ) - - return sim - - -@pytest.mark.slow -def test_scenario_ipt_expansion(seed): - """ test scenario IPT expansion is set up correctly - should be expanded age eligibility in scenario 3 - otherwise only ages <5 are eligible - """ - - popsize = 100 - - sim = get_sim(seed=seed) - - # stop PLHIV getting IPT for purpose of tests - sim.modules['Tb'].parameters['ipt_coverage'].coverage_plhiv = 0 - # set coverage of IPT for TB contacts to 100% - sim.modules['Tb'].parameters['ipt_coverage'].coverage_paediatric = 100 - - # Make the population - sim.make_initial_population(n=popsize) - # simulate for 0 days, just get everything set up (dxtests etc) - sim.simulate(end_date=sim.date + pd.DateOffset(days=0)) - df = sim.population.props - - # check default scenario is set to 0 - assert sim.modules['Tb'].parameters['scenario'] == 0 - - # ipt eligibility should be limited to ages <=5 years - # assign all population into one district - so all are eligible as contacts - df.loc[df.is_alive, 'district_of_residence'] = 'Blantyre' - - # assign active TB to person 0 - person_id = 0 - - # assign person_id 0 active tb - df.at[person_id, 'tb_inf'] = 'active' - df.at[person_id, 'tb_strain'] = 'ds' - df.at[person_id, 'tb_date_active'] = sim.date - df.at[person_id, 'tb_smear'] = True - df.at[person_id, 'age_exact_years'] = 20 - df.at[person_id, 'age_years'] = 20 - - # assign symptoms - symptom_list = {"fever", "respiratory_symptoms", "fatigue", "night_sweats"} - sim.modules["SymptomManager"].change_symptom( - person_id=person_id, - symptom_string=symptom_list, - add_or_remove="+", - disease_module=sim.modules['Tb'], - duration_in_days=None, - ) - - # run diagnosis (HSI_Tb_ScreeningAndRefer) for person 0 - assert "tb_sputum_test_smear_positive" in sim.modules["HealthSystem"].dx_manager.dx_tests - screening_appt = tb.HSI_Tb_ScreeningAndRefer(person_id=person_id, - module=sim.modules['Tb']) - screening_appt.apply(person_id=person_id, squeeze_factor=0.0) - - assert pd.notnull(df.at[person_id, 'tb_date_tested']) - assert df.at[person_id, 'tb_diagnosed'] - - # check ages of those scheduled for HSI_Tb_Start_or_Continue_Ipt - list_of_events = list() - - for ev_tuple in sim.modules['HealthSystem'].HSI_EVENT_QUEUE: - date = ev_tuple.topen # this is the 'topen' value - event = ev_tuple.hsi_event - if isinstance(event, tb.HSI_Tb_Start_or_Continue_Ipt): - list_of_events.append((date, event, event.target)) - - idx_of_ipt_candidates = [x[2] for x in list_of_events] - ages_of_ipt_candidates = df.loc[idx_of_ipt_candidates, "age_exact_years"] - assert (ages_of_ipt_candidates < 6).all() - - -@pytest.mark.slow -def test_check_tb_test_under_each_scenario(seed): - """ test correct test is scheduled under each scenario - """ - - popsize = 10 - - sim = get_sim(seed=seed) - - # Make the population - sim.make_initial_population(n=popsize) - - sim.modules['Tb'].parameters["prop_presumptive_mdr_has_xpert"] = 1.0 # xpert always available - sim.modules['Tb'].parameters["sens_xpert"] = 1.0 # increase sensitivity of xpert testing - - # ------------------------- scenario 0 ------------------------- # - sim.modules['Tb'].parameters['scenario'] = 0 - - # simulate for 0 days, just get everything set up (dxtests etc) - sim.simulate(end_date=sim.date + pd.DateOffset(days=0)) - - df = sim.population.props - - # assign person_id active tb - hiv_neg_person = 0 - hiv_pos_person = 1 - both_people = [hiv_neg_person, hiv_pos_person] - - df.loc[both_people, 'tb_inf'] = 'active' - df.loc[both_people, 'tb_strain'] = 'ds' - df.loc[both_people, 'tb_date_active'] = sim.date - df.loc[both_people, 'tb_smear'] = True - df.loc[both_people, 'age_exact_years'] = 20 - df.loc[both_people, 'age_years'] = 20 - - # set HIV status - df.at[hiv_neg_person, 'hv_inf'] = False - df.at[hiv_pos_person, 'hv_inf'] = True - df.at[hiv_neg_person, 'hv_diagnosed'] = False # this is used for tb test selection - df.at[hiv_pos_person, 'hv_diagnosed'] = True - - # assign symptoms - symptom_list = {"fever", "respiratory_symptoms", "fatigue", "night_sweats"} - sim.modules["SymptomManager"].change_symptom( - person_id=both_people, - symptom_string=symptom_list, - add_or_remove="+", - disease_module=sim.modules['Tb'], - duration_in_days=None, - ) - - # select test for each person under baseline scenario - standard guidelines - assert "sputum" == sim.modules["Tb"].select_tb_test(hiv_neg_person) - assert "xpert" == sim.modules["Tb"].select_tb_test(hiv_pos_person) - - # screen and test hiv_neg_person - screening_appt = tb.HSI_Tb_ScreeningAndRefer(person_id=hiv_neg_person, - module=sim.modules['Tb']) - screening_appt.apply(person_id=hiv_neg_person, squeeze_factor=0.0) - - assert pd.notnull(df.at[hiv_neg_person, 'tb_date_tested']) - assert df.at[hiv_neg_person, 'tb_diagnosed'] - assert not df.at[hiv_neg_person, 'tb_diagnosed_mdr'] - - # screen and test hiv_pos_person - screening_appt = tb.HSI_Tb_ScreeningAndRefer(person_id=hiv_pos_person, - module=sim.modules['Tb']) - screening_appt.apply(person_id=hiv_pos_person, squeeze_factor=0.0) - - assert pd.notnull(df.at[hiv_pos_person, 'tb_date_tested']) - assert df.at[hiv_pos_person, 'tb_diagnosed'] - assert not df.at[hiv_pos_person, 'tb_diagnosed_mdr'] - - # apply scenario change, re-test, should be same - scenario_change_event = tb.ScenarioSetupEvent(module=sim.modules['Tb']) - scenario_change_event.apply(sim.population) - - # select test for each person under baseline scenario - standard guidelines - # this person should still qualify for sputum as they have not been treated - assert "sputum" == sim.modules["Tb"].select_tb_test(hiv_neg_person) - assert "xpert" == sim.modules["Tb"].select_tb_test(hiv_pos_person) - - # ------------------------- scenario 1 ------------------------- # - sim = get_sim(seed=seed) - - # Make the population - sim.make_initial_population(n=popsize) - - sim.modules['Tb'].parameters["prop_presumptive_mdr_has_xpert"] = 1.0 # xpert always available - sim.modules['Tb'].parameters["sens_xpert"] = 1.0 # increase sensitivity of xpert testing - sim.modules['Tb'].parameters['scenario'] = 1 - - # simulate for 0 days, just get everything set up (dxtests etc) - sim.simulate(end_date=sim.date + pd.DateOffset(days=0)) - - df = sim.population.props - - # assign person_id active tb - hiv_neg_person = 0 - hiv_pos_person = 1 - both_people = [hiv_neg_person, hiv_pos_person] - - df.loc[both_people, 'tb_inf'] = 'active' - df.loc[both_people, 'tb_strain'] = 'ds' - df.loc[both_people, 'tb_date_active'] = sim.date - df.loc[both_people, 'tb_smear'] = True - df.loc[both_people, 'age_exact_years'] = 20 - df.loc[both_people, 'age_years'] = 20 - # set HIV status - df.at[hiv_neg_person, 'hv_inf'] = False - df.at[hiv_pos_person, 'hv_inf'] = True - df.at[hiv_pos_person, 'hv_diagnosed'] = True - - # assign symptoms - symptom_list = {"fever", "respiratory_symptoms", "fatigue", "night_sweats"} - sim.modules["SymptomManager"].change_symptom( - person_id=both_people, - symptom_string=symptom_list, - add_or_remove="+", - disease_module=sim.modules['Tb'], - duration_in_days=None, - ) - - # select test for each person under scenario 1, should be standard at first - assert "sputum" == sim.modules["Tb"].select_tb_test(hiv_neg_person) - assert "xpert" == sim.modules["Tb"].select_tb_test(hiv_pos_person) - - # apply scenario change, re-test, should be xpert for all - scenario_change_event = tb.ScenarioSetupEvent(module=sim.modules['Tb']) - scenario_change_event.apply(sim.population) - - # select test for each person under changed guidelines - assert "xpert" == sim.modules["Tb"].select_tb_test(hiv_neg_person) - assert "xpert" == sim.modules["Tb"].select_tb_test(hiv_pos_person) From 63a364aee140153e1555584ad5c6d38c97f4a82c Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 13 Feb 2024 12:23:39 +0000 Subject: [PATCH 006/131] updated test_healthsystem.py: test_manipulation_of_service_availability as small population size over 7 days will not schedule VMMC through HIV module. Remove HIV_Prevention_Circumcision in assert statement for HIV services delivered in one week --- tests/test_healthsystem.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_healthsystem.py b/tests/test_healthsystem.py index 07bf3f71a1..c823a15f0f 100644 --- a/tests/test_healthsystem.py +++ b/tests/test_healthsystem.py @@ -1447,7 +1447,8 @@ def get_set_of_treatment_ids_that_run(service_availability) -> Set[str]: get_set_of_treatment_ids_that_run(service_availability=["Hiv_Test_*"]) - generic_first_appts # Allow all `Hiv` things (but nothing else) - assert set({'Hiv_Test', 'Hiv_Treatment', 'Hiv_Prevention_Circumcision'}) == \ + # for pop=500 over 7 days, Hiv_Prevention_Circumcision will not occur + assert set({'Hiv_Test', 'Hiv_Treatment'}) == \ get_set_of_treatment_ids_that_run(service_availability=["Hiv_*"]) - generic_first_appts # Allow all except `Hiv_Test` From b591b82f9a01ccbf22beb9a37c5e2f32c24d8c59 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 13 Feb 2024 12:43:35 +0000 Subject: [PATCH 007/131] change ipt_coverage in TB logger as conflicts with existing parameter --- ...ceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx | 3 +++ resources/~$ResourceFile_TB.xlsx | 3 +++ src/tlo/methods/tb.py | 6 +++--- 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 resources/~$ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx create mode 100644 resources/~$ResourceFile_TB.xlsx diff --git a/resources/~$ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx b/resources/~$ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/~$ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/resources/~$ResourceFile_TB.xlsx b/resources/~$ResourceFile_TB.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/~$ResourceFile_TB.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index c515ad5ea5..11295a5707 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -2766,9 +2766,9 @@ def apply(self, population): # this will give ipt among whole population - not just eligible pop if new_tb_ipt: - ipt_coverage = new_tb_ipt / len(df[df.is_alive]) + current_ipt_coverage = new_tb_ipt / len(df[df.is_alive]) else: - ipt_coverage = 0 + current_ipt_coverage = 0 logger.info( key="tb_treatment", @@ -2777,7 +2777,7 @@ def apply(self, population): "tbNewDiagnosis": new_tb_diagnosis, "tbPropDiagnosed": prop_dx, "tbTreatmentCoverage": tx_coverage, - "tbIptCoverage": ipt_coverage, + "tbIptCoverage": current_ipt_coverage, }, ) From 68b295ed7cae300afd8829d41a866ee61004281c Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 13 Feb 2024 13:14:40 +0000 Subject: [PATCH 008/131] updated ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx with updated NTP2019 TB data --- ...urceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx | 4 ++-- tests/test_analysis.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx index de2f66d2d0..2bdaf2f038 100644 --- a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx +++ b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4d9c48c88403b1a45bd7c4f983b228c683ea2597a6a02fc2469d2ec955d8724 -size 48646 +oid sha256:724a9d02f2813bdabf1b01e9d810f4fe3e9799c6039c343004d6aeef3aa5bd15 +size 48826 diff --git a/tests/test_analysis.py b/tests/test_analysis.py index 7658506a5c..96bfe1d7cc 100644 --- a/tests/test_analysis.py +++ b/tests/test_analysis.py @@ -313,7 +313,8 @@ def test_get_parameter_functions(seed): # Check that the parameter identified exists in the simulation assert name in sim.modules[module].parameters, f"Parameter not recognised: {module}:{name}." - + if module == 'Tb': + print('TB') # Check that the original value and the updated value are of the same type. original = sim.modules[module].parameters[name] From 7dbe7b5838e619256f33e72bc15b36500fdca115 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 13 Feb 2024 13:16:47 +0000 Subject: [PATCH 009/131] remove test code --- ...urceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx | 4 ++-- tests/test_analysis.py | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx index 2bdaf2f038..9efe4c8d66 100644 --- a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx +++ b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:724a9d02f2813bdabf1b01e9d810f4fe3e9799c6039c343004d6aeef3aa5bd15 -size 48826 +oid sha256:c0b17e025cd9de74b38f78e70ac55e868ed168daa0f317e819fcc48dd9dd8a35 +size 48829 diff --git a/tests/test_analysis.py b/tests/test_analysis.py index 96bfe1d7cc..7658506a5c 100644 --- a/tests/test_analysis.py +++ b/tests/test_analysis.py @@ -313,8 +313,7 @@ def test_get_parameter_functions(seed): # Check that the parameter identified exists in the simulation assert name in sim.modules[module].parameters, f"Parameter not recognised: {module}:{name}." - if module == 'Tb': - print('TB') + # Check that the original value and the updated value are of the same type. original = sim.modules[module].parameters[name] From 09c9b261d9ad9c714f1181db88d774f069f5fcc8 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 13 Feb 2024 13:31:16 +0000 Subject: [PATCH 010/131] remove test code --- resources/malaria/ResourceFile_malaria.xlsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/malaria/ResourceFile_malaria.xlsx b/resources/malaria/ResourceFile_malaria.xlsx index 9692613985..70902b7480 100644 --- a/resources/malaria/ResourceFile_malaria.xlsx +++ b/resources/malaria/ResourceFile_malaria.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9b5e7cadad2a14d0e547238a0cb02c75c41c39f3bf1106ca8a903f0364f8c048 -size 67591 +oid sha256:6ba5849e265103ee799d1982325b6fed1ef4d3df559ffce9d6790395c201fcaf +size 67562 From 73f19da19b8a9a68617b903cd43a1de0603a4ddb Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 13 Feb 2024 13:37:57 +0000 Subject: [PATCH 011/131] delete tmp resourcefiles --- ...ourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx | 3 --- resources/~$ResourceFile_TB.xlsx | 3 --- 2 files changed, 6 deletions(-) delete mode 100644 resources/~$ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx delete mode 100644 resources/~$ResourceFile_TB.xlsx diff --git a/resources/~$ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx b/resources/~$ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/~$ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 diff --git a/resources/~$ResourceFile_TB.xlsx b/resources/~$ResourceFile_TB.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/~$ResourceFile_TB.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 From b81b2d6f11dbd1fc050b07ca0b12c2c83fd8b0b4 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 13 Feb 2024 13:50:45 +0000 Subject: [PATCH 012/131] malaria code use person_id consistently instead of individual_id --- src/tlo/methods/malaria.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index f274f9cdd8..9992c25888 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -574,7 +574,7 @@ def _draw_incidence_for(_col, _where): ) death_event = MalariaDeathEvent( - self, individual_id=person, cause="Malaria" + self, person_id=person, cause="Malaria" ) # make that death event self.sim.schedule_event(death_event, date_death) # schedule the death @@ -940,53 +940,53 @@ class MalariaDeathEvent(Event, IndividualScopeEventMixin): Performs the Death operation on an individual and logs it. """ - def __init__(self, module, individual_id, cause): - super().__init__(module, person_id=individual_id) + def __init__(self, module, person_id, cause): + super().__init__(module, person_id=person_id) self.cause = cause - def apply(self, individual_id): + def apply(self, person_id): df = self.sim.population.props - if not df.at[individual_id, "is_alive"] or ( - df.at[individual_id, "ma_inf_type"] == "none" + if not df.at[person_id, "is_alive"] or ( + df.at[person_id, "ma_inf_type"] == "none" ): return # if on treatment for severe malaria, will reduce probability of death # use random number generator - currently param treatment_adjustment set to 0.5 - if df.at[individual_id, "ma_tx"] == "complicated": + if df.at[person_id, "ma_tx"] == "complicated": prob = self.module.rng.rand() # if draw -> death if prob < self.module.parameters["treatment_adjustment"]: self.sim.modules["Demography"].do_death( - individual_id=individual_id, + person_id=person_id, cause=self.cause, originating_module=self.module, ) - df.at[individual_id, "ma_date_death"] = self.sim.date + df.at[person_id, "ma_date_death"] = self.sim.date # else if draw does not result in death -> cure else: - df.at[individual_id, "ma_tx"] = "none" - df.at[individual_id, "ma_inf_type"] = "none" - df.at[individual_id, "ma_is_infected"] = False + df.at[person_id, "ma_tx"] = "none" + df.at[person_id, "ma_inf_type"] = "none" + df.at[person_id, "ma_is_infected"] = False # clear symptoms self.sim.modules["SymptomManager"].clear_symptoms( - person_id=individual_id, disease_module=self.module + person_id=person_id, disease_module=self.module ) # if not on treatment - death will occur else: self.sim.modules["Demography"].do_death( - individual_id=individual_id, + person_id=person_id, cause=self.cause, originating_module=self.module, ) - df.at[individual_id, "ma_date_death"] = self.sim.date + df.at[person_id, "ma_date_death"] = self.sim.date # --------------------------------------------------------------------------------- From df2b77ea4d98f9eb49c94dd245f767081d483847 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 13 Feb 2024 16:04:57 +0000 Subject: [PATCH 013/131] use individual_id for demography.do_death() --- src/tlo/methods/malaria.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 9992c25888..9242657bd6 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -960,7 +960,7 @@ def apply(self, person_id): # if draw -> death if prob < self.module.parameters["treatment_adjustment"]: self.sim.modules["Demography"].do_death( - person_id=person_id, + individual_id=person_id, cause=self.cause, originating_module=self.module, ) @@ -981,7 +981,7 @@ def apply(self, person_id): # if not on treatment - death will occur else: self.sim.modules["Demography"].do_death( - person_id=person_id, + individual_id=person_id, cause=self.cause, originating_module=self.module, ) From b2bd355175b278b46a0296f2226ade7c2c587d48 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 14 Feb 2024 10:47:06 +0000 Subject: [PATCH 014/131] style change to avoid conflicts with master --- src/tlo/methods/malaria.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 9242657bd6..42a7575ff7 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -215,7 +215,7 @@ def __init__(self, name=None, resourcefilepath=None): } def read_parameters(self, data_folder): - workbook = pd.read_excel(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria.xlsx', sheet_name=None) + workbook = pd.read_excel(self.resourcefilepath / "malaria" / "ResourceFile_malaria.xlsx", sheet_name=None) self.load_parameters_from_dataframe(workbook["parameters"]) @@ -229,9 +229,9 @@ def read_parameters(self, data_folder): p["sev_symp_prob"] = workbook["severe_symptoms"] p["rdt_testing_rates"] = workbook["WHO_TestData2023"] - p['inf_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_InfInc_expanded.csv') - p['clin_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_ClinInc_expanded.csv') - p['sev_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_SevInc_expanded.csv') + p["inf_inc"] = pd.read_csv(self.resourcefilepath / "malaria" / "ResourceFile_malaria_InfInc_expanded.csv") + p["clin_inc"] = pd.read_csv(self.resourcefilepath / "malaria" / "ResourceFile_malaria_ClinInc_expanded.csv") + p["sev_inc"] = pd.read_csv(self.resourcefilepath / "malaria" / "ResourceFile_malaria_SevInc_expanded.csv") # check itn projected values are <=0.7 and rounded to 1dp for matching to incidence tables p["itn"] = round(p["itn"], 1) @@ -290,7 +290,7 @@ def read_parameters(self, data_folder): sev_inc = sev_inc.loc[:, ["monthly_prob_sev"]] all_inc = pd.concat([inf_inc, clin_inc, sev_inc], axis=1) - # we don't want age to be part of index + # we don"t want age to be part of index all_inc = all_inc.reset_index() all_inc["district_num"] = all_inc["admin"].map(mapper_district_name_to_num) @@ -426,7 +426,7 @@ def malaria_poll2(self, population): itn_irs_curr = self.itn_irs.loc[pd.IndexSlice[:, current_year], :] itn_irs_curr = itn_irs_curr.reset_index().drop( "Year", axis=1 - ) # we don't use the year column + ) # we don"t use the year column itn_irs_curr.insert( 0, "month", now.month ) # add current month for the incidence index lookup @@ -459,7 +459,7 @@ def _draw_incidence_for(_col, _where): ) # get the monthly incidence probabilities for these individuals monthly_prob = curr_inc.loc[district_age_lookup, _col] - # update the index so it's the same as the original population dataframe for these individuals + # update the index so it"s the same as the original population dataframe for these individuals monthly_prob = monthly_prob.set_axis(df.index[_where]) # the linear models only apply to clinical and severe malaria risk @@ -485,7 +485,7 @@ def _draw_incidence_for(_col, _where): return selected - # we don't have incidence data for over 80s + # we don"t have incidence data for over 80s alive = df.is_alive & (df.age_years < 80) alive_over_one = alive & (df.age_exact_years >= 1) @@ -723,7 +723,7 @@ def on_birth(self, mother_id, child_id): df.at[child_id, "ma_tx_counter"] = 0 df.at[child_id, "ma_iptp"] = False - # reset mother's IPTp status to False + # reset mother"s IPTp status to False if mother_id >= 0: # exclude direct births df.at[mother_id, "ma_iptp"] = False @@ -814,7 +814,7 @@ def do_for_suspected_malaria_case(self, person_id, hsi_event): tclose=None, ) - # return type 'clinical_malaria' includes asymptomatic infection + # return type "clinical_malaria" includes asymptomatic infection elif malaria_test_result == "clinical_malaria": df.at[person_id, "ma_dx_counter"] += 1 self.sim.modules["HealthSystem"].schedule_hsi_event( @@ -892,7 +892,7 @@ def apply(self, population): & ~df.ma_iptp & ( ~df.hv_on_cotrimoxazole - if 'Hiv' in self.sim.modules + if "Hiv" in self.sim.modules else True ) ) @@ -1380,7 +1380,7 @@ def apply(self, person_id, squeeze_factor): return # IPTp contra-indicated if currently on cotrimoxazole - if 'Hiv' in self.sim.modules and df.at[person_id, "hv_on_cotrimoxazole"]: + if "Hiv" in self.sim.modules and df.at[person_id, "hv_on_cotrimoxazole"]: return logger.debug( From 09b9b661635ac362c541aa62a919c4a92c9594b4 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 14 Feb 2024 10:47:27 +0000 Subject: [PATCH 015/131] style change to avoid conflicts with master --- tests/test_malaria.py | 198 +++++++++++++++++++++--------------------- 1 file changed, 99 insertions(+), 99 deletions(-) diff --git a/tests/test_malaria.py b/tests/test_malaria.py index 049a6b07bd..bf3239e860 100644 --- a/tests/test_malaria.py +++ b/tests/test_malaria.py @@ -74,7 +74,7 @@ def test_sims(sim): check_dtypes(sim) # IRS rates should be 0 or 0.8 - assert (sim.modules['Malaria'].itn_irs.irs_rate.isin([0, 0.8])).all() + assert (sim.modules["Malaria"].itn_irs.irs_rate.isin([0, 0.8])).all() # check malaria deaths only being scheduled due to severe malaria (not clinical or asym) df = sim.population.props @@ -128,7 +128,7 @@ def test_remove_malaria_test(seed): resourcefilepath=resourcefilepath, service_availability=service_availability, mode_appt_constraints=0, - cons_availability='all', + cons_availability="all", ignore_priority=True, capabilities_coefficient=0.0, disable=False, # disables the health system constraints so all HSI events run @@ -147,10 +147,10 @@ def test_remove_malaria_test(seed): sim.make_initial_population(n=2000) # set testing adjustment to 0 - sim.modules['Malaria'].parameters['testing_adj'] = 0 + sim.modules["Malaria"].parameters["testing_adj"] = 0 # increase death rate due to severe malaria - sim.modules['Malaria'].parameters['cfr'] = 1.0 + sim.modules["Malaria"].parameters["cfr"] = 1.0 sim.simulate(end_date=end_date) check_dtypes(sim) @@ -162,20 +162,20 @@ def test_remove_malaria_test(seed): # Check malaria cases occurring assert 0 < df.ma_clinical_counter.sum() - assert (df['ma_date_infected'] != pd.NaT).all() - assert (df['ma_date_symptoms'] != pd.NaT).all() - assert (df['ma_date_death'] != pd.NaT).all() + assert (df["ma_date_infected"] != pd.NaT).all() + assert (df["ma_date_symptoms"] != pd.NaT).all() + assert (df["ma_date_death"] != pd.NaT).all() # check all with severe malaria are assigned death date for person in df.index[(df.ma_inf_type == "severe")]: assert not pd.isnull(df.at[person, "ma_date_death"]) # Check deaths are occurring - assert (df.cause_of_death.loc[~df.is_alive & ~df.date_of_birth.isna()].isin({'severe_malaria', 'Malaria'})).any() + assert (df.cause_of_death.loc[~df.is_alive & ~df.date_of_birth.isna()].isin({"severe_malaria", "Malaria"})).any() # Check that those with a scheduled malaria death in the past, are now dead with a cause of death severe_malaria dead_due_to_malaria = ~df.is_alive & ~df.date_of_birth.isna() & df.cause_of_death.isin( - {'severe_malaria', 'Malaria'}) + {"severe_malaria", "Malaria"}) malaria_death_date_in_past = ~pd.isnull(df.ma_date_death) & (df.ma_date_death <= sim.date) assert (dead_due_to_malaria == malaria_death_date_in_past).all() @@ -185,7 +185,7 @@ def test_remove_malaria_test(seed): def test_schedule_rdt_for_all(sim): # Run the simulation and flush the logger sim.make_initial_population(n=popsize) - sim.modules['Malaria'].parameters['prob_malaria_case_tests'] = 10 + sim.modules["Malaria"].parameters["prob_malaria_case_tests"] = 10 sim.simulate(end_date=end_date) check_dtypes(sim) @@ -203,28 +203,28 @@ def _setup_simulation_for_dx_algorithm_test(sim): popsize = 200 # smallest population size that works sim.make_initial_population(n=popsize) - sim.modules['Malaria'].parameters['sensitivity_rdt'] = 1.0 + sim.modules["Malaria"].parameters["sensitivity_rdt"] = 1.0 sim.simulate(end_date=start_date) # Create the HSI event that is notionally doing the call on diagnostic algorithm class DummyHSIEvent(HSI_Event, IndividualScopeEventMixin): def __init__(self, module, person_id): super().__init__(module, person_id=person_id) - self.TREATMENT_ID = 'DummyHSIEvent' + self.TREATMENT_ID = "DummyHSIEvent" the_appt_footprint = self.sim.modules["HealthSystem"].get_blank_appt_footprint() the_appt_footprint["Under5OPD"] = 1 # This requires one out patient self.EXPECTED_APPT_FOOTPRINT = the_appt_footprint - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.ACCEPTED_FACILITY_LEVEL = "1a" def apply(self, person_id, squeeze_factor): pass - hsi_event = DummyHSIEvent(module=sim.modules['Malaria'], person_id=0) + hsi_event = DummyHSIEvent(module=sim.modules["Malaria"], person_id=0) # check that the queue of events is empty - assert 0 == len(sim.modules['HealthSystem'].HSI_EVENT_QUEUE) + assert 0 == len(sim.modules["HealthSystem"].HSI_EVENT_QUEUE) return sim, hsi_event @@ -239,26 +239,26 @@ def test_dx_algorithm_for_malaria_outcomes_clinical(sim): # Set up the person - clinical malaria and aged <5 years: df = sim.population.props - df.at[0, 'ma_is_infected'] = True - df.at[0, 'ma_date_infected'] = sim.date - df.at[0, 'ma_date_symptoms'] = sim.date - df.at[0, 'ma_inf_type'] = 'clinical' + df.at[0, "ma_is_infected"] = True + df.at[0, "ma_date_infected"] = sim.date + df.at[0, "ma_date_symptoms"] = sim.date + df.at[0, "ma_inf_type"] = "clinical" symptom_list = {"fever", "headache", "vomiting", "stomachache"} for symptom in symptom_list: # no symptom resolution - sim.modules['SymptomManager'].change_symptom( + sim.modules["SymptomManager"].change_symptom( person_id=0, symptom_string=symptom, - disease_module=sim.modules['Malaria'], - add_or_remove='+' + disease_module=sim.modules["Malaria"], + add_or_remove="+" ) person_id = 0 assert "fever" in sim.modules["SymptomManager"].has_what(person_id) - assert sim.modules['Malaria'].check_if_fever_is_caused_by_malaria( + assert sim.modules["Malaria"].check_if_fever_is_caused_by_malaria( person_id=0, hsi_event=hsi_event ) == "clinical_malaria" @@ -276,25 +276,25 @@ def test_dx_algorithm_for_malaria_outcomes_severe(sim): df = sim.population.props person_id = 1 - df.at[person_id, 'ma_is_infected'] = True - df.at[person_id, 'ma_date_infected'] = sim.date - df.at[person_id, 'ma_date_symptoms'] = sim.date - df.at[person_id, 'ma_inf_type'] = 'severe' + df.at[person_id, "ma_is_infected"] = True + df.at[person_id, "ma_date_infected"] = sim.date + df.at[person_id, "ma_date_symptoms"] = sim.date + df.at[person_id, "ma_inf_type"] = "severe" symptom_list = {"fever", "headache", "vomiting", "stomachache"} for symptom in symptom_list: # no symptom resolution - sim.modules['SymptomManager'].change_symptom( + sim.modules["SymptomManager"].change_symptom( person_id=person_id, symptom_string=symptom, - disease_module=sim.modules['Malaria'], - add_or_remove='+' + disease_module=sim.modules["Malaria"], + add_or_remove="+" ) assert "fever" in sim.modules["SymptomManager"].has_what(person_id) - assert sim.modules['Malaria'].check_if_fever_is_caused_by_malaria( + assert sim.modules["Malaria"].check_if_fever_is_caused_by_malaria( person_id=person_id, hsi_event=hsi_event ) == "severe_malaria" @@ -341,23 +341,23 @@ def make_blank_simulation(): class DummyHSIEvent(HSI_Event, IndividualScopeEventMixin): def __init__(self, module, person_id): super().__init__(module, person_id=person_id) - self.TREATMENT_ID = 'DummyHSIEvent' + self.TREATMENT_ID = "DummyHSIEvent" the_appt_footprint = self.sim.modules["HealthSystem"].get_blank_appt_footprint() the_appt_footprint["Under5OPD"] = 1 # This requires one out patient self.EXPECTED_APPT_FOOTPRINT = the_appt_footprint - self.ACCEPTED_FACILITY_LEVEL = '1a' + self.ACCEPTED_FACILITY_LEVEL = "1a" self.ALERT_OTHER_DISEASES = [] def apply(self, person_id, squeeze_factor): pass # assume diarrhoea has created a fever (as an example of a non-malarial fever) - hsi_event = DummyHSIEvent(module=sim.modules['Diarrhoea'], person_id=0) + hsi_event = DummyHSIEvent(module=sim.modules["Diarrhoea"], person_id=0) # check that the queue of events is empty - assert 0 == len(sim.modules['HealthSystem'].HSI_EVENT_QUEUE) + assert 0 == len(sim.modules["HealthSystem"].HSI_EVENT_QUEUE) return sim, hsi_event @@ -370,16 +370,16 @@ def apply(self, person_id, squeeze_factor): person_id = 0 sim.population.props.loc[person_id, ["ma_is_infected", " ma_inf_type"]] = (False, "none") - sim.modules['SymptomManager'].change_symptom( + sim.modules["SymptomManager"].change_symptom( person_id=person_id, symptom_string="fever", - disease_module=sim.modules['Diarrhoea'], - add_or_remove='+' + disease_module=sim.modules["Diarrhoea"], + add_or_remove="+" ) assert "fever" in sim.modules["SymptomManager"].has_what(person_id) - assert sim.modules['Malaria'].check_if_fever_is_caused_by_malaria( + assert sim.modules["Malaria"].check_if_fever_is_caused_by_malaria( person_id=0, hsi_event=hsi_event ) == "negative_malaria_test" @@ -388,7 +388,7 @@ def apply(self, person_id, squeeze_factor): def test_severe_malaria_deaths_perfect_treatment(sim): # -------------- Perfect treatment for severe malaria -------------- # # set perfect treatment for severe malaria cases - no deaths should occur - sim.modules['Malaria'].parameters['treatment_adjustment'] = 0 + sim.modules["Malaria"].parameters["treatment_adjustment"] = 0 # Run the simulation and flush the logger sim.make_initial_population(n=10) @@ -403,19 +403,19 @@ def test_severe_malaria_deaths_perfect_treatment(sim): # put person on treatment treatment_appt = malaria.HSI_Malaria_Treatment_Complicated(person_id=person_id, - module=sim.modules['Malaria']) + module=sim.modules["Malaria"]) treatment_appt.apply(person_id=person_id, squeeze_factor=0.0) - assert df.at[person_id, 'ma_tx'] != 'none' + assert df.at[person_id, "ma_tx"] != "none" assert df.at[person_id, "ma_date_tx"] == sim.date assert df.at[person_id, "ma_tx_counter"] > 0 # run the death event death_event = malaria.MalariaDeathEvent( - module=sim.modules['Malaria'], person_id=person_id, cause="Malaria") + module=sim.modules["Malaria"], person_id=person_id, cause="Malaria") death_event.apply(person_id) # should not cause death but result in cure - assert df.at[person_id, 'is_alive'] + assert df.at[person_id, "is_alive"] assert df.at[person_id, "ma_inf_type"] == "none" @@ -423,7 +423,7 @@ def test_severe_malaria_deaths_treatment_failure(sim): # -------------- treatment failure for severe malaria -------------- # # set treatment with zero efficacy for severe malaria cases - death should occur - sim.modules['Malaria'].parameters['treatment_adjustment'] = 1 + sim.modules["Malaria"].parameters["treatment_adjustment"] = 1 # Run the simulation and flush the logger sim.make_initial_population(n=10) @@ -438,20 +438,20 @@ def test_severe_malaria_deaths_treatment_failure(sim): # put person on treatment treatment_appt = malaria.HSI_Malaria_Treatment_Complicated(person_id=person_id, - module=sim.modules['Malaria']) + module=sim.modules["Malaria"]) treatment_appt.apply(person_id=person_id, squeeze_factor=0.0) - assert df.at[person_id, 'ma_tx'] != 'none' + assert df.at[person_id, "ma_tx"] != "none" assert df.at[person_id, "ma_date_tx"] == sim.date assert df.at[person_id, "ma_tx_counter"] > 0 # run the death event death_event = malaria.MalariaDeathEvent( - module=sim.modules['Malaria'], person_id=person_id, cause="Malaria") + module=sim.modules["Malaria"], person_id=person_id, cause="Malaria") death_event.apply(person_id) # should cause death - no cure - assert not df.at[person_id, 'is_alive'] - assert df.at[person_id, 'cause_of_death'] == "Malaria" + assert not df.at[person_id, "is_alive"] + assert df.at[person_id, "cause_of_death"] == "Malaria" # -------------- no treatment for severe malaria -------------- # # set treatment with zero efficacy for severe malaria cases - death should occur @@ -459,18 +459,18 @@ def test_severe_malaria_deaths_treatment_failure(sim): person_id = 1 df.loc[person_id, ["ma_is_infected", "ma_inf_type"]] = (True, "severe") - assert df.at[person_id, 'ma_tx'] == 'none' + assert df.at[person_id, "ma_tx"] == "none" assert df.at[person_id, "ma_date_tx"] is pd.NaT assert df.at[person_id, "ma_tx_counter"] == 0 # run the death event death_event = malaria.MalariaDeathEvent( - module=sim.modules['Malaria'], person_id=person_id, cause="Malaria") + module=sim.modules["Malaria"], person_id=person_id, cause="Malaria") death_event.apply(person_id) # should cause death - no cure - assert not df.at[person_id, 'is_alive'] - assert df.at[person_id, 'cause_of_death'] == "Malaria" + assert not df.at[person_id, "is_alive"] + assert df.at[person_id, "cause_of_death"] == "Malaria" def get_sim(seed): @@ -509,7 +509,7 @@ def test_individual_testing_and_treatment(sim): sim = get_sim(seed) - sim.modules['Malaria'].parameters['prob_malaria_case_tests'] = 1.0 # all cases referred for rdt + sim.modules["Malaria"].parameters["prob_malaria_case_tests"] = 1.0 # all cases referred for rdt sim.modules["Malaria"].parameters["sensitivity_rdt"] = 1.0 # Run the simulation and flush the logger @@ -523,22 +523,22 @@ def test_individual_testing_and_treatment(sim): person_id = 0 # assign person_id malaria infection - df.at[person_id, 'ma_is_infected'] = True - df.at[person_id, 'ma_date_infected'] = sim.date - df.at[person_id, 'ma_date_symptoms'] = sim.date - df.at[person_id, 'ma_inf_type'] = "clinical" - df.at[person_id, 'age_years'] = 3 + df.at[person_id, "ma_is_infected"] = True + df.at[person_id, "ma_date_infected"] = sim.date + df.at[person_id, "ma_date_symptoms"] = sim.date + df.at[person_id, "ma_inf_type"] = "clinical" + df.at[person_id, "age_years"] = 3 # assign clinical symptoms and schedule rdt - pollevent = malaria.MalariaUpdateEvent(module=sim.modules['Malaria']) + pollevent = malaria.MalariaUpdateEvent(module=sim.modules["Malaria"]) pollevent.run() assert not pd.isnull(df.at[person_id, "ma_date_symptoms"]) - assert set(sim.modules['SymptomManager'].has_what(person_id)) == {"fever", "headache", "vomiting", "stomachache"} + assert set(sim.modules["SymptomManager"].has_what(person_id)) == {"fever", "headache", "vomiting", "stomachache"} # check rdt is scheduled date_event, event = [ - ev for ev in sim.modules['HealthSystem'].find_events_for_person(person_id) if + ev for ev in sim.modules["HealthSystem"].find_events_for_person(person_id) if isinstance(ev[1], malaria.HSI_Malaria_rdt) ][0] assert date_event > sim.date @@ -551,7 +551,7 @@ def test_individual_testing_and_treatment(sim): # check treatment event is scheduled date_event, tx_event = [ - ev for ev in sim.modules['HealthSystem'].find_events_for_person(person_id) if + ev for ev in sim.modules["HealthSystem"].find_events_for_person(person_id) if isinstance(ev[1], malaria.HSI_Malaria_Treatment) ][0] assert date_event >= sim.date @@ -561,30 +561,30 @@ def test_individual_testing_and_treatment(sim): tx_event.run(squeeze_factor=0.0) assert df.at[person_id, "ma_tx_counter"] == 1 - assert df.at[person_id, "ma_tx"] != 'none' + assert df.at[person_id, "ma_tx"] != "none" # -------- asymptomatic infection person_id = 1 # assign person_id malaria - df.at[person_id, 'ma_is_infected'] = True - df.at[person_id, 'ma_date_infected'] = sim.date - df.at[person_id, 'ma_date_symptoms'] = sim.date - df.at[person_id, 'ma_inf_type'] = "asym" - df.at[person_id, 'age_years'] = 3 + df.at[person_id, "ma_is_infected"] = True + df.at[person_id, "ma_date_infected"] = sim.date + df.at[person_id, "ma_date_symptoms"] = sim.date + df.at[person_id, "ma_inf_type"] = "asym" + df.at[person_id, "age_years"] = 3 # check no clinical symptoms set and no rdt scheduled - pollevent = malaria.MalariaUpdateEvent(module=sim.modules['Malaria']) + pollevent = malaria.MalariaUpdateEvent(module=sim.modules["Malaria"]) pollevent.apply(sim.population) - assert sim.modules['SymptomManager'].has_what(person_id) == [] + assert sim.modules["SymptomManager"].has_what(person_id) == [] # check no rdt is scheduled - assert "malaria.HSI_Malaria_rdt" not in sim.modules['HealthSystem'].find_events_for_person(person_id) + assert "malaria.HSI_Malaria_rdt" not in sim.modules["HealthSystem"].find_events_for_person(person_id) # screen and test person_id rdt_appt = malaria.HSI_Malaria_rdt(person_id=person_id, - module=sim.modules['Malaria']) + module=sim.modules["Malaria"]) rdt_appt.apply(person_id=person_id, squeeze_factor=0.0) # check person diagnosed (with asym infection but no clinical symptoms) @@ -592,7 +592,7 @@ def test_individual_testing_and_treatment(sim): # check treatment event is scheduled date_event, event = [ - ev for ev in sim.modules['HealthSystem'].find_events_for_person(person_id) if + ev for ev in sim.modules["HealthSystem"].find_events_for_person(person_id) if isinstance(ev[1], malaria.HSI_Malaria_Treatment) ][0] assert date_event >= sim.date @@ -600,38 +600,38 @@ def test_individual_testing_and_treatment(sim): # run treatment event and check person is treated and treatment counter incremented assert df.at[person_id, "ma_tx_counter"] == 0 tx_appt = malaria.HSI_Malaria_Treatment(person_id=person_id, - module=sim.modules['Malaria']) + module=sim.modules["Malaria"]) tx_appt.apply(person_id=person_id, squeeze_factor=0.0) assert df.at[person_id, "ma_tx_counter"] == 1 - assert df.at[person_id, "ma_tx"] != 'none' + assert df.at[person_id, "ma_tx"] != "none" # -------- severe infection person_id = 2 # assign person_id malaria - df.at[person_id, 'ma_is_infected'] = True - df.at[person_id, 'ma_date_infected'] = sim.date - df.at[person_id, 'ma_date_symptoms'] = sim.date - df.at[person_id, 'ma_inf_type'] = "severe" - df.at[person_id, 'age_years'] = 3 + df.at[person_id, "ma_is_infected"] = True + df.at[person_id, "ma_date_infected"] = sim.date + df.at[person_id, "ma_date_symptoms"] = sim.date + df.at[person_id, "ma_inf_type"] = "severe" + df.at[person_id, "age_years"] = 3 # assign clinical symptoms and schedule rdt - pollevent = malaria.MalariaUpdateEvent(module=sim.modules['Malaria']) + pollevent = malaria.MalariaUpdateEvent(module=sim.modules["Malaria"]) pollevent.apply(sim.population) assert not pd.isnull(df.at[person_id, "ma_date_symptoms"]) # check rdt is scheduled date_event, event = [ - ev for ev in sim.modules['HealthSystem'].find_events_for_person(person_id) if + ev for ev in sim.modules["HealthSystem"].find_events_for_person(person_id) if isinstance(ev[1], malaria.HSI_Malaria_rdt) ][0] assert date_event > sim.date # screen and test person_id rdt_appt = malaria.HSI_Malaria_rdt(person_id=person_id, - module=sim.modules['Malaria']) + module=sim.modules["Malaria"]) rdt_appt.apply(person_id=person_id, squeeze_factor=0.0) # check person diagnosed @@ -639,7 +639,7 @@ def test_individual_testing_and_treatment(sim): # check treatment event is scheduled date_event, event = [ - ev for ev in sim.modules['HealthSystem'].find_events_for_person(person_id) if + ev for ev in sim.modules["HealthSystem"].find_events_for_person(person_id) if isinstance(ev[1], malaria.HSI_Malaria_Treatment_Complicated) ][0] assert date_event >= sim.date @@ -647,11 +647,11 @@ def test_individual_testing_and_treatment(sim): # run treatment event and check person is treated and treatment counter incremented assert df.at[person_id, "ma_tx_counter"] == 0 tx_appt = malaria.HSI_Malaria_Treatment_Complicated(person_id=person_id, - module=sim.modules['Malaria']) + module=sim.modules["Malaria"]) tx_appt.apply(person_id=person_id, squeeze_factor=0.0) assert df.at[person_id, "ma_tx_counter"] == 1 - assert df.at[person_id, "ma_tx"] != 'none' + assert df.at[person_id, "ma_tx"] != "none" def test_population_testing_and_treatment(sim): @@ -662,7 +662,7 @@ def test_population_testing_and_treatment(sim): sim = get_sim(seed) - sim.modules['Malaria'].parameters['prob_malaria_case_tests'] = 1.0 # all cases referred for rdt + sim.modules["Malaria"].parameters["prob_malaria_case_tests"] = 1.0 # all cases referred for rdt sim.modules["Malaria"].parameters["sensitivity_rdt"] = 1.0 pop = 100 @@ -674,7 +674,7 @@ def test_population_testing_and_treatment(sim): df = sim.population.props # Make no-one has malaria and clear the event queues: - sim.modules['HealthSystem'].HSI_EVENT_QUEUE = [] + sim.modules["HealthSystem"].HSI_EVENT_QUEUE = [] sim.event_queue.queue = [] df.loc[df.is_alive, "ma_is_infected"] = True @@ -685,7 +685,7 @@ def test_population_testing_and_treatment(sim): idx = list(df.loc[df.is_alive].index) # assign clinical symptoms and schedule rdt - pollevent = malaria.MalariaUpdateEvent(module=sim.modules['Malaria']) + pollevent = malaria.MalariaUpdateEvent(module=sim.modules["Malaria"]) pollevent.apply(sim.population) assert df["ma_clinical_counter"].sum() == pop @@ -693,14 +693,14 @@ def test_population_testing_and_treatment(sim): # check one rdt is scheduled for each person in idx for person in idx: assert 1 == len([ - ev[0] for ev in sim.modules['HealthSystem'].find_events_for_person(person_id=person) if + ev[0] for ev in sim.modules["HealthSystem"].find_events_for_person(person_id=person) if (isinstance(ev[1], malaria.HSI_Malaria_rdt) & (ev[0] >= sim.date)) ]) # run the rdt for everyone for person in idx: rdt_appt = malaria.HSI_Malaria_rdt(person_id=person, - module=sim.modules['Malaria']) + module=sim.modules["Malaria"]) rdt_appt.apply(person_id=person, squeeze_factor=0.0) assert df.loc[df.is_alive, "ma_clinical_counter"].sum() == len(df.loc[df.is_alive]) @@ -708,14 +708,14 @@ def test_population_testing_and_treatment(sim): # check 10 treatment events are scheduled for person in idx: assert 1 == len([ - ev[0] for ev in sim.modules['HealthSystem'].find_events_for_person(person_id=person) if + ev[0] for ev in sim.modules["HealthSystem"].find_events_for_person(person_id=person) if (isinstance(ev[1], malaria.HSI_Malaria_Treatment) & (ev[0] >= sim.date)) ]) # run the treatment for everyone for person in idx: tx_appt = malaria.HSI_Malaria_Treatment(person_id=person, - module=sim.modules['Malaria']) + module=sim.modules["Malaria"]) tx_appt.apply(person_id=person, squeeze_factor=0.0) assert df["ma_tx_counter"].sum() == pop @@ -726,10 +726,10 @@ def test_linear_model_for_clinical_malaria(sim): # -------------- Perfect protection through IPTp -------------- # # set perfect protection for IPTp against clinical malaria - no cases should occur - sim.modules['Malaria'].parameters['rr_clinical_malaria_iptp'] = 0 + sim.modules["Malaria"].parameters["rr_clinical_malaria_iptp"] = 0 # set clinical incidence probability to very high value - sim.modules['Malaria'].parameters['clin_inc']['monthly_prob_clin'] = 0.99 + sim.modules["Malaria"].parameters["clin_inc"]["monthly_prob_clin"] = 0.99 # Run the simulation and flush the logger sim.make_initial_population(n=10) @@ -747,8 +747,8 @@ def test_linear_model_for_clinical_malaria(sim): df.loc[df.is_alive, "ma_iptp"] = True # run malaria poll - pollevent = malaria.MalariaPollingEventDistrict(module=sim.modules['Malaria']) + pollevent = malaria.MalariaPollingEventDistrict(module=sim.modules["Malaria"]) pollevent.run() # make sure no-one assigned clinical or severe malaria - assert not (df.loc[df.is_alive, 'ma_inf_type'].isin({'clinical', 'severe'})).any() + assert not (df.loc[df.is_alive, "ma_inf_type"].isin({"clinical", "severe"})).any() From 758955e3a2291bab16b970f2fb0112aefaaa5eb5 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 14 Feb 2024 18:11:00 +0000 Subject: [PATCH 016/131] fix conflicts with master --- src/tlo/methods/hiv.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 3021f27f50..e3e1412b1a 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -382,6 +382,10 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): Types.INT, "number of repeat visits assumed for healthcare services", ), + "dispensation_period_months": Parameter( + Types.REAL, + "length of prescription for ARVs in months, same for all PLHIV", + ), } def read_parameters(self, data_folder): @@ -2716,6 +2720,12 @@ def apply(self, person_id, squeeze_factor): # Confirm that the person is diagnosed (this should not run if they are not) assert person["hv_diagnosed"] + # check whether person had Rx at least 3 months ago and is now due repeat prescription + # alternate routes into testing/tx may mean person already has recent ARV dispensation + if person['hv_date_last_ART'] >= ( + self.sim.date - pd.DateOffset(months=self.module.parameters['dispensation_period_months'])): + return self.sim.modules["HealthSystem"].get_blank_appt_footprint() + if art_status_at_beginning_of_hsi == "not": assert person[ @@ -2735,7 +2745,7 @@ def apply(self, person_id, squeeze_factor): Hiv_DecisionToContinueTreatment( person_id=person_id, module=self.module ), - self.sim.date + pd.DateOffset(months=3), + self.sim.date + pd.DateOffset(months=self.module.parameters['dispensation_period_months']), ) else: # logger for drugs not available From f7ac7e28dd41719f7ab3d6a5d157a3f3df2c757a Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 14 Feb 2024 18:11:14 +0000 Subject: [PATCH 017/131] fix conflicts with master --- resources/ResourceFile_HIV.xlsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index 96f361dece..9eb4f364b4 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3ad1b77dc635a0df8d552da3f054856c3ef4feccb7f2e26280d3a1ac4c3d336 -size 160604 +oid sha256:ca9100bd4c97a8a28c38a64a897ada58a4cd915b555f4d86e94cc5fa1e24ad3a +size 160625 From 9690949d72be33a2bf57b1e246b21802c3193f69 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 14 Feb 2024 18:16:49 +0000 Subject: [PATCH 018/131] merge in master --- src/tlo/methods/hiv.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index e3e1412b1a..095b4d1cca 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -2740,6 +2740,7 @@ def apply(self, person_id, squeeze_factor): # if ART is available (1st item in drugs_were_available dict) if list(drugs_were_available.values())[0]: + # If person has been placed/continued on ART, schedule 'decision about whether to continue on Treatment self.sim.schedule_event( Hiv_DecisionToContinueTreatment( From 05c69ff386d5909160cfec029c7d977f0ca4d0cb Mon Sep 17 00:00:00 2001 From: tdm32 Date: Thu, 15 Feb 2024 10:53:45 +0000 Subject: [PATCH 019/131] check property hv_date_last_ART correctly set --- src/tlo/methods/hiv.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 095b4d1cca..715df2dc0e 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -127,6 +127,7 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "hv_last_test_date": Property(Types.DATE, "Date of last HIV test"), "hv_date_inf": Property(Types.DATE, "Date infected with HIV"), "hv_date_treated": Property(Types.DATE, "date hiv treatment started"), + "hv_date_last_ART": Property(Types.DATE, "date of last ART dispensation"), } PARAMETERS = { @@ -589,7 +590,6 @@ def initialise_population(self, population): # --- Current status df.loc[df.is_alive, "hv_inf"] = False df.loc[df.is_alive, "hv_art"] = "not" - df.loc[df.is_alive, "hv_date_treated"] = pd.NaT df.loc[df.is_alive, "hv_is_on_prep"] = False df.loc[df.is_alive, "hv_behaviour_change"] = False df.loc[df.is_alive, "hv_diagnosed"] = False @@ -598,6 +598,8 @@ def initialise_population(self, population): # --- Dates on which things have happened df.loc[df.is_alive, "hv_date_inf"] = pd.NaT df.loc[df.is_alive, "hv_last_test_date"] = pd.NaT + df.loc[df.is_alive, "hv_date_treated"] = pd.NaT + df.loc[df.is_alive, "hv_date_last_ART"] = pd.NaT # Launch sub-routines for allocating the right number of people into each category self.initialise_baseline_prevalence(population) # allocate baseline prevalence @@ -1122,7 +1124,6 @@ def on_birth(self, mother_id, child_id): df.at[child_id, "hv_inf"] = False df.at[child_id, "hv_art"] = "not" df.at[child_id, "hv_on_cotrimoxazole"] = False - df.at[child_id, "hv_date_treated"] = pd.NaT df.at[child_id, "hv_is_on_prep"] = False df.at[child_id, "hv_behaviour_change"] = False df.at[child_id, "hv_diagnosed"] = False @@ -1131,6 +1132,8 @@ def on_birth(self, mother_id, child_id): # --- Dates on which things have happened df.at[child_id, "hv_date_inf"] = pd.NaT df.at[child_id, "hv_last_test_date"] = pd.NaT + df.at[child_id, "hv_date_treated"] = pd.NaT + df.at[child_id, "hv_date_last_ART"] = pd.NaT # ----------------------------------- MTCT - AT OR PRIOR TO BIRTH -------------------------- # DETERMINE IF THE CHILD IS INFECTED WITH HIV FROM THEIR MOTHER DURING PREGNANCY / DELIVERY @@ -2740,6 +2743,7 @@ def apply(self, person_id, squeeze_factor): # if ART is available (1st item in drugs_were_available dict) if list(drugs_were_available.values())[0]: + df.at[person_id, 'hv_date_last_ART'] = self.sim.date # If person has been placed/continued on ART, schedule 'decision about whether to continue on Treatment self.sim.schedule_event( From 3e3c0d4d077ec9c690deb9906c0be22b1a8ff87f Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 16 Feb 2024 10:53:57 +0000 Subject: [PATCH 020/131] Manually add PostnatalCare_Comprehensive to policy priorities --- tests/test_healthsystem.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_healthsystem.py b/tests/test_healthsystem.py index 17440968f5..8feb03ca58 100644 --- a/tests/test_healthsystem.py +++ b/tests/test_healthsystem.py @@ -130,6 +130,7 @@ def test_all_treatment_ids_defined_in_priority_policies(seed, tmpdir): clean_set_of_filtered_treatment_ids.add("Alri_Pneumonia_Treatment_Inpatient") clean_set_of_filtered_treatment_ids.add("Alri_Pneumonia_Treatment_Inpatient_Followup") clean_set_of_filtered_treatment_ids.add("DeliveryCare_Comprehensive") + clean_set_of_filtered_treatment_ids.add("PostnatalCare_Comprehensive") for policy_name in sim.modules['HealthSystem'].parameters['priority_rank'].keys(): sim.modules['HealthSystem'].load_priority_policy(policy_name) From cfd91661309d614f22fd3d6f122cfea4d60ed826 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 16 Feb 2024 11:01:30 +0000 Subject: [PATCH 021/131] edit fix --- tests/test_healthsystem.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_healthsystem.py b/tests/test_healthsystem.py index 8feb03ca58..17440968f5 100644 --- a/tests/test_healthsystem.py +++ b/tests/test_healthsystem.py @@ -130,7 +130,6 @@ def test_all_treatment_ids_defined_in_priority_policies(seed, tmpdir): clean_set_of_filtered_treatment_ids.add("Alri_Pneumonia_Treatment_Inpatient") clean_set_of_filtered_treatment_ids.add("Alri_Pneumonia_Treatment_Inpatient_Followup") clean_set_of_filtered_treatment_ids.add("DeliveryCare_Comprehensive") - clean_set_of_filtered_treatment_ids.add("PostnatalCare_Comprehensive") for policy_name in sim.modules['HealthSystem'].parameters['priority_rank'].keys(): sim.modules['HealthSystem'].load_priority_policy(policy_name) From 3ff7bcb7b5f42972736748a3bd6488b8e144a14a Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 27 Feb 2024 12:53:03 +0000 Subject: [PATCH 022/131] add schisto high infection as conditional predictor for bladder cancer --- resources/~$ResourceFile_Bladder_Cancer.xlsx | 3 ++ .../bladder_cancer_analyses_single_run.py | 31 ++++++------------- src/tlo/methods/bladder_cancer.py | 22 ++++++++----- 3 files changed, 26 insertions(+), 30 deletions(-) create mode 100644 resources/~$ResourceFile_Bladder_Cancer.xlsx diff --git a/resources/~$ResourceFile_Bladder_Cancer.xlsx b/resources/~$ResourceFile_Bladder_Cancer.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/~$ResourceFile_Bladder_Cancer.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py b/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py index ce83ced543..722af57a2c 100644 --- a/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py +++ b/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py @@ -9,17 +9,13 @@ from tlo.analysis.utils import parse_log_file from tlo.methods import ( bladder_cancer, - care_of_women_during_pregnancy, - contraception, demography, enhanced_lifestyle, healthburden, healthseekingbehaviour, healthsystem, - labour, - newborn_outcomes, - postnatal_supervisor, - pregnancy_supervisor, + simplified_births, + schisto, symptommanager, ) @@ -38,39 +34,30 @@ start_date = Date(2010, 1, 1) end_date = Date(2013, 1, 1) -popsize = 19000 +popsize = 5000 # Establish the simulation object log_config = { 'filename': 'LogFile', 'directory': outputpath, 'custom_levels': { - 'tlo.methods.demography': logging.CRITICAL, - 'tlo.methods.contraception': logging.CRITICAL, - 'tlo.methods.healthsystem': logging.CRITICAL, - 'tlo.methods.labour': logging.CRITICAL, - 'tlo.methods.healthburden': logging.CRITICAL, - 'tlo.methods.symptommanager': logging.CRITICAL, - 'tlo.methods.healthseekingbehaviour': logging.CRITICAL, - 'tlo.methods.pregnancy_supervisor': logging.CRITICAL + "*": logging.WARNING, + 'tlo.methods.bladder_cancer': logging.INFO, + 'tlo.methods.schisto': logging.INFO, } } sim = Simulation(start_date=start_date, seed=4, log_config=log_config) # Register the appropriate modules sim.register(demography.Demography(resourcefilepath=resourcefilepath), - care_of_women_during_pregnancy.CareOfWomenDuringPregnancy(resourcefilepath=resourcefilepath), - contraception.Contraception(resourcefilepath=resourcefilepath), enhanced_lifestyle.Lifestyle(resourcefilepath=resourcefilepath), healthsystem.HealthSystem(resourcefilepath=resourcefilepath), symptommanager.SymptomManager(resourcefilepath=resourcefilepath), healthseekingbehaviour.HealthSeekingBehaviour(resourcefilepath=resourcefilepath), healthburden.HealthBurden(resourcefilepath=resourcefilepath), - labour.Labour(resourcefilepath=resourcefilepath), - newborn_outcomes.NewbornOutcomes(resourcefilepath=resourcefilepath), - pregnancy_supervisor.PregnancySupervisor(resourcefilepath=resourcefilepath), - postnatal_supervisor.PostnatalSupervisor(resourcefilepath=resourcefilepath), - bladder_cancer.BladderCancer(resourcefilepath=resourcefilepath) + simplified_births.SimplifiedBirths(resourcefilepath=resourcefilepath), + bladder_cancer.BladderCancer(resourcefilepath=resourcefilepath), + schisto.Schisto(resourcefilepath=resourcefilepath) ) # Run the simulation and flush the logger diff --git a/src/tlo/methods/bladder_cancer.py b/src/tlo/methods/bladder_cancer.py index 7231125519..03d03eac6a 100644 --- a/src/tlo/methods/bladder_cancer.py +++ b/src/tlo/methods/bladder_cancer.py @@ -386,20 +386,26 @@ def initialise_simulation(self, sim): p = self.parameters lm = self.linear_models_for_progession_of_bc_status - lm['tis_t1'] = LinearModel( - LinearModelType.MULTIPLICATIVE, - p['r_tis_t1_bladder_cancer_none'], - # todo: add in when schisto is in - # Predictor('sh_infection_status').when('High-infection', p['rp_bladder_cancer_schisto_h']), + predictors = [ Predictor('age_years', conditions_are_mutually_exclusive=True) .when('.between(30,49)', p['rp_bladder_cancer_age3049']) .when('.between(50,69)', p['rp_bladder_cancer_age5069']) .when('.between(70,120)', p['rp_bladder_cancer_agege70']) .when('.between(0,14)', 0.0), Predictor('li_tob').when(True, p['rr_tis_t1_bladder_cancer_none_tobacco']), - # todo: add in when schisto module in master - # Predictor('sh_').when(True, p['rr_tis_t1_bladder_cancer_none_ex_alc']), - Predictor('bc_status').when('none', 1.0).otherwise(0.0) + # todo: + # Predictor('tmp_').when(True, p['rr_tis_t1_bladder_cancer_none_ex_alc']), + Predictor('bc_status').when('none', 1.0).otherwise(0.0), + ] + + conditional_predictors = [ + Predictor('sh_infection_status').when('High-infection', p['rp_bladder_cancer_schisto_h']), + ] if "Schisto" in self.sim.modules else [] + + lm["tis_t1"] = LinearModel( + LinearModelType.MULTIPLICATIVE, + p['r_tis_t1_bladder_cancer_none'], + *(predictors + conditional_predictors) ) lm['t2p'] = LinearModel( From 434147c2b5014660c74a340c45f5d03eb45a3fb4 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 27 Feb 2024 12:58:21 +0000 Subject: [PATCH 023/131] fix conditional predictor for active TB - should check presence of CardioMetabolicDisorders --- src/scripts/hiv/projections_jan2023/analysis_full_model.py | 2 +- src/tlo/methods/tb.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripts/hiv/projections_jan2023/analysis_full_model.py b/src/scripts/hiv/projections_jan2023/analysis_full_model.py index e1213cd28e..e90234bc23 100644 --- a/src/scripts/hiv/projections_jan2023/analysis_full_model.py +++ b/src/scripts/hiv/projections_jan2023/analysis_full_model.py @@ -23,7 +23,7 @@ # %% Run the simulation start_date = Date(2010, 1, 1) -end_date = Date(2020, 1, 1) +end_date = Date(2012, 1, 1) popsize = 5000 # scenario = 0 diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 79afd6fa5f..f88a569e36 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -541,7 +541,7 @@ def pre_initialise_population(self): ] conditional_predictors = [ Predictor("nc_diabetes").when(True, p['rr_tb_diabetes1']), - ] if "cardio_metabolic_disorders" in self.sim.modules else [] + ] if "CardioMetabolicDisorders" in self.sim.modules else [] self.lm["active_tb"] = LinearModel.multiplicative( *(predictors + conditional_predictors)) From 808c5ae3d0961417fdf0aca8fb4726ec10eef91e Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 27 Feb 2024 13:07:25 +0000 Subject: [PATCH 024/131] add 'ss' prefix to properties from schisto module referenced in bladder_cancer.py --- src/scripts/hiv/projections_jan2023/analysis_full_model.py | 2 +- src/tlo/methods/bladder_cancer.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scripts/hiv/projections_jan2023/analysis_full_model.py b/src/scripts/hiv/projections_jan2023/analysis_full_model.py index e90234bc23..2386ea56c6 100644 --- a/src/scripts/hiv/projections_jan2023/analysis_full_model.py +++ b/src/scripts/hiv/projections_jan2023/analysis_full_model.py @@ -24,7 +24,7 @@ # %% Run the simulation start_date = Date(2010, 1, 1) end_date = Date(2012, 1, 1) -popsize = 5000 +popsize = 500 # scenario = 0 # set up the log config diff --git a/src/tlo/methods/bladder_cancer.py b/src/tlo/methods/bladder_cancer.py index 03d03eac6a..d305135fbd 100644 --- a/src/tlo/methods/bladder_cancer.py +++ b/src/tlo/methods/bladder_cancer.py @@ -399,7 +399,7 @@ def initialise_simulation(self, sim): ] conditional_predictors = [ - Predictor('sh_infection_status').when('High-infection', p['rp_bladder_cancer_schisto_h']), + Predictor('ss_sh_infection_status').when('High-infection', p['rp_bladder_cancer_schisto_h']), ] if "Schisto" in self.sim.modules else [] lm["tis_t1"] = LinearModel( From 218f4cf5dfd063dddb6c1c56857b31e7b5321e0c Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 27 Feb 2024 13:09:54 +0000 Subject: [PATCH 025/131] edit praziquantel code in schisto.py to use value from CMST in place of donated --- src/tlo/methods/schisto.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tlo/methods/schisto.py b/src/tlo/methods/schisto.py index 2a7c94eed1..ef937276c0 100644 --- a/src/tlo/methods/schisto.py +++ b/src/tlo/methods/schisto.py @@ -308,7 +308,7 @@ def _get_disability_weight(self) -> dict: def _get_item_code_for_praziquantel(self) -> int: """Look-up the item code for Praziquantel""" - return self.sim.modules['HealthSystem'].get_item_code_from_item_name("Praziquantel, 600 mg (donated)") + return self.sim.modules['HealthSystem'].get_item_code_from_item_name("Praziquantel 600mg_1000_CMST") def _schedule_mda_events(self) -> None: """Schedule MDA events, historical and prognosed.""" From f8ebee56ad2d5925a761bf663b397cbf0369f685 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 27 Feb 2024 13:42:07 +0000 Subject: [PATCH 026/131] add parameter rr_depr_hiv for risk of depression with HIV infection --- resources/~$ResourceFile_Depression.xlsx | 3 +++ src/tlo/methods/depression.py | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 resources/~$ResourceFile_Depression.xlsx diff --git a/resources/~$ResourceFile_Depression.xlsx b/resources/~$ResourceFile_Depression.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/~$ResourceFile_Depression.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/src/tlo/methods/depression.py b/src/tlo/methods/depression.py index 8f5fd9661c..f3ad969f8c 100644 --- a/src/tlo/methods/depression.py +++ b/src/tlo/methods/depression.py @@ -138,6 +138,8 @@ def __init__(self, name=None, resourcefilepath=None): 'rr_depr_agege60': Parameter(Types.REAL, 'Relative rate of depression associated with age > 60'), + 'rr_depr_hiv': Parameter(Types.REAL, 'Relative rate of depression associated with HIV infection'), + 'depr_resolution_rates': Parameter( Types.LIST, 'Risk of depression resolving in 3 months if no chronic conditions and no treatments.' @@ -237,7 +239,8 @@ def read_parameters(self, data_folder): .when('.between(0, 14)', 0) .when('.between(15, 19)', 1.0) .when('.between(20, 59)', p['init_rp_depr_age2059']) - .when('>= 60', p['init_rp_depr_agege60']) + .when('>= 60', p['init_rp_depr_agege60']), + Predictor("hv_inf").when(True, p["rr_depr_hiv"]), ) self.linearModels['Depression_Ever_At_Population_Initialisation_Males'] = LinearModel.multiplicative( From 904905d00dfd5392a63c2fafa6bcf0201c4944ef Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 27 Feb 2024 14:00:18 +0000 Subject: [PATCH 027/131] tidy up linear models in depression, include conditional predictors for hiv infection and add comments --- resources/ResourceFile_Depression.xlsx | 4 +- resources/~$ResourceFile_Depression.xlsx | 3 -- src/tlo/methods/depression.py | 50 +++++++++++++++++++----- 3 files changed, 43 insertions(+), 14 deletions(-) delete mode 100644 resources/~$ResourceFile_Depression.xlsx diff --git a/resources/ResourceFile_Depression.xlsx b/resources/ResourceFile_Depression.xlsx index c4feb268fa..b6daab2002 100644 --- a/resources/ResourceFile_Depression.xlsx +++ b/resources/ResourceFile_Depression.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bb3ed875059fa60e8708db0c19da56942d46297c07185849b1ff604f9c67ca8 -size 12357 +oid sha256:d7016feb63786ef5e87ed199dd79f8b6b4a8b8ead227196a4fb578bf33bc1a2f +size 12432 diff --git a/resources/~$ResourceFile_Depression.xlsx b/resources/~$ResourceFile_Depression.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/~$ResourceFile_Depression.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 diff --git a/src/tlo/methods/depression.py b/src/tlo/methods/depression.py index f3ad969f8c..3303f0c29f 100644 --- a/src/tlo/methods/depression.py +++ b/src/tlo/methods/depression.py @@ -222,11 +222,12 @@ def read_parameters(self, data_folder): ) p = self.parameters - # Build the Linear Models: - self.linearModels = dict() - self.linearModels['Depression_At_Population_Initialisation'] = LinearModel( - LinearModelType.MULTIPLICATIVE, - self.parameters['init_pr_depr_m_age1519_no_cc_wealth123'], + # Build the Linear Models + + # ----- Initialisation of population ----- + + # risk of depression in initial population + predictors = [ Predictor('de_cc').when(True, p['init_rp_depr_cc']), Predictor('li_wealth').when('.isin([4,5])', p['init_rp_depr_wealth45']), Predictor().when('(sex=="F") & de_recently_pregnant', p['init_rp_depr_f_rec_preg']), @@ -241,43 +242,60 @@ def read_parameters(self, data_folder): .when('.between(20, 59)', p['init_rp_depr_age2059']) .when('>= 60', p['init_rp_depr_agege60']), Predictor("hv_inf").when(True, p["rr_depr_hiv"]), + ] + + conditional_predictors = [ + Predictor('hv_inf').when(True, p['rr_depr_hiv']), + ] if "Hiv" in self.sim.modules else [] + + self.linearModels["Depression_At_Population_Initialisation"] = LinearModel( + LinearModelType.MULTIPLICATIVE, + p['init_pr_depr_m_age1519_no_cc_wealth123'], + *(predictors + conditional_predictors) ) + # risk of ever having depression in initial population self.linearModels['Depression_Ever_At_Population_Initialisation_Males'] = LinearModel.multiplicative( Predictor('age_years').apply( lambda x: (x if x > 15 else 0) * self.parameters['init_rp_ever_depr_per_year_older_m'] ) ) + # risk of ever having depression in initial population (female) self.linearModels['Depression_Ever_At_Population_Initialisation_Females'] = LinearModel.multiplicative( Predictor('age_years').apply(lambda x: (x if x > 15 else 0) * p['init_rp_ever_depr_per_year_older_f']) ) + # risk of ever having diagnosed depression in initial population self.linearModels['Depression_Ever_Diagnosed_At_Population_Initialisation'] = LinearModel.multiplicative( Predictor('de_ever_depr').when(True, p['init_pr_ever_diagnosed_depression']) .otherwise(0.0) ) + # risk of currently using anti-depressants in initial population self.linearModels['Using_AntiDepressants_Initialisation'] = LinearModel.multiplicative( Predictor('de_depr').when(True, p['init_pr_antidepr_curr_depr']), Predictor().when('~de_depr & de_ever_diagnosed_depression', p['init_rp_antidepr_ever_depr_not_curr']) ) + # risk of ever having talking therapy in initial population self.linearModels['Ever_Talking_Therapy_Initialisation'] = LinearModel( LinearModelType.MULTIPLICATIVE, p['init_pr_ever_talking_therapy_if_diagnosed'], Predictor('de_ever_diagnosed_depression').when(False, 0) ) + # risk of ever having self-harmed in initial population self.linearModels['Ever_Self_Harmed_Initialisation'] = LinearModel( LinearModelType.MULTIPLICATIVE, p['init_pr_ever_self_harmed_if_ever_depr'], Predictor('de_ever_depr').when(False, 0) ) - self.linearModels['Risk_of_Depression_Onset_per3mo'] = LinearModel( - LinearModelType.MULTIPLICATIVE, - p['base_3m_prob_depr'], + # ----- Recurring events ----- + + # risk of depression every 3 months + predictors = [ Predictor('de_cc').when(True, p['rr_depr_cc']), Predictor('age_years', conditions_are_mutually_exclusive=True) .when('.between(0, 14)', 0) @@ -287,9 +305,20 @@ def read_parameters(self, data_folder): Predictor('sex').when('F', p['rr_depr_female']), Predictor('de_recently_pregnant').when(True, p['rr_depr_pregnancy']), Predictor('de_ever_depr').when(True, p['rr_depr_prev_epis']), - Predictor('de_on_antidepr').when(True, p['rr_depr_on_antidepr']) + Predictor('de_on_antidepr').when(True, p['rr_depr_on_antidepr']), + ] + + conditional_predictors = [ + Predictor('hv_inf').when(True, p['rr_depr_hiv']), + ] if "Hiv" in self.sim.modules else [] + + self.linearModels["Risk_of_Depression_Onset_per3mo"] = LinearModel( + LinearModelType.MULTIPLICATIVE, + p['base_3m_prob_depr'], + *(predictors + conditional_predictors) ) + # risk of depression resolution every 3 months self.linearModels['Risk_of_Depression_Resolution_per3mo'] = LinearModel.multiplicative( Predictor('de_intrinsic_3mo_risk_of_depr_resolution').apply(lambda x: x), Predictor('de_cc').when(True, p['rr_resol_depr_cc']), @@ -297,16 +326,19 @@ def read_parameters(self, data_folder): Predictor('de_ever_talk_ther').when(True, p['rr_resol_depr_current_talk_ther']) ) + # risk of stopping anti-depressants every 3 months self.linearModels['Risk_of_Stopping_Antidepressants_per3mo'] = LinearModel.multiplicative( Predictor('de_depr').when(True, p['prob_3m_default_antidepr']) .when(False, p['prob_3m_stop_antidepr']) ) + # risk of self-harm every 3 months self.linearModels['Risk_of_SelfHarm_per3mo'] = LinearModel( LinearModelType.MULTIPLICATIVE, p['prob_3m_selfharm_depr'] ) + # risk of suicide every 3 months self.linearModels['Risk_of_Suicide_per3mo'] = LinearModel( LinearModelType.MULTIPLICATIVE, p['prob_3m_suicide_depr_m'], From 79216dbc151bfda920da2f94cb7151260dd22594 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 27 Feb 2024 14:04:03 +0000 Subject: [PATCH 028/131] move hv_inf into conditional predictor for depression in initial population --- src/tlo/methods/depression.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tlo/methods/depression.py b/src/tlo/methods/depression.py index 3303f0c29f..b72b4d3128 100644 --- a/src/tlo/methods/depression.py +++ b/src/tlo/methods/depression.py @@ -241,7 +241,6 @@ def read_parameters(self, data_folder): .when('.between(15, 19)', 1.0) .when('.between(20, 59)', p['init_rp_depr_age2059']) .when('>= 60', p['init_rp_depr_agege60']), - Predictor("hv_inf").when(True, p["rr_depr_hiv"]), ] conditional_predictors = [ From ade9f57eb860dfcd609f9b1e97be4e766e60f482 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 28 Feb 2024 09:56:49 +0000 Subject: [PATCH 029/131] convert lm for incident cancer (site_confined) to model with conditional predictors. Include HIV as risk factor. --- docs/write-ups/OtherAdultCancer.docx | 4 ++-- src/tlo/methods/other_adult_cancers.py | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/write-ups/OtherAdultCancer.docx b/docs/write-ups/OtherAdultCancer.docx index 99c7c562e1..e1341cfbe4 100644 --- a/docs/write-ups/OtherAdultCancer.docx +++ b/docs/write-ups/OtherAdultCancer.docx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:299b9025a1fe1fad73a42ed321061e655e638a9c3ce8ab67e3ee691815ae6d98 -size 71556 +oid sha256:7a77ec4dccf9334a32cad08b040295344f46fd3cf0b3181a0d05ef1fae7115de +size 75588 diff --git a/src/tlo/methods/other_adult_cancers.py b/src/tlo/methods/other_adult_cancers.py index 508e96c12b..048a43e828 100644 --- a/src/tlo/methods/other_adult_cancers.py +++ b/src/tlo/methods/other_adult_cancers.py @@ -381,15 +381,23 @@ def initialise_simulation(self, sim): p = self.parameters lm = self.linear_models_for_progession_of_oac_status - lm['site_confined'] = LinearModel( - LinearModelType.MULTIPLICATIVE, - p['r_site_confined_none'], + predictors = [ Predictor('age_years', conditions_are_mutually_exclusive=True) .when('.between(30,49)', p['rr_site_confined_age3049']) .when('.between(50,69)', p['rr_site_confined_age5069']) .when('.between(0,14)', 0.0) .when('.between(70,120)', p['rr_site_confined_agege70']), Predictor('oac_status').when('none', 1.0).otherwise(0.0) + ] + + conditional_predictors = [ + Predictor('hv_inf').when(True, p['rr_site_confined_hiv']), + ] if "Hiv" in self.sim.modules else [] + + lm['site_confined'] = LinearModel( + LinearModelType.MULTIPLICATIVE, + p['r_site_confined_none'], + *(predictors + conditional_predictors) ) lm['local_ln'] = LinearModel( From 754a1bf727900f7ba135134c01bc6104c2aec760 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 28 Feb 2024 10:00:25 +0000 Subject: [PATCH 030/131] add parameter rr_site_confined_hiv to other_adult_cancers.py --- resources/ResourceFile_Other_Adult_Cancers.xlsx | 4 ++-- src/tlo/methods/other_adult_cancers.py | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/resources/ResourceFile_Other_Adult_Cancers.xlsx b/resources/ResourceFile_Other_Adult_Cancers.xlsx index bf6997cb24..276a358fa6 100644 --- a/resources/ResourceFile_Other_Adult_Cancers.xlsx +++ b/resources/ResourceFile_Other_Adult_Cancers.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be37486adb1684f56f4a03a490b7a952d5ddd4e0a19b5aa146d5aaee8dc5e680 -size 10830 +oid sha256:b11b887c7d35f67e09fd06ad4a289a2538ecf2ff40de0c5d270243c2f8c9ecd3 +size 10864 diff --git a/src/tlo/methods/other_adult_cancers.py b/src/tlo/methods/other_adult_cancers.py index 048a43e828..a442ea2742 100644 --- a/src/tlo/methods/other_adult_cancers.py +++ b/src/tlo/methods/other_adult_cancers.py @@ -113,6 +113,9 @@ def __init__(self, name=None, resourcefilepath=None): "rr_site_confined_agege70": Parameter( Types.REAL, "rate ratio for site-confined other_adult cancer for age ge 70" ), + "rr_site_confined_hiv": Parameter( + Types.REAL, "rate ratio for site-confined other_adult_cancer if infected with HIV" + ), "r_local_ln_site_confined_other_adult_ca": Parameter( Types.REAL, "probabilty per 3 months of local ln other_adult cancer amongst people with site confined", From 876c9791215b455cc235a4ef7e22f58199fdcf78 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 28 Feb 2024 10:05:47 +0000 Subject: [PATCH 031/131] update other_adult_cancers write-up to include HIV as risk factor --- docs/write-ups/OtherAdultCancer.docx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/write-ups/OtherAdultCancer.docx b/docs/write-ups/OtherAdultCancer.docx index e1341cfbe4..0cab5dfb13 100644 --- a/docs/write-ups/OtherAdultCancer.docx +++ b/docs/write-ups/OtherAdultCancer.docx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a77ec4dccf9334a32cad08b040295344f46fd3cf0b3181a0d05ef1fae7115de -size 75588 +oid sha256:0767cf3b858ded00fc3aafab5b8f0630fd872679f1916679a5b1d4b5f4a0a747 +size 76023 From 865aae01f00266e46b8bcd989f4c6197119a08f3 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 28 Feb 2024 10:16:52 +0000 Subject: [PATCH 032/131] update Depression.docx to include HIV as risk factor for depression --- docs/write-ups/Depression.docx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/write-ups/Depression.docx b/docs/write-ups/Depression.docx index 0d03c044b6..b2af7525c6 100644 --- a/docs/write-ups/Depression.docx +++ b/docs/write-ups/Depression.docx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74cc9af2d9e114bee5f695cddcab49c92d79b6fa5abd7c8bb59b1c2a284b8ead -size 233268 +oid sha256:947d9954ead452370841b3e8bec8b6bf17fcea40e240ea342dcf169e6c6bb68d +size 235099 From 5bb2560663e65bc38deea573a7084bce3bd7dab4 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 28 Feb 2024 10:33:45 +0000 Subject: [PATCH 033/131] edit HIV in depression to include only HIV cases not virally suppressed --- src/tlo/methods/depression.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tlo/methods/depression.py b/src/tlo/methods/depression.py index b72b4d3128..c05f5d441c 100644 --- a/src/tlo/methods/depression.py +++ b/src/tlo/methods/depression.py @@ -244,7 +244,10 @@ def read_parameters(self, data_folder): ] conditional_predictors = [ - Predictor('hv_inf').when(True, p['rr_depr_hiv']), + Predictor().when( + 'hv_inf & ' + '(hv_art != "on_VL_suppressed")', + p["rr_depr_hiv"]), ] if "Hiv" in self.sim.modules else [] self.linearModels["Depression_At_Population_Initialisation"] = LinearModel( @@ -308,7 +311,10 @@ def read_parameters(self, data_folder): ] conditional_predictors = [ - Predictor('hv_inf').when(True, p['rr_depr_hiv']), + Predictor().when( + 'hv_inf & ' + '(hv_art != "on_VL_suppressed")', + p["rr_depr_hiv"]), ] if "Hiv" in self.sim.modules else [] self.linearModels["Risk_of_Depression_Onset_per3mo"] = LinearModel( From 9cb104391fd58116c9aa1b0bf5a16c0c7f3743ad Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 28 Feb 2024 10:35:03 +0000 Subject: [PATCH 034/131] update other_adult_cancers.py linear model to include HIV as risk factor only if not virally suppressed --- src/tlo/methods/other_adult_cancers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tlo/methods/other_adult_cancers.py b/src/tlo/methods/other_adult_cancers.py index a442ea2742..ddc1ad61ad 100644 --- a/src/tlo/methods/other_adult_cancers.py +++ b/src/tlo/methods/other_adult_cancers.py @@ -394,7 +394,10 @@ def initialise_simulation(self, sim): ] conditional_predictors = [ - Predictor('hv_inf').when(True, p['rr_site_confined_hiv']), + Predictor().when( + 'hv_inf & ' + '(hv_art != "on_VL_suppressed")', + p["rr_site_confined_hiv"]), ] if "Hiv" in self.sim.modules else [] lm['site_confined'] = LinearModel( From 1e316af548f0276926e214683b852de4e6ec51ab Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 28 Feb 2024 10:36:20 +0000 Subject: [PATCH 035/131] edit: HIV remains as risk factor for depression independent of treatment status --- docs/write-ups/~$pression.docx | 3 +++ src/tlo/methods/depression.py | 10 ++-------- 2 files changed, 5 insertions(+), 8 deletions(-) create mode 100644 docs/write-ups/~$pression.docx diff --git a/docs/write-ups/~$pression.docx b/docs/write-ups/~$pression.docx new file mode 100644 index 0000000000..97fef555e0 --- /dev/null +++ b/docs/write-ups/~$pression.docx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c14c3ed6351d8492b544f3f1ad373edc06cf12c1715ed6220b79d0b6b5ae9843 +size 162 diff --git a/src/tlo/methods/depression.py b/src/tlo/methods/depression.py index c05f5d441c..b72b4d3128 100644 --- a/src/tlo/methods/depression.py +++ b/src/tlo/methods/depression.py @@ -244,10 +244,7 @@ def read_parameters(self, data_folder): ] conditional_predictors = [ - Predictor().when( - 'hv_inf & ' - '(hv_art != "on_VL_suppressed")', - p["rr_depr_hiv"]), + Predictor('hv_inf').when(True, p['rr_depr_hiv']), ] if "Hiv" in self.sim.modules else [] self.linearModels["Depression_At_Population_Initialisation"] = LinearModel( @@ -311,10 +308,7 @@ def read_parameters(self, data_folder): ] conditional_predictors = [ - Predictor().when( - 'hv_inf & ' - '(hv_art != "on_VL_suppressed")', - p["rr_depr_hiv"]), + Predictor('hv_inf').when(True, p['rr_depr_hiv']), ] if "Hiv" in self.sim.modules else [] self.linearModels["Risk_of_Depression_Onset_per3mo"] = LinearModel( From 85cec507a0403414a21af02c16f89848b2266dd0 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 28 Feb 2024 10:48:45 +0000 Subject: [PATCH 036/131] include HIV as risk factor for low grade dysplasia (oesophageal cancer). Update ResourceFile_Oesophageal_Cancer.xlsx --- docs/write-ups/OtherAdultCancer.docx | 4 ++-- docs/write-ups/~$pression.docx | 3 --- resources/ResourceFile_Oesophageal_Cancer.xlsx | 4 ++-- src/tlo/methods/oesophagealcancer.py | 5 ++++- 4 files changed, 8 insertions(+), 8 deletions(-) delete mode 100644 docs/write-ups/~$pression.docx diff --git a/docs/write-ups/OtherAdultCancer.docx b/docs/write-ups/OtherAdultCancer.docx index 0cab5dfb13..f96d206d44 100644 --- a/docs/write-ups/OtherAdultCancer.docx +++ b/docs/write-ups/OtherAdultCancer.docx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0767cf3b858ded00fc3aafab5b8f0630fd872679f1916679a5b1d4b5f4a0a747 -size 76023 +oid sha256:5d46317e9ed240f18df2153063e15a497c4ad5fec19bf61a5e02158c77c79f33 +size 76074 diff --git a/docs/write-ups/~$pression.docx b/docs/write-ups/~$pression.docx deleted file mode 100644 index 97fef555e0..0000000000 --- a/docs/write-ups/~$pression.docx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c14c3ed6351d8492b544f3f1ad373edc06cf12c1715ed6220b79d0b6b5ae9843 -size 162 diff --git a/resources/ResourceFile_Oesophageal_Cancer.xlsx b/resources/ResourceFile_Oesophageal_Cancer.xlsx index 3ce8a6432e..976daa83b8 100644 --- a/resources/ResourceFile_Oesophageal_Cancer.xlsx +++ b/resources/ResourceFile_Oesophageal_Cancer.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2453935db927550987c9b47cbcb6bbe1816a2cffed31a38d5e350231f6436651 -size 10889 +oid sha256:838d1dc8cf533230aa67ec7fbfc72750741701978c1a0fb8d74035232e9ee56e +size 10908 diff --git a/src/tlo/methods/oesophagealcancer.py b/src/tlo/methods/oesophagealcancer.py index fb8e96116c..5cd55e4b5d 100644 --- a/src/tlo/methods/oesophagealcancer.py +++ b/src/tlo/methods/oesophagealcancer.py @@ -76,7 +76,7 @@ def __init__(self, name=None, resourcefilepath=None): ), "r_low_grade_dysplasia_none": Parameter( Types.REAL, - "probabilty per 3 months of incident low grade oesophageal dysplasia, amongst people with no " + "probability per 3 months of incident low grade oesophageal dysplasia, amongst people with no " "oesophageal dysplasia (men, age20, no excess alcohol, no tobacco)", ), "rr_low_grade_dysplasia_none_female": Parameter( @@ -91,6 +91,9 @@ def __init__(self, name=None, resourcefilepath=None): "rr_low_grade_dysplasia_none_ex_alc": Parameter( Types.REAL, "rate ratio for low grade oesophageal dysplasia for no excess alcohol" ), + "rr_low_grade_dysplasia_none_hiv": Parameter( + Types.REAL, "rate ratio for low grade oesophageal dysplasia for HIV infection" + ), "r_high_grade_dysplasia_low_grade_dysp": Parameter( Types.REAL, "probabilty per 3 months of high grade oesophageal dysplasia, amongst people with low grade dysplasia", From 6f45dc79de13afd5c0d087b6848573da81fb3d11 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 28 Feb 2024 10:50:48 +0000 Subject: [PATCH 037/131] update linear model for low grade dysplasia to include HIV as conditional risk factor --- src/tlo/methods/oesophagealcancer.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/tlo/methods/oesophagealcancer.py b/src/tlo/methods/oesophagealcancer.py index 5cd55e4b5d..3830e1e0b4 100644 --- a/src/tlo/methods/oesophagealcancer.py +++ b/src/tlo/methods/oesophagealcancer.py @@ -376,9 +376,7 @@ def initialise_simulation(self, sim): p = self.parameters lm = self.linear_models_for_progession_of_oc_status - lm['low_grade_dysplasia'] = LinearModel( - LinearModelType.MULTIPLICATIVE, - p['r_low_grade_dysplasia_none'], + predictors = [ Predictor('age_years').apply( lambda x: 0 if x < 20 else (x - 20) ** p['rr_low_grade_dysplasia_none_per_year_older'] ), @@ -387,6 +385,19 @@ def initialise_simulation(self, sim): Predictor('li_ex_alc').when(True, p['rr_low_grade_dysplasia_none_ex_alc']), Predictor('oc_status').when('none', 1.0) .otherwise(0.0) + ] + + conditional_predictors = [ + Predictor().when( + 'hv_inf & ' + '(hv_art != "on_VL_suppressed")', + p["rr_low_grade_dysplasia_none_hiv"]), + ] if "Hiv" in self.sim.modules else [] + + lm['low_grade_dysplasia'] = LinearModel( + LinearModelType.MULTIPLICATIVE, + p['r_low_grade_dysplasia_none'], + *(predictors + conditional_predictors) ) lm['high_grade_dysplasia'] = LinearModel( From 2dbb98dfcfe4a2d0b0d8b3452b428e5b2fac5779 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 28 Feb 2024 10:54:54 +0000 Subject: [PATCH 038/131] update OesophagealCancer.docx write-up to include HIV risk --- docs/write-ups/OesophagealCancer.docx | 4 ++-- docs/write-ups/OtherAdultCancer.docx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/write-ups/OesophagealCancer.docx b/docs/write-ups/OesophagealCancer.docx index 80654e9cb9..a500408b03 100644 --- a/docs/write-ups/OesophagealCancer.docx +++ b/docs/write-ups/OesophagealCancer.docx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ae9f447c5ab5d296f4d5d766a0675865998e386c5b17c97095e36799435f9277 -size 180430 +oid sha256:c7c7cf7f5ca8e16abb86047ec3c10b2ac220f21073bb7a966c167370e7de0a2c +size 185806 diff --git a/docs/write-ups/OtherAdultCancer.docx b/docs/write-ups/OtherAdultCancer.docx index f96d206d44..23b5a2360d 100644 --- a/docs/write-ups/OtherAdultCancer.docx +++ b/docs/write-ups/OtherAdultCancer.docx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d46317e9ed240f18df2153063e15a497c4ad5fec19bf61a5e02158c77c79f33 -size 76074 +oid sha256:f3ddd14909dffb4ef07f132880e702c6c83d0f4bfb07ca4b54e233bb2b6b5fff +size 75034 From b6b5fdc1c2a054ab63a562c9c027fdb8f01d6cd8 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 4 Mar 2024 09:50:33 +0000 Subject: [PATCH 039/131] add condition hiv diagnosed for increased risk of depression --- src/tlo/methods/depression.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/tlo/methods/depression.py b/src/tlo/methods/depression.py index b72b4d3128..f070cc2aec 100644 --- a/src/tlo/methods/depression.py +++ b/src/tlo/methods/depression.py @@ -244,7 +244,9 @@ def read_parameters(self, data_folder): ] conditional_predictors = [ - Predictor('hv_inf').when(True, p['rr_depr_hiv']), + Predictor().when( + 'hv_inf & hv_diagnosed', + p["rr_depr_hiv"]), ] if "Hiv" in self.sim.modules else [] self.linearModels["Depression_At_Population_Initialisation"] = LinearModel( @@ -311,6 +313,12 @@ def read_parameters(self, data_folder): Predictor('hv_inf').when(True, p['rr_depr_hiv']), ] if "Hiv" in self.sim.modules else [] + conditional_predictors = [ + Predictor().when( + 'hv_inf & hv_diagnosed', + p["rr_depr_hiv"]), + ] if "Hiv" in self.sim.modules else [] + self.linearModels["Risk_of_Depression_Onset_per3mo"] = LinearModel( LinearModelType.MULTIPLICATIVE, p['base_3m_prob_depr'], From a040823f75b754448910790b7d133a710b98dcfc Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 4 Mar 2024 09:57:02 +0000 Subject: [PATCH 040/131] remove hiv as risk factor for oesophageal cancer --- src/tlo/methods/oesophagealcancer.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/tlo/methods/oesophagealcancer.py b/src/tlo/methods/oesophagealcancer.py index 3830e1e0b4..5cd55e4b5d 100644 --- a/src/tlo/methods/oesophagealcancer.py +++ b/src/tlo/methods/oesophagealcancer.py @@ -376,7 +376,9 @@ def initialise_simulation(self, sim): p = self.parameters lm = self.linear_models_for_progession_of_oc_status - predictors = [ + lm['low_grade_dysplasia'] = LinearModel( + LinearModelType.MULTIPLICATIVE, + p['r_low_grade_dysplasia_none'], Predictor('age_years').apply( lambda x: 0 if x < 20 else (x - 20) ** p['rr_low_grade_dysplasia_none_per_year_older'] ), @@ -385,19 +387,6 @@ def initialise_simulation(self, sim): Predictor('li_ex_alc').when(True, p['rr_low_grade_dysplasia_none_ex_alc']), Predictor('oc_status').when('none', 1.0) .otherwise(0.0) - ] - - conditional_predictors = [ - Predictor().when( - 'hv_inf & ' - '(hv_art != "on_VL_suppressed")', - p["rr_low_grade_dysplasia_none_hiv"]), - ] if "Hiv" in self.sim.modules else [] - - lm['low_grade_dysplasia'] = LinearModel( - LinearModelType.MULTIPLICATIVE, - p['r_low_grade_dysplasia_none'], - *(predictors + conditional_predictors) ) lm['high_grade_dysplasia'] = LinearModel( From 1eeff16786d24dacb6ad84a1340e4ab03ffbc19d Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 4 Mar 2024 09:58:06 +0000 Subject: [PATCH 041/131] remove parameter for hiv as risk factor for oesophageal cancer --- resources/ResourceFile_Oesophageal_Cancer.xlsx | 4 ++-- src/tlo/methods/oesophagealcancer.py | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/resources/ResourceFile_Oesophageal_Cancer.xlsx b/resources/ResourceFile_Oesophageal_Cancer.xlsx index 976daa83b8..fc68b62c99 100644 --- a/resources/ResourceFile_Oesophageal_Cancer.xlsx +++ b/resources/ResourceFile_Oesophageal_Cancer.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:838d1dc8cf533230aa67ec7fbfc72750741701978c1a0fb8d74035232e9ee56e -size 10908 +oid sha256:34ebbfadba9c02a040c453b8ee98d15a2764a5811cf47a77ff558aa125049dfa +size 10881 diff --git a/src/tlo/methods/oesophagealcancer.py b/src/tlo/methods/oesophagealcancer.py index 5cd55e4b5d..7dfeba5634 100644 --- a/src/tlo/methods/oesophagealcancer.py +++ b/src/tlo/methods/oesophagealcancer.py @@ -91,9 +91,6 @@ def __init__(self, name=None, resourcefilepath=None): "rr_low_grade_dysplasia_none_ex_alc": Parameter( Types.REAL, "rate ratio for low grade oesophageal dysplasia for no excess alcohol" ), - "rr_low_grade_dysplasia_none_hiv": Parameter( - Types.REAL, "rate ratio for low grade oesophageal dysplasia for HIV infection" - ), "r_high_grade_dysplasia_low_grade_dysp": Parameter( Types.REAL, "probabilty per 3 months of high grade oesophageal dysplasia, amongst people with low grade dysplasia", From 78b73e56fadf124a68e8e6c79146f1cd513130d7 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 4 Mar 2024 10:00:45 +0000 Subject: [PATCH 042/131] update OesophagealCancer.docx to remove hiv as risk factor --- docs/write-ups/OesophagealCancer.docx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/write-ups/OesophagealCancer.docx b/docs/write-ups/OesophagealCancer.docx index a500408b03..c52e9e261a 100644 --- a/docs/write-ups/OesophagealCancer.docx +++ b/docs/write-ups/OesophagealCancer.docx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7c7cf7f5ca8e16abb86047ec3c10b2ac220f21073bb7a966c167370e7de0a2c -size 185806 +oid sha256:ef6ac87512767a2c70dc67d22f5c409c425bf26736624ba039285a1d906750ca +size 185471 From b899660d615298701dc26c6978f8bcfb8f15a4b2 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 4 Mar 2024 11:13:01 +0000 Subject: [PATCH 043/131] update value for weighted risk of other_adult_cancers with unsuppressed HIV --- docs/write-ups/OtherAdultCancer.docx | 4 ++-- resources/ResourceFile_Other_Adult_Cancers.xlsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/write-ups/OtherAdultCancer.docx b/docs/write-ups/OtherAdultCancer.docx index 23b5a2360d..42bf5f2b09 100644 --- a/docs/write-ups/OtherAdultCancer.docx +++ b/docs/write-ups/OtherAdultCancer.docx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3ddd14909dffb4ef07f132880e702c6c83d0f4bfb07ca4b54e233bb2b6b5fff -size 75034 +oid sha256:1f3ff79a3f4e2ad48981cffd95eb6d96d7aa3dc8777015e2b71e63995bf336c9 +size 76152 diff --git a/resources/ResourceFile_Other_Adult_Cancers.xlsx b/resources/ResourceFile_Other_Adult_Cancers.xlsx index 276a358fa6..160247c80d 100644 --- a/resources/ResourceFile_Other_Adult_Cancers.xlsx +++ b/resources/ResourceFile_Other_Adult_Cancers.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b11b887c7d35f67e09fd06ad4a289a2538ecf2ff40de0c5d270243c2f8c9ecd3 -size 10864 +oid sha256:3d7a8daecbf8a3a6fb7efdcefffb5ece5e91c6abc811da5a038e4fc1fbb44774 +size 10865 From 599580bc232c319d9eac52a39968ec044c362211 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 4 Mar 2024 11:35:12 +0000 Subject: [PATCH 044/131] add rr_hiv to linear model. update ResourceFile_cmd_condition_onset.xlsx with rr_hiv, leave value=1.0 if no effect of hiv --- src/tlo/methods/cardio_metabolic_disorders.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/tlo/methods/cardio_metabolic_disorders.py b/src/tlo/methods/cardio_metabolic_disorders.py index 4ef5c41c8e..9c2ba8514c 100644 --- a/src/tlo/methods/cardio_metabolic_disorders.py +++ b/src/tlo/methods/cardio_metabolic_disorders.py @@ -513,7 +513,7 @@ def initialise_simulation(self, sim): # Hypertension is the only condition for which we assume some community-based testing occurs; build LM based on # age / sex self.lms_testing['hypertension'] = self.build_linear_model('hypertension', self.parameters[ - 'interval_between_polls'], lm_type='testing') + 'interval_between_polls'], lm_type='testing') for event in self.events: self.lms_event_onset[event] = self.build_linear_model(event, self.parameters['interval_between_polls'], @@ -677,6 +677,9 @@ def build_linear_model(self, condition, interval_between_polls, lm_type): Predictor('nc_chronic_kidney_disease').when(True, p['rr_chronic_kidney_disease']), Predictor('nc_chronic_lower_back_pain').when(True, p['rr_chronic_lower_back_pain']), Predictor('nc_chronic_ischemic_hd').when(True, p['rr_chronic_ischemic_heart_disease']), + Predictor().when('hv_inf & ' + '(hv_art != "on_VL_suppressed")', + p["rr_hiv"]), Predictor('nc_ever_stroke').when(True, p['rr_ever_stroke']), Predictor('nc_ever_heart_attack').when(True, p['rr_ever_heart_attack']), Predictor('nc_diabetes_on_medication').when(True, p['rr_diabetes_on_medication']), @@ -814,7 +817,7 @@ def is_next_test_due(current_date, date_of_last_test): person = df.loc[person_id, df.columns[df.columns.str.startswith('nc_')]] symptoms = self.sim.modules['SymptomManager'].has_what(person_id=person_id) - conditions_to_investigate = [] # The list of conditions that will be investigated in follow-up HSI + conditions_to_investigate = [] # The list of conditions that will be investigated in follow-up HSI has_any_cmd_symptom = False # Marker for whether the person has any symptoms of interest # Determine if there are any conditions that should be investigated: @@ -848,7 +851,7 @@ def is_next_test_due(current_date, date_of_last_test): priority=0, topen=self.sim.date, tclose=None - ) + ) def determine_if_will_be_investigated_events(self, person_id): """ @@ -865,7 +868,7 @@ def determine_if_will_be_investigated_events(self, person_id): for ev in self.events: # If the person has symptoms of damage from within the last 3 days, schedule them for emergency care if f'{ev}_damage' in symptoms and \ - ((self.sim.date - self.sim.population.props.at[person_id, f'nc_{ev}_date_last_event']).days <= 3): + ((self.sim.date - self.sim.population.props.at[person_id, f'nc_{ev}_date_last_event']).days <= 3): ev_to_investigate.append(ev) if ev_to_investigate: @@ -1589,7 +1592,7 @@ def apply(self, person_id, squeeze_factor): # NB. With a probability of 1.0, this will keep occurring, and the person will never give up coming back to # pick up medication. if (m.rng.random_sample() < - m.parameters[f'{self.condition}_hsi'].get('pr_seeking_further_appt_if_drug_not_available')): + m.parameters[f'{self.condition}_hsi'].get('pr_seeking_further_appt_if_drug_not_available')): self.sim.modules['HealthSystem'].schedule_hsi_event( hsi_event=self, topen=self.sim.date + pd.DateOffset(days=1), @@ -1654,7 +1657,7 @@ def apply(self, person_id, squeeze_factor): # NB. With a probability of 1.0, this will keep occurring, and the person will never give-up coming back to # pick-up medication. if (m.rng.random_sample() < - m.parameters[f'{self.condition}_hsi'].get('pr_seeking_further_appt_if_drug_not_available')): + m.parameters[f'{self.condition}_hsi'].get('pr_seeking_further_appt_if_drug_not_available')): self.sim.modules['HealthSystem'].schedule_hsi_event( hsi_event=self, topen=self.sim.date + pd.DateOffset(days=1), From 953502e5b212f3859ac7fd97a525339b7db2d566 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 4 Mar 2024 11:51:11 +0000 Subject: [PATCH 045/131] update resourcefiles for CMD include rr_hiv for all, no effect of majority of processes --- resources/cmd/ResourceFile_cmd_condition_death.xlsx | 4 ++-- resources/cmd/ResourceFile_cmd_condition_onset.xlsx | 4 ++-- resources/cmd/ResourceFile_cmd_condition_removal.xlsx | 4 ++-- resources/cmd/ResourceFile_cmd_condition_testing.xlsx | 4 ++-- resources/cmd/ResourceFile_cmd_events.xlsx | 4 ++-- resources/cmd/ResourceFile_cmd_events_death.xlsx | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/resources/cmd/ResourceFile_cmd_condition_death.xlsx b/resources/cmd/ResourceFile_cmd_condition_death.xlsx index 725996cd2c..536c8ebbc9 100644 --- a/resources/cmd/ResourceFile_cmd_condition_death.xlsx +++ b/resources/cmd/ResourceFile_cmd_condition_death.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bedbf3304e4078e2d495a136eb838757fa06d0e5c7cad13714ba469f1c9a63ae -size 19087 +oid sha256:b13090d0ecf6d95c6001133bed733a1642f5e698cc0f048d7526357506088862 +size 19200 diff --git a/resources/cmd/ResourceFile_cmd_condition_onset.xlsx b/resources/cmd/ResourceFile_cmd_condition_onset.xlsx index d53e29356f..292f9b0f7b 100644 --- a/resources/cmd/ResourceFile_cmd_condition_onset.xlsx +++ b/resources/cmd/ResourceFile_cmd_condition_onset.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:154eb80d1676dd56bd832b77bcd40965ab9c69ee9dc6c146d1a3613cac2848a6 -size 30573 +oid sha256:b7db0f7a4aca08cdb1852bde35262295c294354a1838b72d65fb14b5cc257364 +size 30669 diff --git a/resources/cmd/ResourceFile_cmd_condition_removal.xlsx b/resources/cmd/ResourceFile_cmd_condition_removal.xlsx index 801b4b69f9..49a7ea79ee 100644 --- a/resources/cmd/ResourceFile_cmd_condition_removal.xlsx +++ b/resources/cmd/ResourceFile_cmd_condition_removal.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b66ae1f6017e1c506970092b975bd6d002dacce13e5c325e59b17cee984eec2f -size 18556 +oid sha256:b0d42326a048511264bedf856bb757d0a609ac01361ea1da2b0ed89c066dfa8b +size 18625 diff --git a/resources/cmd/ResourceFile_cmd_condition_testing.xlsx b/resources/cmd/ResourceFile_cmd_condition_testing.xlsx index 63b6baf08f..ead6ce011e 100644 --- a/resources/cmd/ResourceFile_cmd_condition_testing.xlsx +++ b/resources/cmd/ResourceFile_cmd_condition_testing.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3fbe4450eb795d3f3c42f71e459f1e703ec4e9aa41c75ed738a6f9c098c5db53 -size 11437 +oid sha256:82ad9558d816133b020e942c7962b1c783469fa3ba322db164a7305c60caf698 +size 11491 diff --git a/resources/cmd/ResourceFile_cmd_events.xlsx b/resources/cmd/ResourceFile_cmd_events.xlsx index cce09206ec..a7d2cb9bae 100644 --- a/resources/cmd/ResourceFile_cmd_events.xlsx +++ b/resources/cmd/ResourceFile_cmd_events.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1a34707bd325d919b8e94cb7135e5a3f17da4bca67d8cc2023418f7b1099e070 -size 14047 +oid sha256:1419799a637737654950aa137f2f7fd131a934deea6fa2e48ee5b37e531e4517 +size 14104 diff --git a/resources/cmd/ResourceFile_cmd_events_death.xlsx b/resources/cmd/ResourceFile_cmd_events_death.xlsx index 1dc7d3d67a..1983a2f88e 100644 --- a/resources/cmd/ResourceFile_cmd_events_death.xlsx +++ b/resources/cmd/ResourceFile_cmd_events_death.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9f6e0e87f17ac90e074eb9434c90f67ec6207f3475c11e4340ba38fe2e94e13 -size 13343 +oid sha256:328f3c2a624dbbf9072712f573213c744dee7f962f854475b08ffb2a306392c0 +size 13425 From b57a5030b37737706cdc15b93bd13b3c3189d763 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 5 Mar 2024 10:35:29 +0000 Subject: [PATCH 046/131] add diabetes as risk for active TB and relapse. add params to ResourceFile_TB.xlsx replace linearmodels dict which was accidentally removed in depression.py --- resources/ResourceFile_TB.xlsx | 4 +- src/tlo/methods/depression.py | 1 + src/tlo/methods/tb.py | 1374 ++++++++++++++++---------------- 3 files changed, 707 insertions(+), 672 deletions(-) diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index 494e6b31fd..00af0b8e33 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6760fe2b529eb1538bbbfbd6d5e7350f9dbc2272ab997b363771c8b901739bb3 -size 54894 +oid sha256:2259cc326c75c3100b2d2e24a64047e41666b257edd8f1a42dd20a94deffd07c +size 55227 diff --git a/src/tlo/methods/depression.py b/src/tlo/methods/depression.py index f070cc2aec..6fddf2017a 100644 --- a/src/tlo/methods/depression.py +++ b/src/tlo/methods/depression.py @@ -225,6 +225,7 @@ def read_parameters(self, data_folder): # Build the Linear Models # ----- Initialisation of population ----- + self.linearModels = dict() # risk of depression in initial population predictors = [ diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index f88a569e36..db22cb757d 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -180,6 +180,9 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "rr_relapse_hiv": Parameter( Types.REAL, "relative risk of relapse for HIV-positive people" ), + "rr_relapse_diabetes": Parameter( + Types.REAL, "relative risk of relapse for people with diabetes (treated/untreated)" + ), # ------------------ active disease ------------------ # "scaling_factor_WHO": Parameter( Types.REAL, @@ -238,9 +241,9 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "rr_tb_obese": Parameter( Types.REAL, "relative risk of progression to active disease if obese" ), - "rr_tb_diabetes1": Parameter( + "rr_tb_diabetes": Parameter( Types.REAL, - "relative risk of progression to active disease with type 1 diabetes", + "relative risk of progression to active disease with any type diabetes", ), "rr_tb_alcohol": Parameter( Types.REAL, @@ -540,16 +543,30 @@ def pre_initialise_population(self): ), ] conditional_predictors = [ - Predictor("nc_diabetes").when(True, p['rr_tb_diabetes1']), + Predictor("nc_diabetes").when(True, p['rr_tb_diabetes']), ] if "CardioMetabolicDisorders" in self.sim.modules else [] self.lm["active_tb"] = LinearModel.multiplicative( *(predictors + conditional_predictors)) # risk of relapse <2 years following treatment - self.lm["risk_relapse_2yrs"] = LinearModel( - LinearModelType.MULTIPLICATIVE, - p["monthly_prob_relapse_tx_complete"], + # self.lm["risk_relapse_2yrs"] = LinearModel( + # LinearModelType.MULTIPLICATIVE, + # p["monthly_prob_relapse_tx_complete"], + # Predictor("hv_inf").when(True, p["rr_relapse_hiv"]), + # Predictor("tb_treatment_failure") + # .when(True, (p["monthly_prob_relapse_tx_incomplete"] / p["monthly_prob_relapse_tx_complete"])), + # Predictor().when( + # 'tb_on_ipt & ' + # 'age_years <= 15', + # p["rr_ipt_child"]), + # Predictor().when( + # 'tb_on_ipt & ' + # 'age_years > 15', + # p["rr_ipt_adult"]), + # ) + + predictors = [ Predictor("hv_inf").when(True, p["rr_relapse_hiv"]), Predictor("tb_treatment_failure") .when(True, (p["monthly_prob_relapse_tx_incomplete"] / p["monthly_prob_relapse_tx_complete"])), @@ -561,7 +578,16 @@ def pre_initialise_population(self): 'tb_on_ipt & ' 'age_years > 15', p["rr_ipt_adult"]), - ) + ] + + conditional_predictors = [ + Predictor("nc_diabetes").when(True, p['rr_relapse_diabetes']), + ] if "CardioMetabolicDisorders" in self.sim.modules else [] + + self.lm["risk_relapse_2yrs"] = LinearModel( + LinearModelType.MULTIPLICATIVE, + p["monthly_prob_relapse_tx_complete"], + *(predictors + conditional_predictors)) # risk of relapse if >=2 years post treatment self.lm["risk_relapse_late"] = LinearModel( @@ -609,720 +635,730 @@ def pre_initialise_population(self): ), ) - def send_for_screening_general(self, population): - df = population.props - p = self.parameters - rng = self.rng +def send_for_screening_general(self, population): + df = population.props + p = self.parameters + rng = self.rng - random_draw = rng.random_sample(size=len(df)) + random_draw = rng.random_sample(size=len(df)) - # randomly select some individuals for screening and testing - # this may include some newly infected active tb cases (that's fine) - screen_idx = df.index[ - df.is_alive - & ~df.tb_diagnosed - & ~df.tb_on_treatment - & (random_draw < p["rate_testing_general_pop"]) - ] - - for person in screen_idx: - self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Tb_ScreeningAndRefer(person_id=person, module=self), - topen=random_date(self.sim.date, self.sim.date + DateOffset(months=1), self.rng), - tclose=None, - priority=0, - ) - - def select_tb_test(self, person_id): - - df = self.sim.population.props - p = self.parameters - person = df.loc[person_id] + # randomly select some individuals for screening and testing + # this may include some newly infected active tb cases (that's fine) + screen_idx = df.index[ + df.is_alive + & ~df.tb_diagnosed + & ~df.tb_on_treatment + & (random_draw < p["rate_testing_general_pop"]) + ] - # xpert tests limited to 60% coverage - # if selected test is xpert, check for availability - # give sputum smear as back-up - # assume sputum smear always available + for person in screen_idx: + self.sim.modules["HealthSystem"].schedule_hsi_event( + HSI_Tb_ScreeningAndRefer(person_id=person, module=self), + topen=random_date(self.sim.date, self.sim.date + DateOffset(months=1), self.rng), + tclose=None, + priority=0, + ) - # previously diagnosed/treated or hiv+ -> xpert - if person["tb_ever_treated"] or person["hv_diagnosed"] or (p["first_line_test"] == 'xpert'): - return "xpert" - else: - return "sputum" - def get_consumables_for_dx_and_tx(self): - p = self.parameters - # consumables = self.sim.modules["HealthSystem"].parameters["Consumables"] - hs = self.sim.modules["HealthSystem"] - - # TB Sputum smear test - # assume that if smear-positive, sputum smear test is 100% specific and sensitive - self.item_codes_for_consumables_required['sputum_test'] = \ - hs.get_item_codes_from_package_name("Microscopy Test") - - self.sim.modules['HealthSystem'].dx_manager.register_dx_test( - tb_sputum_test_smear_positive=DxTest( - property='tb_inf', - target_categories=["active"], - sensitivity=p["sens_sputum_smear_positive"], - specificity=p["spec_sputum_smear_positive"], - item_codes=self.item_codes_for_consumables_required['sputum_test'] - ) +def select_tb_test(self, person_id): + df = self.sim.population.props + p = self.parameters + person = df.loc[person_id] + + # xpert tests limited to 60% coverage + # if selected test is xpert, check for availability + # give sputum smear as back-up + # assume sputum smear always available + + # previously diagnosed/treated or hiv+ -> xpert + if person["tb_ever_treated"] or person["hv_diagnosed"] or (p["first_line_test"] == 'xpert'): + return "xpert" + else: + return "sputum" + + +def get_consumables_for_dx_and_tx(self): + p = self.parameters + # consumables = self.sim.modules["HealthSystem"].parameters["Consumables"] + hs = self.sim.modules["HealthSystem"] + + # TB Sputum smear test + # assume that if smear-positive, sputum smear test is 100% specific and sensitive + self.item_codes_for_consumables_required['sputum_test'] = \ + hs.get_item_codes_from_package_name("Microscopy Test") + + self.sim.modules['HealthSystem'].dx_manager.register_dx_test( + tb_sputum_test_smear_positive=DxTest( + property='tb_inf', + target_categories=["active"], + sensitivity=p["sens_sputum_smear_positive"], + specificity=p["spec_sputum_smear_positive"], + item_codes=self.item_codes_for_consumables_required['sputum_test'] ) - self.sim.modules['HealthSystem'].dx_manager.register_dx_test( - tb_sputum_test_smear_negative=DxTest( - property='tb_inf', - target_categories=["active"], - sensitivity=0.0, - specificity=1.0, - item_codes=self.item_codes_for_consumables_required['sputum_test'] - ) + ) + self.sim.modules['HealthSystem'].dx_manager.register_dx_test( + tb_sputum_test_smear_negative=DxTest( + property='tb_inf', + target_categories=["active"], + sensitivity=0.0, + specificity=1.0, + item_codes=self.item_codes_for_consumables_required['sputum_test'] ) - - # TB GeneXpert - self.item_codes_for_consumables_required['xpert_test'] = \ - hs.get_item_codes_from_package_name("Xpert test") - - # sensitivity/specificity set for smear status of cases - self.sim.modules["HealthSystem"].dx_manager.register_dx_test( - tb_xpert_test_smear_positive=DxTest( - property="tb_inf", - target_categories=["active"], - sensitivity=p["sens_xpert_smear_positive"], - specificity=p["spec_xpert_smear_positive"], - item_codes=self.item_codes_for_consumables_required['xpert_test'] - ) + ) + + # TB GeneXpert + self.item_codes_for_consumables_required['xpert_test'] = \ + hs.get_item_codes_from_package_name("Xpert test") + + # sensitivity/specificity set for smear status of cases + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( + tb_xpert_test_smear_positive=DxTest( + property="tb_inf", + target_categories=["active"], + sensitivity=p["sens_xpert_smear_positive"], + specificity=p["spec_xpert_smear_positive"], + item_codes=self.item_codes_for_consumables_required['xpert_test'] ) - self.sim.modules["HealthSystem"].dx_manager.register_dx_test( - tb_xpert_test_smear_negative=DxTest( - property="tb_inf", - target_categories=["active"], - sensitivity=p["sens_xpert_smear_negative"], - specificity=p["spec_xpert_smear_negative"], - item_codes=self.item_codes_for_consumables_required['xpert_test'] - ) + ) + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( + tb_xpert_test_smear_negative=DxTest( + property="tb_inf", + target_categories=["active"], + sensitivity=p["sens_xpert_smear_negative"], + specificity=p["spec_xpert_smear_negative"], + item_codes=self.item_codes_for_consumables_required['xpert_test'] ) - - # TB Chest x-ray - self.item_codes_for_consumables_required['chest_xray'] = { - hs.get_item_code_from_item_name("X-ray"): 1} - - # sensitivity/specificity set for smear status of cases - self.sim.modules["HealthSystem"].dx_manager.register_dx_test( - tb_xray_smear_positive=DxTest( - property="tb_inf", - target_categories=["active"], - sensitivity=p["sens_xray_smear_positive"], - specificity=p["spec_xray_smear_positive"], - item_codes=self.item_codes_for_consumables_required['chest_xray'] - ) + ) + + # TB Chest x-ray + self.item_codes_for_consumables_required['chest_xray'] = { + hs.get_item_code_from_item_name("X-ray"): 1} + + # sensitivity/specificity set for smear status of cases + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( + tb_xray_smear_positive=DxTest( + property="tb_inf", + target_categories=["active"], + sensitivity=p["sens_xray_smear_positive"], + specificity=p["spec_xray_smear_positive"], + item_codes=self.item_codes_for_consumables_required['chest_xray'] ) - self.sim.modules["HealthSystem"].dx_manager.register_dx_test( - tb_xray_smear_negative=DxTest( - property="tb_inf", - target_categories=["active"], - sensitivity=p["sens_xray_smear_negative"], - specificity=p["spec_xray_smear_negative"], - item_codes=self.item_codes_for_consumables_required['chest_xray'] - ) + ) + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( + tb_xray_smear_negative=DxTest( + property="tb_inf", + target_categories=["active"], + sensitivity=p["sens_xray_smear_negative"], + specificity=p["spec_xray_smear_negative"], + item_codes=self.item_codes_for_consumables_required['chest_xray'] ) - - # TB clinical diagnosis - self.sim.modules["HealthSystem"].dx_manager.register_dx_test( - tb_clinical=DxTest( - property="tb_inf", - target_categories=["active"], - sensitivity=p["sens_clinical"], - specificity=p["spec_clinical"], - item_codes=[] - ) + ) + + # TB clinical diagnosis + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( + tb_clinical=DxTest( + property="tb_inf", + target_categories=["active"], + sensitivity=p["sens_clinical"], + specificity=p["spec_clinical"], + item_codes=[] ) + ) + + # 4) -------- Define the treatment options -------- + # adult treatment - primary + self.item_codes_for_consumables_required['tb_tx_adult'] = \ + hs.get_item_code_from_item_name("Cat. I & III Patient Kit A") + + # child treatment - primary + self.item_codes_for_consumables_required['tb_tx_child'] = \ + hs.get_item_code_from_item_name("Cat. I & III Patient Kit B") + + # child treatment - primary, shorter regimen + self.item_codes_for_consumables_required['tb_tx_child_shorter'] = \ + hs.get_item_code_from_item_name("Cat. I & III Patient Kit B") + + # adult treatment - secondary + self.item_codes_for_consumables_required['tb_retx_adult'] = \ + hs.get_item_code_from_item_name("Cat. II Patient Kit A1") + + # child treatment - secondary + self.item_codes_for_consumables_required['tb_retx_child'] = \ + hs.get_item_code_from_item_name("Cat. II Patient Kit A2") + + # mdr treatment + self.item_codes_for_consumables_required['tb_mdrtx'] = { + hs.get_item_code_from_item_name("Treatment: second-line drugs"): 1} + + # ipt + self.item_codes_for_consumables_required['tb_ipt'] = { + hs.get_item_code_from_item_name("Isoniazid/Pyridoxine, tablet 300 mg"): 1} + + +def initialise_population(self, population): + df = population.props + p = self.parameters + + # if HIV is not registered, create a dummy property + if "Hiv" not in self.sim.modules: + population.make_test_property("hv_inf", Types.BOOL) + population.make_test_property("sy_aids_symptoms", Types.INT) + population.make_test_property("hv_art", Types.STRING) + + df["hv_inf"] = False + df["sy_aids_symptoms"] = 0 + df["hv_art"] = "not" + + # Set our property values for the initial population + df["tb_inf"].values[:] = "uninfected" + df["tb_strain"].values[:] = "none" + + df["tb_date_latent"] = pd.NaT + df["tb_scheduled_date_active"] = pd.NaT + df["tb_date_active"] = pd.NaT + df["tb_smear"] = False + + # ------------------ testing status ------------------ # + df["tb_date_tested"] = pd.NaT + df["tb_diagnosed"] = False + df["tb_date_diagnosed"] = pd.NaT + df["tb_diagnosed_mdr"] = False + + # ------------------ treatment status ------------------ # + df["tb_on_treatment"] = False + df["tb_date_treated"] = pd.NaT + df["tb_treatment_regimen"].values[:] = "none" + df["tb_ever_treated"] = False + df["tb_treatment_failure"] = False + + df["tb_on_ipt"] = False + df["tb_date_ipt"] = pd.NaT + + # # ------------------ infection status ------------------ # + # WHO estimates of active TB for 2010 to get infected initial population + # don't need to scale or include treated proportion as no-one on treatment yet + inc_estimates = p["who_incidence_estimates"] + incidence_year = (inc_estimates.loc[ + (inc_estimates.year == self.sim.date.year), "incidence_per_100k" + ].values[0]) / 100_000 + + incidence_year = incidence_year * p["scaling_factor_WHO"] + + self.assign_active_tb( + population, + strain="ds", + incidence=incidence_year) + + self.assign_active_tb( + population, + strain="mdr", + incidence=incidence_year * p['prop_mdr2010']) + + self.send_for_screening_general( + population + ) # send some baseline population for screening + + +def initialise_simulation(self, sim): + """ + * 1) Schedule the regular TB events + * 2) Schedule the scenario change + * 3) Define the DxTests and treatment options + """ - # 4) -------- Define the treatment options -------- - # adult treatment - primary - self.item_codes_for_consumables_required['tb_tx_adult'] = \ - hs.get_item_code_from_item_name("Cat. I & III Patient Kit A") - - # child treatment - primary - self.item_codes_for_consumables_required['tb_tx_child'] = \ - hs.get_item_code_from_item_name("Cat. I & III Patient Kit B") - - # child treatment - primary, shorter regimen - self.item_codes_for_consumables_required['tb_tx_child_shorter'] = \ - hs.get_item_code_from_item_name("Cat. I & III Patient Kit B") - - # adult treatment - secondary - self.item_codes_for_consumables_required['tb_retx_adult'] = \ - hs.get_item_code_from_item_name("Cat. II Patient Kit A1") - - # child treatment - secondary - self.item_codes_for_consumables_required['tb_retx_child'] = \ - hs.get_item_code_from_item_name("Cat. II Patient Kit A2") - - # mdr treatment - self.item_codes_for_consumables_required['tb_mdrtx'] = { - hs.get_item_code_from_item_name("Treatment: second-line drugs"): 1} - - # ipt - self.item_codes_for_consumables_required['tb_ipt'] = { - hs.get_item_code_from_item_name("Isoniazid/Pyridoxine, tablet 300 mg"): 1} - - def initialise_population(self, population): - - df = population.props - p = self.parameters - - # if HIV is not registered, create a dummy property - if "Hiv" not in self.sim.modules: - population.make_test_property("hv_inf", Types.BOOL) - population.make_test_property("sy_aids_symptoms", Types.INT) - population.make_test_property("hv_art", Types.STRING) - - df["hv_inf"] = False - df["sy_aids_symptoms"] = 0 - df["hv_art"] = "not" - - # Set our property values for the initial population - df["tb_inf"].values[:] = "uninfected" - df["tb_strain"].values[:] = "none" - - df["tb_date_latent"] = pd.NaT - df["tb_scheduled_date_active"] = pd.NaT - df["tb_date_active"] = pd.NaT - df["tb_smear"] = False - - # ------------------ testing status ------------------ # - df["tb_date_tested"] = pd.NaT - df["tb_diagnosed"] = False - df["tb_date_diagnosed"] = pd.NaT - df["tb_diagnosed_mdr"] = False - - # ------------------ treatment status ------------------ # - df["tb_on_treatment"] = False - df["tb_date_treated"] = pd.NaT - df["tb_treatment_regimen"].values[:] = "none" - df["tb_ever_treated"] = False - df["tb_treatment_failure"] = False - - df["tb_on_ipt"] = False - df["tb_date_ipt"] = pd.NaT - - # # ------------------ infection status ------------------ # - # WHO estimates of active TB for 2010 to get infected initial population - # don't need to scale or include treated proportion as no-one on treatment yet - inc_estimates = p["who_incidence_estimates"] - incidence_year = (inc_estimates.loc[ - (inc_estimates.year == self.sim.date.year), "incidence_per_100k" - ].values[0]) / 100_000 - - incidence_year = incidence_year * p["scaling_factor_WHO"] - - self.assign_active_tb( - population, - strain="ds", - incidence=incidence_year) - - self.assign_active_tb( - population, - strain="mdr", - incidence=incidence_year * p['prop_mdr2010']) - - self.send_for_screening_general( - population - ) # send some baseline population for screening - - def initialise_simulation(self, sim): - """ - * 1) Schedule the regular TB events - * 2) Schedule the scenario change - * 3) Define the DxTests and treatment options - """ - - # 1) Regular events - sim.schedule_event(TbActiveEvent(self), sim.date) - sim.schedule_event(TbTreatmentAndRelapseEvents(self), sim.date) - sim.schedule_event(TbSelfCureEvent(self), sim.date) - sim.schedule_event(TbActiveCasePoll(self), sim.date + DateOffset(years=1)) - - # log at the end of the year - sim.schedule_event(TbLoggingEvent(self), sim.date + DateOffset(years=1)) - - # 2) Scenario change - sim.schedule_event(ScenarioSetupEvent(self), self.parameters["scenario_start_date"]) - - # 3) Define the DxTests and get the consumables required - self.get_consumables_for_dx_and_tx() - - # 4) (Optionally) Schedule the event to check the configuration of all properties - if self.run_with_checks: - sim.schedule_event( - TbCheckPropertiesEvent(self), sim.date + pd.DateOffset(months=1) - ) - - def on_birth(self, mother_id, child_id): - """Initialise properties for a newborn individual - allocate IPT for child if mother diagnosed with TB - """ - - df = self.sim.population.props - now = self.sim.date - - df.at[child_id, "tb_inf"] = "uninfected" - df.at[child_id, "tb_strain"] = "none" + # 1) Regular events + sim.schedule_event(TbActiveEvent(self), sim.date) + sim.schedule_event(TbTreatmentAndRelapseEvents(self), sim.date) + sim.schedule_event(TbSelfCureEvent(self), sim.date) + sim.schedule_event(TbActiveCasePoll(self), sim.date + DateOffset(years=1)) - df.at[child_id, "tb_date_latent"] = pd.NaT - df.at[child_id, "tb_scheduled_date_active"] = pd.NaT - df.at[child_id, "tb_date_active"] = pd.NaT - df.at[child_id, "tb_smear"] = False + # log at the end of the year + sim.schedule_event(TbLoggingEvent(self), sim.date + DateOffset(years=1)) - # ------------------ testing status ------------------ # - df.at[child_id, "tb_date_tested"] = pd.NaT + # 2) Scenario change + sim.schedule_event(ScenarioSetupEvent(self), self.parameters["scenario_start_date"]) - df.at[child_id, "tb_diagnosed"] = False - df.at[child_id, "tb_date_diagnosed"] = pd.NaT - df.at[child_id, "tb_diagnosed_mdr"] = False + # 3) Define the DxTests and get the consumables required + self.get_consumables_for_dx_and_tx() - # ------------------ treatment status ------------------ # - df.at[child_id, "tb_on_treatment"] = False - df.at[child_id, "tb_date_treated"] = pd.NaT - df.at[child_id, "tb_treatment_regimen"] = "none" - df.at[child_id, "tb_treatment_failure"] = False - df.at[child_id, "tb_ever_treated"] = False - - df.at[child_id, "tb_on_ipt"] = False - df.at[child_id, "tb_date_ipt"] = pd.NaT - - if "Hiv" not in self.sim.modules: - df.at[child_id, "hv_inf"] = False - df.at[child_id, "sy_aids_symptoms"] = 0 - df.at[child_id, "hv_art"] = "not" - - # Not interested in whether true or direct birth - # give IPT to child of TB diagnosed mother if 2014 or later - if df.at[abs(mother_id), "tb_diagnosed"] and (now.year >= self.parameters["ipt_start_date"]): - event = HSI_Tb_Start_or_Continue_Ipt(self, person_id=child_id) - self.sim.modules["HealthSystem"].schedule_hsi_event( - event, - priority=1, - topen=now, - tclose=now + DateOffset(days=28), - ) + # 4) (Optionally) Schedule the event to check the configuration of all properties + if self.run_with_checks: + sim.schedule_event( + TbCheckPropertiesEvent(self), sim.date + pd.DateOffset(months=1) + ) - def report_daly_values(self): - """ - This must send back a pd.Series or pd.DataFrame that reports on the average daly-weights that have been - experienced by persons in the previous month. Only rows for alive-persons must be returned. - The names of the series of columns is taken to be the label of the cause of this disability. - It will be recorded by the healthburden module as _. - """ - df = self.sim.population.props # shortcut to population properties dataframe - # to avoid errors when hiv module not running - df_tmp = df.loc[df.is_alive] - health_values = pd.Series(0, index=df_tmp.index) +def on_birth(self, mother_id, child_id): + """Initialise properties for a newborn individual + allocate IPT for child if mother diagnosed with TB + """ - # hiv-negative - health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "ds") - & ~df_tmp.hv_inf - ] = self.daly_wts["daly_tb"] - - health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "mdr") - & ~df_tmp.hv_inf - ] = self.daly_wts["daly_tb"] - - # hiv-positive - health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "ds") - & df_tmp.hv_inf - ] = self.daly_wts["daly_tb_hiv"] - - health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "mdr") - & df_tmp.hv_inf - ] = self.daly_wts["daly_mdr_tb_hiv"] - - return health_values.loc[df.is_alive] - - def calculate_untreated_proportion(self, population, strain): - """ - calculate the proportion of active TB cases not on correct treatment - if mdr-tb and on first-line treatment, count case as untreated - they will continue to contribute to transmission - """ - df = population.props + df = self.sim.population.props + now = self.sim.date + + df.at[child_id, "tb_inf"] = "uninfected" + df.at[child_id, "tb_strain"] = "none" + + df.at[child_id, "tb_date_latent"] = pd.NaT + df.at[child_id, "tb_scheduled_date_active"] = pd.NaT + df.at[child_id, "tb_date_active"] = pd.NaT + df.at[child_id, "tb_smear"] = False + + # ------------------ testing status ------------------ # + df.at[child_id, "tb_date_tested"] = pd.NaT + + df.at[child_id, "tb_diagnosed"] = False + df.at[child_id, "tb_date_diagnosed"] = pd.NaT + df.at[child_id, "tb_diagnosed_mdr"] = False + + # ------------------ treatment status ------------------ # + df.at[child_id, "tb_on_treatment"] = False + df.at[child_id, "tb_date_treated"] = pd.NaT + df.at[child_id, "tb_treatment_regimen"] = "none" + df.at[child_id, "tb_treatment_failure"] = False + df.at[child_id, "tb_ever_treated"] = False + + df.at[child_id, "tb_on_ipt"] = False + df.at[child_id, "tb_date_ipt"] = pd.NaT + + if "Hiv" not in self.sim.modules: + df.at[child_id, "hv_inf"] = False + df.at[child_id, "sy_aids_symptoms"] = 0 + df.at[child_id, "hv_art"] = "not" + + # Not interested in whether true or direct birth + # give IPT to child of TB diagnosed mother if 2014 or later + if df.at[abs(mother_id), "tb_diagnosed"] and (now.year >= self.parameters["ipt_start_date"]): + event = HSI_Tb_Start_or_Continue_Ipt(self, person_id=child_id) + self.sim.modules["HealthSystem"].schedule_hsi_event( + event, + priority=1, + topen=now, + tclose=now + DateOffset(days=28), + ) - # sum active tb cases - num_active_tb_cases = len(df[(df.tb_inf == "active") & - (df.tb_strain == strain) & - df.is_alive]) - - # sum treated active tb cases - # if mdr-tb must be on mdr treatment, otherwise consider as untreated case - if strain == "mdr": - num_treated_tb_cases = len(df[(df.tb_inf == "active") & - (df.tb_strain == strain) & - df.tb_on_treatment & - (df.tb_treatment_regimen == "tb_mdrtx") & - df.is_alive]) - else: - num_treated_tb_cases = len(df[(df.tb_inf == "active") & - (df.tb_strain == strain) & - df.tb_on_treatment & - df.is_alive]) - prop_untreated = 1 - (num_treated_tb_cases / num_active_tb_cases) if num_active_tb_cases else 1 +def report_daly_values(self): + """ + This must send back a pd.Series or pd.DataFrame that reports on the average daly-weights that have been + experienced by persons in the previous month. Only rows for alive-persons must be returned. + The names of the series of columns is taken to be the label of the cause of this disability. + It will be recorded by the healthburden module as _. + """ + df = self.sim.population.props # shortcut to population properties dataframe + + # to avoid errors when hiv module not running + df_tmp = df.loc[df.is_alive] + health_values = pd.Series(0, index=df_tmp.index) + + # hiv-negative + health_values.loc[ + (df_tmp.tb_inf == "active") + & (df_tmp.tb_strain == "ds") + & ~df_tmp.hv_inf + ] = self.daly_wts["daly_tb"] + + health_values.loc[ + (df_tmp.tb_inf == "active") + & (df_tmp.tb_strain == "mdr") + & ~df_tmp.hv_inf + ] = self.daly_wts["daly_tb"] + + # hiv-positive + health_values.loc[ + (df_tmp.tb_inf == "active") + & (df_tmp.tb_strain == "ds") + & df_tmp.hv_inf + ] = self.daly_wts["daly_tb_hiv"] + + health_values.loc[ + (df_tmp.tb_inf == "active") + & (df_tmp.tb_strain == "mdr") + & df_tmp.hv_inf + ] = self.daly_wts["daly_mdr_tb_hiv"] + + return health_values.loc[df.is_alive] + + +def calculate_untreated_proportion(self, population, strain): + """ + calculate the proportion of active TB cases not on correct treatment + if mdr-tb and on first-line treatment, count case as untreated + they will continue to contribute to transmission + """ + df = population.props - return prop_untreated + # sum active tb cases + num_active_tb_cases = len(df[(df.tb_inf == "active") & + (df.tb_strain == strain) & + df.is_alive]) - def assign_active_tb(self, population, strain, incidence): - """ - select individuals to be infected - assign scheduled date of active tb onset - update properties as needed - symptoms and smear status are assigned in the TbActiveEvent - """ + # sum treated active tb cases + # if mdr-tb must be on mdr treatment, otherwise consider as untreated case + if strain == "mdr": + num_treated_tb_cases = len(df[(df.tb_inf == "active") & + (df.tb_strain == strain) & + df.tb_on_treatment & + (df.tb_treatment_regimen == "tb_mdrtx") & + df.is_alive]) + else: + num_treated_tb_cases = len(df[(df.tb_inf == "active") & + (df.tb_strain == strain) & + df.tb_on_treatment & + df.is_alive]) - df = population.props - rng = self.rng - now = self.sim.date + prop_untreated = 1 - (num_treated_tb_cases / num_active_tb_cases) if num_active_tb_cases else 1 - # identify eligible people, not currently with active tb infection - eligible = df.loc[ - df.is_alive - & (df.tb_inf != "active") - ].index + return prop_untreated - # weight risk by individual characteristics - # Compute chance that each susceptible person becomes infected: - rr_of_infection = self.lm["active_tb"].predict( - df.loc[eligible] - ) - # probability of infection - p_infection = (rr_of_infection * incidence) +def assign_active_tb(self, population, strain, incidence): + """ + select individuals to be infected + assign scheduled date of active tb onset + update properties as needed + symptoms and smear status are assigned in the TbActiveEvent + """ - # New infections: - will_be_infected = ( - self.rng.random_sample(len(p_infection)) < p_infection + df = population.props + rng = self.rng + now = self.sim.date + + # identify eligible people, not currently with active tb infection + eligible = df.loc[ + df.is_alive + & (df.tb_inf != "active") + ].index + + # weight risk by individual characteristics + # Compute chance that each susceptible person becomes infected: + rr_of_infection = self.lm["active_tb"].predict( + df.loc[eligible] + ) + + # probability of infection + p_infection = (rr_of_infection * incidence) + + # New infections: + will_be_infected = ( + self.rng.random_sample(len(p_infection)) < p_infection + ) + idx_new_infection = will_be_infected[will_be_infected].index + + df.loc[idx_new_infection, "tb_strain"] = strain + + # schedule onset of active tb, time now up to 1 year + for person_id in idx_new_infection: + date_progression = now + pd.DateOffset( + days=rng.randint(0, 365) ) - idx_new_infection = will_be_infected[will_be_infected].index - df.loc[idx_new_infection, "tb_strain"] = strain + # set date of active tb - properties will be updated at TbActiveEvent poll daily + df.at[person_id, "tb_scheduled_date_active"] = date_progression - # schedule onset of active tb, time now up to 1 year - for person_id in idx_new_infection: - date_progression = now + pd.DateOffset( - days=rng.randint(0, 365) - ) - - # set date of active tb - properties will be updated at TbActiveEvent poll daily - df.at[person_id, "tb_scheduled_date_active"] = date_progression - def consider_ipt_for_those_initiating_art(self, person_id): - """ - this is called by HIV when person is initiating ART - checks whether person is eligible for IPT - """ - df = self.sim.population.props +def consider_ipt_for_those_initiating_art(self, person_id): + """ + this is called by HIV when person is initiating ART + checks whether person is eligible for IPT + """ + df = self.sim.population.props - if df.loc[person_id, "tb_diagnosed"] or df.loc[person_id, "tb_diagnosed_mdr"]: - pass + if df.loc[person_id, "tb_diagnosed"] or df.loc[person_id, "tb_diagnosed_mdr"]: + pass - high_risk_districts = self.parameters["tb_high_risk_distr"] - district = df.at[person_id, "district_of_residence"] - eligible = df.at[person_id, "tb_inf"] != "active" + high_risk_districts = self.parameters["tb_high_risk_distr"] + district = df.at[person_id, "district_of_residence"] + eligible = df.at[person_id, "tb_inf"] != "active" + + # select coverage rate by year: + now = self.sim.date + year = now.year if now.year <= 2050 else 2050 + + ipt = self.parameters["ipt_coverage"] + ipt_year = ipt.loc[ipt.year == year] + ipt_coverage_plhiv = ipt_year.coverage_plhiv + + if ( + (district in high_risk_districts.district_name.values) + & eligible + & (self.rng.rand() < ipt_coverage_plhiv.values) + ): + # Schedule the TB treatment event: + self.sim.modules["HealthSystem"].schedule_hsi_event( + HSI_Tb_Start_or_Continue_Ipt(self, person_id=person_id), + priority=1, + topen=self.sim.date, + tclose=None, + ) - # select coverage rate by year: - now = self.sim.date - year = now.year if now.year <= 2050 else 2050 - ipt = self.parameters["ipt_coverage"] - ipt_year = ipt.loc[ipt.year == year] - ipt_coverage_plhiv = ipt_year.coverage_plhiv +def relapse_event(self, population): + """The Tb Regular Relapse Event + runs every month to randomly sample amongst those previously infected with active tb + * Schedules persons who have previously been infected to relapse with a set probability + * Sets a scheduled_date_active which is picked up by TbActiveEvent + """ - if ( - (district in high_risk_districts.district_name.values) - & eligible - & (self.rng.rand() < ipt_coverage_plhiv.values) - ): - # Schedule the TB treatment event: - self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Tb_Start_or_Continue_Ipt(self, person_id=person_id), - priority=1, - topen=self.sim.date, - tclose=None, + df = population.props + rng = self.rng + now = self.sim.date + + # need a monthly relapse for every person in df + # should return risk=0 for everyone not eligible for relapse + + # risk of relapse if <2 years post treatment start, includes risk if HIV+ + risk_of_relapse_early = self.lm["risk_relapse_2yrs"].predict( + df.loc[df.is_alive + & df.tb_ever_treated + & (df.tb_inf == "latent") + & (now < (df.tb_date_treated + pd.DateOffset(years=2)))] + ) + + will_relapse = ( + rng.random_sample(len(risk_of_relapse_early)) < risk_of_relapse_early + ) + idx_will_relapse_early = will_relapse[will_relapse].index + + # risk of relapse if >=2 years post treatment start, includes risk if HIV+ + risk_of_relapse_later = self.lm["risk_relapse_late"].predict( + df.loc[df.is_alive + & df.tb_ever_treated + & (df.tb_inf == "latent") + & (now >= (df.tb_date_treated + pd.DateOffset(years=2)))] + ) + + will_relapse_later = ( + rng.random_sample(len(risk_of_relapse_later)) < risk_of_relapse_later + ) + idx_will_relapse_late2 = will_relapse_later[will_relapse_later].index + + # join both indices + idx_will_relapse = idx_will_relapse_early.union( + idx_will_relapse_late2 + ).drop_duplicates() + + # set date of scheduled active tb + # properties will be updated at TbActiveEvent every month + df.loc[idx_will_relapse, "tb_scheduled_date_active"] = now + + +def end_treatment(self, population): + """ + * check for those eligible to finish treatment + * sample for treatment failure and refer for follow-up screening/testing + * if treatment has finished, change individual properties + """ + + df = population.props + rng = self.rng + now = self.sim.date + p = self.parameters + + # check across population on tb treatment and end treatment if required + # if current date is after (treatment start date + treatment length) -> end tx + + # ---------------------- treatment end: first case ds-tb (6 months) ---------------------- # + # end treatment for new tb (ds) cases + end_ds_tx_idx = df.loc[ + df.is_alive + & df.tb_on_treatment + & ((df.tb_treatment_regimen == "tb_tx_adult") | (df.tb_treatment_regimen == "tb_tx_child")) + & ( + now + > (df.tb_date_treated + pd.DateOffset(months=p["ds_treatment_length"])) + ) + ].index + + # ---------------------- treatment end: retreatment ds-tb (7 months) ---------------------- # + # end treatment for retreatment cases + end_ds_retx_idx = df.loc[ + df.is_alive + & df.tb_on_treatment + & ((df.tb_treatment_regimen == "tb_retx_adult") | (df.tb_treatment_regimen == "tb_retx_child")) + & ( + now + > ( + df.tb_date_treated + + pd.DateOffset(months=p["ds_retreatment_length"]) ) - - def relapse_event(self, population): - """The Tb Regular Relapse Event - runs every month to randomly sample amongst those previously infected with active tb - * Schedules persons who have previously been infected to relapse with a set probability - * Sets a scheduled_date_active which is picked up by TbActiveEvent - """ - - df = population.props - rng = self.rng - now = self.sim.date - - # need a monthly relapse for every person in df - # should return risk=0 for everyone not eligible for relapse - - # risk of relapse if <2 years post treatment start, includes risk if HIV+ - risk_of_relapse_early = self.lm["risk_relapse_2yrs"].predict( - df.loc[df.is_alive - & df.tb_ever_treated - & (df.tb_inf == "latent") - & (now < (df.tb_date_treated + pd.DateOffset(years=2)))] ) - - will_relapse = ( - rng.random_sample(len(risk_of_relapse_early)) < risk_of_relapse_early + ].index + + # ---------------------- treatment end: mdr-tb (24 months) ---------------------- # + # end treatment for mdr-tb cases + end_mdr_tx_idx = df.loc[ + df.is_alive + & df.tb_on_treatment + & (df.tb_treatment_regimen == "tb_mdrtx") + & ( + now + > (df.tb_date_treated + pd.DateOffset(months=p["mdr_treatment_length"])) ) - idx_will_relapse_early = will_relapse[will_relapse].index - - # risk of relapse if >=2 years post treatment start, includes risk if HIV+ - risk_of_relapse_later = self.lm["risk_relapse_late"].predict( - df.loc[df.is_alive - & df.tb_ever_treated - & (df.tb_inf == "latent") - & (now >= (df.tb_date_treated + pd.DateOffset(years=2)))] + ].index + + # ---------------------- treatment end: shorter paediatric regimen ---------------------- # + # end treatment for paediatric cases on 4 month regimen + end_tx_shorter_idx = df.loc[ + df.is_alive + & df.tb_on_treatment + & (df.tb_treatment_regimen == "tb_tx_child_shorter") + & ( + now + > (df.tb_date_treated + pd.DateOffset(months=p["child_shorter_treatment_length"])) ) - - will_relapse_later = ( - rng.random_sample(len(risk_of_relapse_later)) < risk_of_relapse_later + ].index + + # join indices + end_tx_idx = end_ds_tx_idx.union(end_ds_retx_idx) + end_tx_idx = end_tx_idx.union(end_mdr_tx_idx) + end_tx_idx = end_tx_idx.union(end_tx_shorter_idx) + + # ---------------------- treatment failure ---------------------- # + # sample some to have treatment failure + # assume all retreatment cases will cure + random_var = rng.random_sample(size=len(df)) + + # children aged 0-4 ds-tb + ds_tx_failure0_4_idx = df.loc[ + (df.index.isin(end_ds_tx_idx)) + & (df.age_years < 5) + & (random_var < (1 - p["prob_tx_success_0_4"])) + ].index + + # children aged 5-14 ds-tb + ds_tx_failure5_14_idx = df.loc[ + (df.index.isin(end_ds_tx_idx)) + & (df.age_years.between(5, 14)) + & (random_var < (1 - p["prob_tx_success_5_14"])) + ].index + + # children aged <16 and on shorter regimen + ds_tx_failure_shorter_idx = df.loc[ + (df.index.isin(end_tx_shorter_idx)) + & (df.age_years < 16) + & (random_var < (1 - p["prob_tx_success_shorter"])) + ].index + + # adults ds-tb + ds_tx_failure_adult_idx = df.loc[ + (df.index.isin(end_ds_tx_idx)) + & (df.age_years >= 15) + & (random_var < (1 - p["prob_tx_success_ds"])) + ].index + + # all mdr cases on ds tx will fail + failure_in_mdr_with_ds_tx_idx = df.loc[ + (df.index.isin(end_ds_tx_idx)) + & (df.tb_strain == "mdr") + ].index + + # some mdr cases on mdr treatment will fail + failure_due_to_mdr_idx = df.loc[ + (df.index.isin(end_mdr_tx_idx)) + & (df.tb_strain == "mdr") + & (random_var < (1 - p["prob_tx_success_mdr"])) + + ].index + + # join indices of failing cases together + tx_failure = reduce( + pd.Index.union, + ( + ds_tx_failure0_4_idx, + ds_tx_failure5_14_idx, + ds_tx_failure_shorter_idx, + ds_tx_failure_adult_idx, + failure_in_mdr_with_ds_tx_idx, + failure_due_to_mdr_idx, ) - idx_will_relapse_late2 = will_relapse_later[will_relapse_later].index - - # join both indices - idx_will_relapse = idx_will_relapse_early.union( - idx_will_relapse_late2 - ).drop_duplicates() - - # set date of scheduled active tb - # properties will be updated at TbActiveEvent every month - df.loc[idx_will_relapse, "tb_scheduled_date_active"] = now - - def end_treatment(self, population): - """ - * check for those eligible to finish treatment - * sample for treatment failure and refer for follow-up screening/testing - * if treatment has finished, change individual properties - """ - - df = population.props - rng = self.rng - now = self.sim.date - p = self.parameters - - # check across population on tb treatment and end treatment if required - # if current date is after (treatment start date + treatment length) -> end tx - - # ---------------------- treatment end: first case ds-tb (6 months) ---------------------- # - # end treatment for new tb (ds) cases - end_ds_tx_idx = df.loc[ - df.is_alive - & df.tb_on_treatment - & ((df.tb_treatment_regimen == "tb_tx_adult") | (df.tb_treatment_regimen == "tb_tx_child")) - & ( - now - > (df.tb_date_treated + pd.DateOffset(months=p["ds_treatment_length"])) - ) - ].index + ) - # ---------------------- treatment end: retreatment ds-tb (7 months) ---------------------- # - # end treatment for retreatment cases - end_ds_retx_idx = df.loc[ - df.is_alive - & df.tb_on_treatment - & ((df.tb_treatment_regimen == "tb_retx_adult") | (df.tb_treatment_regimen == "tb_retx_child")) - & ( - now - > ( - df.tb_date_treated - + pd.DateOffset(months=p["ds_retreatment_length"]) - ) - ) - ].index + if not tx_failure.empty: + df.loc[tx_failure, "tb_treatment_failure"] = True + df.loc[ + tx_failure, "tb_ever_treated" + ] = True # ensure classed as retreatment case - # ---------------------- treatment end: mdr-tb (24 months) ---------------------- # - # end treatment for mdr-tb cases - end_mdr_tx_idx = df.loc[ - df.is_alive - & df.tb_on_treatment - & (df.tb_treatment_regimen == "tb_mdrtx") - & ( - now - > (df.tb_date_treated + pd.DateOffset(months=p["mdr_treatment_length"])) + for person in tx_failure: + self.sim.modules["HealthSystem"].schedule_hsi_event( + HSI_Tb_ScreeningAndRefer(person_id=person, module=self), + topen=self.sim.date, + tclose=None, + priority=0, ) - ].index - # ---------------------- treatment end: shorter paediatric regimen ---------------------- # - # end treatment for paediatric cases on 4 month regimen - end_tx_shorter_idx = df.loc[ + # remove any treatment failure indices from the treatment end indices + cure_idx = end_tx_idx.difference(tx_failure) + + # change individual properties for all to off treatment + df.loc[end_tx_idx, "tb_diagnosed"] = False + df.loc[end_tx_idx, "tb_on_treatment"] = False + df.loc[end_tx_idx, "tb_treated_mdr"] = False + # this will indicate that this person has had one complete course of tb treatment + # subsequent infections will be classified as retreatment + df.loc[end_tx_idx, "tb_ever_treated"] = True + + # if cured, move infection status back to latent + # leave tb_strain property set in case of relapse + df.loc[cure_idx, "tb_inf"] = "latent" + df.loc[cure_idx, "tb_date_latent"] = now + df.loc[cure_idx, "tb_smear"] = False + + # this will clear all tb symptoms + self.sim.modules["SymptomManager"].clear_symptoms( + person_id=cure_idx, disease_module=self + ) + + # if HIV+ and on ART (virally suppressed), remove AIDS symptoms if cured of TB + hiv_tb_infected = cure_idx.intersection( + df.loc[ df.is_alive - & df.tb_on_treatment - & (df.tb_treatment_regimen == "tb_tx_child_shorter") - & ( - now - > (df.tb_date_treated + pd.DateOffset(months=p["child_shorter_treatment_length"])) - ) - ].index - - # join indices - end_tx_idx = end_ds_tx_idx.union(end_ds_retx_idx) - end_tx_idx = end_tx_idx.union(end_mdr_tx_idx) - end_tx_idx = end_tx_idx.union(end_tx_shorter_idx) - - # ---------------------- treatment failure ---------------------- # - # sample some to have treatment failure - # assume all retreatment cases will cure - random_var = rng.random_sample(size=len(df)) - - # children aged 0-4 ds-tb - ds_tx_failure0_4_idx = df.loc[ - (df.index.isin(end_ds_tx_idx)) - & (df.age_years < 5) - & (random_var < (1 - p["prob_tx_success_0_4"])) - ].index - - # children aged 5-14 ds-tb - ds_tx_failure5_14_idx = df.loc[ - (df.index.isin(end_ds_tx_idx)) - & (df.age_years.between(5, 14)) - & (random_var < (1 - p["prob_tx_success_5_14"])) - ].index - - # children aged <16 and on shorter regimen - ds_tx_failure_shorter_idx = df.loc[ - (df.index.isin(end_tx_shorter_idx)) - & (df.age_years < 16) - & (random_var < (1 - p["prob_tx_success_shorter"])) - ].index - - # adults ds-tb - ds_tx_failure_adult_idx = df.loc[ - (df.index.isin(end_ds_tx_idx)) - & (df.age_years >= 15) - & (random_var < (1 - p["prob_tx_success_ds"])) - ].index - - # all mdr cases on ds tx will fail - failure_in_mdr_with_ds_tx_idx = df.loc[ - (df.index.isin(end_ds_tx_idx)) - & (df.tb_strain == "mdr") - ].index - - # some mdr cases on mdr treatment will fail - failure_due_to_mdr_idx = df.loc[ - (df.index.isin(end_mdr_tx_idx)) - & (df.tb_strain == "mdr") - & (random_var < (1 - p["prob_tx_success_mdr"])) - + & df.hv_inf + & (df.hv_art == "on_VL_suppressed") ].index + ) - # join indices of failing cases together - tx_failure = reduce( - pd.Index.union, - ( - ds_tx_failure0_4_idx, - ds_tx_failure5_14_idx, - ds_tx_failure_shorter_idx, - ds_tx_failure_adult_idx, - failure_in_mdr_with_ds_tx_idx, - failure_due_to_mdr_idx, - ) - ) + self.sim.modules["SymptomManager"].clear_symptoms( + person_id=hiv_tb_infected, disease_module=self.sim.modules["Hiv"] + ) - if not tx_failure.empty: - df.loc[tx_failure, "tb_treatment_failure"] = True - df.loc[ - tx_failure, "tb_ever_treated" - ] = True # ensure classed as retreatment case - for person in tx_failure: - self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Tb_ScreeningAndRefer(person_id=person, module=self), - topen=self.sim.date, - tclose=None, - priority=0, - ) - - # remove any treatment failure indices from the treatment end indices - cure_idx = end_tx_idx.difference(tx_failure) - - # change individual properties for all to off treatment - df.loc[end_tx_idx, "tb_diagnosed"] = False - df.loc[end_tx_idx, "tb_on_treatment"] = False - df.loc[end_tx_idx, "tb_treated_mdr"] = False - # this will indicate that this person has had one complete course of tb treatment - # subsequent infections will be classified as retreatment - df.loc[end_tx_idx, "tb_ever_treated"] = True - - # if cured, move infection status back to latent - # leave tb_strain property set in case of relapse - df.loc[cure_idx, "tb_inf"] = "latent" - df.loc[cure_idx, "tb_date_latent"] = now - df.loc[cure_idx, "tb_smear"] = False - - # this will clear all tb symptoms - self.sim.modules["SymptomManager"].clear_symptoms( - person_id=cure_idx, disease_module=self - ) +def check_config_of_properties(self): + """check that the properties are currently configured correctly""" + df = self.sim.population.props + df_alive = df.loc[df.is_alive] - # if HIV+ and on ART (virally suppressed), remove AIDS symptoms if cured of TB - hiv_tb_infected = cure_idx.intersection( - df.loc[ - df.is_alive - & df.hv_inf - & (df.hv_art == "on_VL_suppressed") - ].index - ) + # basic check types of columns and dtypes + orig = self.sim.population.new_row + assert (df.dtypes == orig.dtypes).all() - self.sim.modules["SymptomManager"].clear_symptoms( - person_id=hiv_tb_infected, disease_module=self.sim.modules["Hiv"] + def is_subset(col_for_set, col_for_subset): + # Confirms that the series of col_for_subset is true only for a subset of the series for col_for_set + return set(col_for_subset.loc[col_for_subset].index).issubset( + col_for_set.loc[col_for_set].index ) - def check_config_of_properties(self): - """check that the properties are currently configured correctly""" - df = self.sim.population.props - df_alive = df.loc[df.is_alive] + # Check that core properties of current status are never None/NaN/NaT + assert not df_alive.tb_inf.isna().any() + assert not df_alive.tb_strain.isna().any() + assert not df_alive.tb_smear.isna().any() + assert not df_alive.tb_on_treatment.isna().any() + assert not df_alive.tb_treatment_regimen.isna().any() + assert not df_alive.tb_ever_treated.isna().any() + assert not df_alive.tb_on_ipt.isna().any() - # basic check types of columns and dtypes - orig = self.sim.population.new_row - assert (df.dtypes == orig.dtypes).all() + # Check that the core TB properties are 'nested' in the way expected. + assert is_subset( + col_for_set=(df_alive.tb_inf != "uninfected"), col_for_subset=df_alive.tb_diagnosed + ) + assert is_subset( + col_for_set=df_alive.tb_diagnosed, col_for_subset=df_alive.tb_on_treatment + ) - def is_subset(col_for_set, col_for_subset): - # Confirms that the series of col_for_subset is true only for a subset of the series for col_for_set - return set(col_for_subset.loc[col_for_subset].index).issubset( - col_for_set.loc[col_for_set].index - ) - - # Check that core properties of current status are never None/NaN/NaT - assert not df_alive.tb_inf.isna().any() - assert not df_alive.tb_strain.isna().any() - assert not df_alive.tb_smear.isna().any() - assert not df_alive.tb_on_treatment.isna().any() - assert not df_alive.tb_treatment_regimen.isna().any() - assert not df_alive.tb_ever_treated.isna().any() - assert not df_alive.tb_on_ipt.isna().any() - - # Check that the core TB properties are 'nested' in the way expected. - assert is_subset( - col_for_set=(df_alive.tb_inf != "uninfected"), col_for_subset=df_alive.tb_diagnosed - ) - assert is_subset( - col_for_set=df_alive.tb_diagnosed, col_for_subset=df_alive.tb_on_treatment - ) - - # Check that if person is infected, the dates of active TB is NOT missing - assert not df.loc[(df.tb_inf == "active"), "tb_date_active"].isna().all() + # Check that if person is infected, the dates of active TB is NOT missing + assert not df.loc[(df.tb_inf == "active"), "tb_date_active"].isna().all() # # --------------------------------------------------------------------------- @@ -1463,11 +1499,11 @@ def apply(self, population): prop_untreated_mdr = self.module.calculate_untreated_proportion(population, strain="mdr") scaled_incidence_ds = incidence_year * \ - p["scaling_factor_WHO"] * prop_untreated_ds + p["scaling_factor_WHO"] * prop_untreated_ds scaled_incidence_mdr = incidence_year * \ - p["prop_mdr2010"] * \ - p["scaling_factor_WHO"] * \ - prop_untreated_mdr + p["prop_mdr2010"] * \ + p["scaling_factor_WHO"] * \ + prop_untreated_mdr # transmission ds-tb self.module.assign_active_tb(population, strain="ds", incidence=scaled_incidence_ds) @@ -2048,7 +2084,6 @@ def apply(self, person_id, squeeze_factor): # if consumables not available, refer to level 2 # return blank footprint as xray did not occur if test_result is None: - ACTUAL_APPT_FOOTPRINT = self.make_appt_footprint({}) self.sim.modules["HealthSystem"].schedule_hsi_event( @@ -2119,7 +2154,6 @@ def apply(self, person_id, squeeze_factor): # if consumables not available, rely on clinical diagnosis # return blank footprint as xray was not available if test_result is None: - ACTUAL_APPT_FOOTPRINT = self.make_appt_footprint({}) self.sim.modules["HealthSystem"].schedule_hsi_event( @@ -2285,7 +2319,7 @@ def select_treatment(self, person_id): & (person["age_years"] <= 16) \ & ~(person["tb_smear"]) \ & ~person["tb_ever_treated"] \ - & ~person["tb_diagnosed_mdr"]: + & ~person["tb_diagnosed_mdr"]: treatment_regimen = "tb_tx_child_shorter" return treatment_regimen @@ -2341,7 +2375,7 @@ def apply(self, person_id, squeeze_factor): # if previously treated: if ((person["tb_treatment_regimen"] == "tb_retx_adult") or - (person["tb_treatment_regimen"] == "tb_retx_child")): + (person["tb_treatment_regimen"] == "tb_retx_child")): # if strain is ds and person previously treated: sputum_fup = follow_up_times["ds_retreatment_sputum"].dropna() From 465b18bf5ca8130f59ba166a88ca0b793a441390 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 5 Mar 2024 10:40:14 +0000 Subject: [PATCH 047/131] add diabetes as risk factor for tb death --- resources/ResourceFile_TB.xlsx | 4 ++-- resources/~$ResourceFile_TB.xlsx | 3 +++ src/tlo/methods/tb.py | 32 ++++++++++++-------------------- 3 files changed, 17 insertions(+), 22 deletions(-) create mode 100644 resources/~$ResourceFile_TB.xlsx diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index 00af0b8e33..cae22b02c6 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2259cc326c75c3100b2d2e24a64047e41666b257edd8f1a42dd20a94deffd07c -size 55227 +oid sha256:074559640cb20a6b5654762569ae668a732190680fa814231441793975760447 +size 55246 diff --git a/resources/~$ResourceFile_TB.xlsx b/resources/~$ResourceFile_TB.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/~$ResourceFile_TB.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index db22cb757d..62525b6129 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -218,6 +218,9 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "death_rate_adult_treated": Parameter( Types.REAL, "probability of death in adult aged >=15 years with treated tb" ), + "rr_death_diabetes": Parameter( + Types.REAL, "additional risk of death if person has diabetes (treated/untreated)" + ), # ------------------ progression to active disease ------------------ # "rr_tb_bcg": Parameter( Types.REAL, @@ -550,22 +553,6 @@ def pre_initialise_population(self): *(predictors + conditional_predictors)) # risk of relapse <2 years following treatment - # self.lm["risk_relapse_2yrs"] = LinearModel( - # LinearModelType.MULTIPLICATIVE, - # p["monthly_prob_relapse_tx_complete"], - # Predictor("hv_inf").when(True, p["rr_relapse_hiv"]), - # Predictor("tb_treatment_failure") - # .when(True, (p["monthly_prob_relapse_tx_incomplete"] / p["monthly_prob_relapse_tx_complete"])), - # Predictor().when( - # 'tb_on_ipt & ' - # 'age_years <= 15', - # p["rr_ipt_child"]), - # Predictor().when( - # 'tb_on_ipt & ' - # 'age_years > 15', - # p["rr_ipt_adult"]), - # ) - predictors = [ Predictor("hv_inf").when(True, p["rr_relapse_hiv"]), Predictor("tb_treatment_failure") @@ -605,9 +592,7 @@ def pre_initialise_population(self): ) # probability of death - self.lm["death_rate"] = LinearModel( - LinearModelType.MULTIPLICATIVE, - 1, + predictors = [ Predictor().when( "(tb_on_treatment == True) & " "(age_years <=4)", @@ -633,7 +618,14 @@ def pre_initialise_population(self): "(tb_smear == False)", p["death_rate_smear_neg_untreated"], ), - ) + ] + + conditional_predictors = [ + Predictor("nc_diabetes").when(True, p['rr_death_diabetes']), + ] if "CardioMetabolicDisorders" in self.sim.modules else [] + + self.lm["death_rate"] = LinearModel.multiplicative( + *(predictors + conditional_predictors)) def send_for_screening_general(self, population): From d573211e9724eec5294474b8bac8c6f1e1271a1e Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 5 Mar 2024 11:02:36 +0000 Subject: [PATCH 048/131] add diabetes as risk factor for PLHIV with active TB and on TB treatment --- src/tlo/methods/hiv.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 6a1e7f0245..a8723549ef 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -1934,16 +1934,21 @@ def __init__(self, module, person_id, cause): def apply(self, person_id): df = self.sim.population.props + p = self.sim.modules["Hiv"].parameters # Check person is_alive if not df.at[person_id, "is_alive"]: return if df.at[person_id, 'tb_on_treatment']: - prob = self.module.rng.rand() + + risk_of_death = p["aids_tb_treatment_adjustment"] + + if "CardioMetabolicDisorders" in self.sim.modules and df.at[person_id, "nc_diabetes"]: + risk_of_death *= self.sim.modules["Tb"].parameters["rr_death_diabetes"] # treatment adjustment reduces probability of death - if prob < self.sim.modules["Hiv"].parameters["aids_tb_treatment_adjustment"]: + if self.module.rng.rand() < risk_of_death: self.sim.modules["Demography"].do_death( individual_id=person_id, cause="AIDS_TB", From 3734acb559fbe74fc812c6ae07585693c83c0c4a Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 5 Mar 2024 11:02:53 +0000 Subject: [PATCH 049/131] add diabetes as risk factor for PLHIV with active TB and on TB treatment --- resources/ResourceFile_HIV.xlsx | 4 ++-- resources/ResourceFile_TB.xlsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index 6e73cf07fe..fc700a6185 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d20e316c9b00816da27cc8c104b70cec214787f0d96c80a437f8690806c5e2fb -size 158196 +oid sha256:e8b1907576ec0f1bf8a3d3ced1fca07b2a9fc598e0d2d1046061b7fcacad0096 +size 158370 diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index cae22b02c6..88323bd6cb 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:074559640cb20a6b5654762569ae668a732190680fa814231441793975760447 -size 55246 +oid sha256:da0075f8dedd40849684f3e77ae0fcc2608c81a74c3ce663d318aa0c6f6c5f89 +size 55257 From 7e5f6a68de4ac47a9736859794cafc10c9ffe619 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 5 Mar 2024 11:45:24 +0000 Subject: [PATCH 050/131] set up run to check calibration of deaths and disability --- resources/~$ResourceFile_TB.xlsx | 3 - .../scenarios/long_run_all_diseases.py | 2 +- src/tlo/methods/tb.py | 1338 ++++++++--------- 3 files changed, 670 insertions(+), 673 deletions(-) delete mode 100644 resources/~$ResourceFile_TB.xlsx diff --git a/resources/~$ResourceFile_TB.xlsx b/resources/~$ResourceFile_TB.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/~$ResourceFile_TB.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 diff --git a/src/scripts/calibration_analyses/scenarios/long_run_all_diseases.py b/src/scripts/calibration_analyses/scenarios/long_run_all_diseases.py index 4a354e026c..7aba3499a2 100644 --- a/src/scripts/calibration_analyses/scenarios/long_run_all_diseases.py +++ b/src/scripts/calibration_analyses/scenarios/long_run_all_diseases.py @@ -24,7 +24,7 @@ def __init__(self): self.end_date = Date(2031, 1, 1) # The simulation will stop before reaching this date. self.pop_size = 20_000 self.number_of_draws = 1 - self.runs_per_draw = 10 + self.runs_per_draw = 1 def log_configuration(self): return { diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 62525b6129..552211d53d 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -628,729 +628,729 @@ def pre_initialise_population(self): *(predictors + conditional_predictors)) -def send_for_screening_general(self, population): - df = population.props - p = self.parameters - rng = self.rng - - random_draw = rng.random_sample(size=len(df)) - - # randomly select some individuals for screening and testing - # this may include some newly infected active tb cases (that's fine) - screen_idx = df.index[ - df.is_alive - & ~df.tb_diagnosed - & ~df.tb_on_treatment - & (random_draw < p["rate_testing_general_pop"]) - ] + def send_for_screening_general(self, population): + df = population.props + p = self.parameters + rng = self.rng - for person in screen_idx: - self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Tb_ScreeningAndRefer(person_id=person, module=self), - topen=random_date(self.sim.date, self.sim.date + DateOffset(months=1), self.rng), - tclose=None, - priority=0, - ) + random_draw = rng.random_sample(size=len(df)) + + # randomly select some individuals for screening and testing + # this may include some newly infected active tb cases (that's fine) + screen_idx = df.index[ + df.is_alive + & ~df.tb_diagnosed + & ~df.tb_on_treatment + & (random_draw < p["rate_testing_general_pop"]) + ] + + for person in screen_idx: + self.sim.modules["HealthSystem"].schedule_hsi_event( + HSI_Tb_ScreeningAndRefer(person_id=person, module=self), + topen=random_date(self.sim.date, self.sim.date + DateOffset(months=1), self.rng), + tclose=None, + priority=0, + ) + + + def select_tb_test(self, person_id): + df = self.sim.population.props + p = self.parameters + person = df.loc[person_id] + # xpert tests limited to 60% coverage + # if selected test is xpert, check for availability + # give sputum smear as back-up + # assume sputum smear always available -def select_tb_test(self, person_id): - df = self.sim.population.props - p = self.parameters - person = df.loc[person_id] - - # xpert tests limited to 60% coverage - # if selected test is xpert, check for availability - # give sputum smear as back-up - # assume sputum smear always available - - # previously diagnosed/treated or hiv+ -> xpert - if person["tb_ever_treated"] or person["hv_diagnosed"] or (p["first_line_test"] == 'xpert'): - return "xpert" - else: - return "sputum" - - -def get_consumables_for_dx_and_tx(self): - p = self.parameters - # consumables = self.sim.modules["HealthSystem"].parameters["Consumables"] - hs = self.sim.modules["HealthSystem"] - - # TB Sputum smear test - # assume that if smear-positive, sputum smear test is 100% specific and sensitive - self.item_codes_for_consumables_required['sputum_test'] = \ - hs.get_item_codes_from_package_name("Microscopy Test") - - self.sim.modules['HealthSystem'].dx_manager.register_dx_test( - tb_sputum_test_smear_positive=DxTest( - property='tb_inf', - target_categories=["active"], - sensitivity=p["sens_sputum_smear_positive"], - specificity=p["spec_sputum_smear_positive"], - item_codes=self.item_codes_for_consumables_required['sputum_test'] + # previously diagnosed/treated or hiv+ -> xpert + if person["tb_ever_treated"] or person["hv_diagnosed"] or (p["first_line_test"] == 'xpert'): + return "xpert" + else: + return "sputum" + + + def get_consumables_for_dx_and_tx(self): + p = self.parameters + # consumables = self.sim.modules["HealthSystem"].parameters["Consumables"] + hs = self.sim.modules["HealthSystem"] + + # TB Sputum smear test + # assume that if smear-positive, sputum smear test is 100% specific and sensitive + self.item_codes_for_consumables_required['sputum_test'] = \ + hs.get_item_codes_from_package_name("Microscopy Test") + + self.sim.modules['HealthSystem'].dx_manager.register_dx_test( + tb_sputum_test_smear_positive=DxTest( + property='tb_inf', + target_categories=["active"], + sensitivity=p["sens_sputum_smear_positive"], + specificity=p["spec_sputum_smear_positive"], + item_codes=self.item_codes_for_consumables_required['sputum_test'] + ) ) - ) - self.sim.modules['HealthSystem'].dx_manager.register_dx_test( - tb_sputum_test_smear_negative=DxTest( - property='tb_inf', - target_categories=["active"], - sensitivity=0.0, - specificity=1.0, - item_codes=self.item_codes_for_consumables_required['sputum_test'] + self.sim.modules['HealthSystem'].dx_manager.register_dx_test( + tb_sputum_test_smear_negative=DxTest( + property='tb_inf', + target_categories=["active"], + sensitivity=0.0, + specificity=1.0, + item_codes=self.item_codes_for_consumables_required['sputum_test'] + ) ) - ) - - # TB GeneXpert - self.item_codes_for_consumables_required['xpert_test'] = \ - hs.get_item_codes_from_package_name("Xpert test") - - # sensitivity/specificity set for smear status of cases - self.sim.modules["HealthSystem"].dx_manager.register_dx_test( - tb_xpert_test_smear_positive=DxTest( - property="tb_inf", - target_categories=["active"], - sensitivity=p["sens_xpert_smear_positive"], - specificity=p["spec_xpert_smear_positive"], - item_codes=self.item_codes_for_consumables_required['xpert_test'] + + # TB GeneXpert + self.item_codes_for_consumables_required['xpert_test'] = \ + hs.get_item_codes_from_package_name("Xpert test") + + # sensitivity/specificity set for smear status of cases + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( + tb_xpert_test_smear_positive=DxTest( + property="tb_inf", + target_categories=["active"], + sensitivity=p["sens_xpert_smear_positive"], + specificity=p["spec_xpert_smear_positive"], + item_codes=self.item_codes_for_consumables_required['xpert_test'] + ) ) - ) - self.sim.modules["HealthSystem"].dx_manager.register_dx_test( - tb_xpert_test_smear_negative=DxTest( - property="tb_inf", - target_categories=["active"], - sensitivity=p["sens_xpert_smear_negative"], - specificity=p["spec_xpert_smear_negative"], - item_codes=self.item_codes_for_consumables_required['xpert_test'] + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( + tb_xpert_test_smear_negative=DxTest( + property="tb_inf", + target_categories=["active"], + sensitivity=p["sens_xpert_smear_negative"], + specificity=p["spec_xpert_smear_negative"], + item_codes=self.item_codes_for_consumables_required['xpert_test'] + ) ) - ) - - # TB Chest x-ray - self.item_codes_for_consumables_required['chest_xray'] = { - hs.get_item_code_from_item_name("X-ray"): 1} - - # sensitivity/specificity set for smear status of cases - self.sim.modules["HealthSystem"].dx_manager.register_dx_test( - tb_xray_smear_positive=DxTest( - property="tb_inf", - target_categories=["active"], - sensitivity=p["sens_xray_smear_positive"], - specificity=p["spec_xray_smear_positive"], - item_codes=self.item_codes_for_consumables_required['chest_xray'] + + # TB Chest x-ray + self.item_codes_for_consumables_required['chest_xray'] = { + hs.get_item_code_from_item_name("X-ray"): 1} + + # sensitivity/specificity set for smear status of cases + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( + tb_xray_smear_positive=DxTest( + property="tb_inf", + target_categories=["active"], + sensitivity=p["sens_xray_smear_positive"], + specificity=p["spec_xray_smear_positive"], + item_codes=self.item_codes_for_consumables_required['chest_xray'] + ) ) - ) - self.sim.modules["HealthSystem"].dx_manager.register_dx_test( - tb_xray_smear_negative=DxTest( - property="tb_inf", - target_categories=["active"], - sensitivity=p["sens_xray_smear_negative"], - specificity=p["spec_xray_smear_negative"], - item_codes=self.item_codes_for_consumables_required['chest_xray'] + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( + tb_xray_smear_negative=DxTest( + property="tb_inf", + target_categories=["active"], + sensitivity=p["sens_xray_smear_negative"], + specificity=p["spec_xray_smear_negative"], + item_codes=self.item_codes_for_consumables_required['chest_xray'] + ) ) - ) - - # TB clinical diagnosis - self.sim.modules["HealthSystem"].dx_manager.register_dx_test( - tb_clinical=DxTest( - property="tb_inf", - target_categories=["active"], - sensitivity=p["sens_clinical"], - specificity=p["spec_clinical"], - item_codes=[] + + # TB clinical diagnosis + self.sim.modules["HealthSystem"].dx_manager.register_dx_test( + tb_clinical=DxTest( + property="tb_inf", + target_categories=["active"], + sensitivity=p["sens_clinical"], + specificity=p["spec_clinical"], + item_codes=[] + ) ) - ) - - # 4) -------- Define the treatment options -------- - # adult treatment - primary - self.item_codes_for_consumables_required['tb_tx_adult'] = \ - hs.get_item_code_from_item_name("Cat. I & III Patient Kit A") - - # child treatment - primary - self.item_codes_for_consumables_required['tb_tx_child'] = \ - hs.get_item_code_from_item_name("Cat. I & III Patient Kit B") - - # child treatment - primary, shorter regimen - self.item_codes_for_consumables_required['tb_tx_child_shorter'] = \ - hs.get_item_code_from_item_name("Cat. I & III Patient Kit B") - - # adult treatment - secondary - self.item_codes_for_consumables_required['tb_retx_adult'] = \ - hs.get_item_code_from_item_name("Cat. II Patient Kit A1") - - # child treatment - secondary - self.item_codes_for_consumables_required['tb_retx_child'] = \ - hs.get_item_code_from_item_name("Cat. II Patient Kit A2") - - # mdr treatment - self.item_codes_for_consumables_required['tb_mdrtx'] = { - hs.get_item_code_from_item_name("Treatment: second-line drugs"): 1} - - # ipt - self.item_codes_for_consumables_required['tb_ipt'] = { - hs.get_item_code_from_item_name("Isoniazid/Pyridoxine, tablet 300 mg"): 1} - - -def initialise_population(self, population): - df = population.props - p = self.parameters - - # if HIV is not registered, create a dummy property - if "Hiv" not in self.sim.modules: - population.make_test_property("hv_inf", Types.BOOL) - population.make_test_property("sy_aids_symptoms", Types.INT) - population.make_test_property("hv_art", Types.STRING) - - df["hv_inf"] = False - df["sy_aids_symptoms"] = 0 - df["hv_art"] = "not" - - # Set our property values for the initial population - df["tb_inf"].values[:] = "uninfected" - df["tb_strain"].values[:] = "none" - - df["tb_date_latent"] = pd.NaT - df["tb_scheduled_date_active"] = pd.NaT - df["tb_date_active"] = pd.NaT - df["tb_smear"] = False - - # ------------------ testing status ------------------ # - df["tb_date_tested"] = pd.NaT - df["tb_diagnosed"] = False - df["tb_date_diagnosed"] = pd.NaT - df["tb_diagnosed_mdr"] = False - - # ------------------ treatment status ------------------ # - df["tb_on_treatment"] = False - df["tb_date_treated"] = pd.NaT - df["tb_treatment_regimen"].values[:] = "none" - df["tb_ever_treated"] = False - df["tb_treatment_failure"] = False - - df["tb_on_ipt"] = False - df["tb_date_ipt"] = pd.NaT - - # # ------------------ infection status ------------------ # - # WHO estimates of active TB for 2010 to get infected initial population - # don't need to scale or include treated proportion as no-one on treatment yet - inc_estimates = p["who_incidence_estimates"] - incidence_year = (inc_estimates.loc[ - (inc_estimates.year == self.sim.date.year), "incidence_per_100k" - ].values[0]) / 100_000 - - incidence_year = incidence_year * p["scaling_factor_WHO"] - - self.assign_active_tb( - population, - strain="ds", - incidence=incidence_year) - - self.assign_active_tb( - population, - strain="mdr", - incidence=incidence_year * p['prop_mdr2010']) - - self.send_for_screening_general( - population - ) # send some baseline population for screening - - -def initialise_simulation(self, sim): - """ - * 1) Schedule the regular TB events - * 2) Schedule the scenario change - * 3) Define the DxTests and treatment options - """ - # 1) Regular events - sim.schedule_event(TbActiveEvent(self), sim.date) - sim.schedule_event(TbTreatmentAndRelapseEvents(self), sim.date) - sim.schedule_event(TbSelfCureEvent(self), sim.date) - sim.schedule_event(TbActiveCasePoll(self), sim.date + DateOffset(years=1)) + # 4) -------- Define the treatment options -------- + # adult treatment - primary + self.item_codes_for_consumables_required['tb_tx_adult'] = \ + hs.get_item_code_from_item_name("Cat. I & III Patient Kit A") - # log at the end of the year - sim.schedule_event(TbLoggingEvent(self), sim.date + DateOffset(years=1)) + # child treatment - primary + self.item_codes_for_consumables_required['tb_tx_child'] = \ + hs.get_item_code_from_item_name("Cat. I & III Patient Kit B") - # 2) Scenario change - sim.schedule_event(ScenarioSetupEvent(self), self.parameters["scenario_start_date"]) + # child treatment - primary, shorter regimen + self.item_codes_for_consumables_required['tb_tx_child_shorter'] = \ + hs.get_item_code_from_item_name("Cat. I & III Patient Kit B") - # 3) Define the DxTests and get the consumables required - self.get_consumables_for_dx_and_tx() + # adult treatment - secondary + self.item_codes_for_consumables_required['tb_retx_adult'] = \ + hs.get_item_code_from_item_name("Cat. II Patient Kit A1") - # 4) (Optionally) Schedule the event to check the configuration of all properties - if self.run_with_checks: - sim.schedule_event( - TbCheckPropertiesEvent(self), sim.date + pd.DateOffset(months=1) - ) + # child treatment - secondary + self.item_codes_for_consumables_required['tb_retx_child'] = \ + hs.get_item_code_from_item_name("Cat. II Patient Kit A2") + # mdr treatment + self.item_codes_for_consumables_required['tb_mdrtx'] = { + hs.get_item_code_from_item_name("Treatment: second-line drugs"): 1} -def on_birth(self, mother_id, child_id): - """Initialise properties for a newborn individual - allocate IPT for child if mother diagnosed with TB - """ + # ipt + self.item_codes_for_consumables_required['tb_ipt'] = { + hs.get_item_code_from_item_name("Isoniazid/Pyridoxine, tablet 300 mg"): 1} - df = self.sim.population.props - now = self.sim.date - - df.at[child_id, "tb_inf"] = "uninfected" - df.at[child_id, "tb_strain"] = "none" - - df.at[child_id, "tb_date_latent"] = pd.NaT - df.at[child_id, "tb_scheduled_date_active"] = pd.NaT - df.at[child_id, "tb_date_active"] = pd.NaT - df.at[child_id, "tb_smear"] = False - - # ------------------ testing status ------------------ # - df.at[child_id, "tb_date_tested"] = pd.NaT - - df.at[child_id, "tb_diagnosed"] = False - df.at[child_id, "tb_date_diagnosed"] = pd.NaT - df.at[child_id, "tb_diagnosed_mdr"] = False - - # ------------------ treatment status ------------------ # - df.at[child_id, "tb_on_treatment"] = False - df.at[child_id, "tb_date_treated"] = pd.NaT - df.at[child_id, "tb_treatment_regimen"] = "none" - df.at[child_id, "tb_treatment_failure"] = False - df.at[child_id, "tb_ever_treated"] = False - - df.at[child_id, "tb_on_ipt"] = False - df.at[child_id, "tb_date_ipt"] = pd.NaT - - if "Hiv" not in self.sim.modules: - df.at[child_id, "hv_inf"] = False - df.at[child_id, "sy_aids_symptoms"] = 0 - df.at[child_id, "hv_art"] = "not" - - # Not interested in whether true or direct birth - # give IPT to child of TB diagnosed mother if 2014 or later - if df.at[abs(mother_id), "tb_diagnosed"] and (now.year >= self.parameters["ipt_start_date"]): - event = HSI_Tb_Start_or_Continue_Ipt(self, person_id=child_id) - self.sim.modules["HealthSystem"].schedule_hsi_event( - event, - priority=1, - topen=now, - tclose=now + DateOffset(days=28), - ) + def initialise_population(self, population): + df = population.props + p = self.parameters -def report_daly_values(self): - """ - This must send back a pd.Series or pd.DataFrame that reports on the average daly-weights that have been - experienced by persons in the previous month. Only rows for alive-persons must be returned. - The names of the series of columns is taken to be the label of the cause of this disability. - It will be recorded by the healthburden module as _. - """ - df = self.sim.population.props # shortcut to population properties dataframe - - # to avoid errors when hiv module not running - df_tmp = df.loc[df.is_alive] - health_values = pd.Series(0, index=df_tmp.index) - - # hiv-negative - health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "ds") - & ~df_tmp.hv_inf - ] = self.daly_wts["daly_tb"] - - health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "mdr") - & ~df_tmp.hv_inf - ] = self.daly_wts["daly_tb"] - - # hiv-positive - health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "ds") - & df_tmp.hv_inf - ] = self.daly_wts["daly_tb_hiv"] - - health_values.loc[ - (df_tmp.tb_inf == "active") - & (df_tmp.tb_strain == "mdr") - & df_tmp.hv_inf - ] = self.daly_wts["daly_mdr_tb_hiv"] - - return health_values.loc[df.is_alive] - - -def calculate_untreated_proportion(self, population, strain): - """ - calculate the proportion of active TB cases not on correct treatment - if mdr-tb and on first-line treatment, count case as untreated - they will continue to contribute to transmission - """ - df = population.props + # if HIV is not registered, create a dummy property + if "Hiv" not in self.sim.modules: + population.make_test_property("hv_inf", Types.BOOL) + population.make_test_property("sy_aids_symptoms", Types.INT) + population.make_test_property("hv_art", Types.STRING) - # sum active tb cases - num_active_tb_cases = len(df[(df.tb_inf == "active") & - (df.tb_strain == strain) & - df.is_alive]) + df["hv_inf"] = False + df["sy_aids_symptoms"] = 0 + df["hv_art"] = "not" - # sum treated active tb cases - # if mdr-tb must be on mdr treatment, otherwise consider as untreated case - if strain == "mdr": - num_treated_tb_cases = len(df[(df.tb_inf == "active") & - (df.tb_strain == strain) & - df.tb_on_treatment & - (df.tb_treatment_regimen == "tb_mdrtx") & - df.is_alive]) - else: - num_treated_tb_cases = len(df[(df.tb_inf == "active") & - (df.tb_strain == strain) & - df.tb_on_treatment & - df.is_alive]) + # Set our property values for the initial population + df["tb_inf"].values[:] = "uninfected" + df["tb_strain"].values[:] = "none" - prop_untreated = 1 - (num_treated_tb_cases / num_active_tb_cases) if num_active_tb_cases else 1 + df["tb_date_latent"] = pd.NaT + df["tb_scheduled_date_active"] = pd.NaT + df["tb_date_active"] = pd.NaT + df["tb_smear"] = False - return prop_untreated + # ------------------ testing status ------------------ # + df["tb_date_tested"] = pd.NaT + df["tb_diagnosed"] = False + df["tb_date_diagnosed"] = pd.NaT + df["tb_diagnosed_mdr"] = False + # ------------------ treatment status ------------------ # + df["tb_on_treatment"] = False + df["tb_date_treated"] = pd.NaT + df["tb_treatment_regimen"].values[:] = "none" + df["tb_ever_treated"] = False + df["tb_treatment_failure"] = False + + df["tb_on_ipt"] = False + df["tb_date_ipt"] = pd.NaT + + # # ------------------ infection status ------------------ # + # WHO estimates of active TB for 2010 to get infected initial population + # don't need to scale or include treated proportion as no-one on treatment yet + inc_estimates = p["who_incidence_estimates"] + incidence_year = (inc_estimates.loc[ + (inc_estimates.year == self.sim.date.year), "incidence_per_100k" + ].values[0]) / 100_000 -def assign_active_tb(self, population, strain, incidence): - """ - select individuals to be infected - assign scheduled date of active tb onset - update properties as needed - symptoms and smear status are assigned in the TbActiveEvent - """ + incidence_year = incidence_year * p["scaling_factor_WHO"] - df = population.props - rng = self.rng - now = self.sim.date - - # identify eligible people, not currently with active tb infection - eligible = df.loc[ - df.is_alive - & (df.tb_inf != "active") - ].index - - # weight risk by individual characteristics - # Compute chance that each susceptible person becomes infected: - rr_of_infection = self.lm["active_tb"].predict( - df.loc[eligible] - ) - - # probability of infection - p_infection = (rr_of_infection * incidence) - - # New infections: - will_be_infected = ( - self.rng.random_sample(len(p_infection)) < p_infection - ) - idx_new_infection = will_be_infected[will_be_infected].index - - df.loc[idx_new_infection, "tb_strain"] = strain - - # schedule onset of active tb, time now up to 1 year - for person_id in idx_new_infection: - date_progression = now + pd.DateOffset( - days=rng.randint(0, 365) - ) + self.assign_active_tb( + population, + strain="ds", + incidence=incidence_year) - # set date of active tb - properties will be updated at TbActiveEvent poll daily - df.at[person_id, "tb_scheduled_date_active"] = date_progression + self.assign_active_tb( + population, + strain="mdr", + incidence=incidence_year * p['prop_mdr2010']) + self.send_for_screening_general( + population + ) # send some baseline population for screening -def consider_ipt_for_those_initiating_art(self, person_id): - """ - this is called by HIV when person is initiating ART - checks whether person is eligible for IPT - """ - df = self.sim.population.props - if df.loc[person_id, "tb_diagnosed"] or df.loc[person_id, "tb_diagnosed_mdr"]: - pass + def initialise_simulation(self, sim): + """ + * 1) Schedule the regular TB events + * 2) Schedule the scenario change + * 3) Define the DxTests and treatment options + """ - high_risk_districts = self.parameters["tb_high_risk_distr"] - district = df.at[person_id, "district_of_residence"] - eligible = df.at[person_id, "tb_inf"] != "active" - - # select coverage rate by year: - now = self.sim.date - year = now.year if now.year <= 2050 else 2050 - - ipt = self.parameters["ipt_coverage"] - ipt_year = ipt.loc[ipt.year == year] - ipt_coverage_plhiv = ipt_year.coverage_plhiv - - if ( - (district in high_risk_districts.district_name.values) - & eligible - & (self.rng.rand() < ipt_coverage_plhiv.values) - ): - # Schedule the TB treatment event: - self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Tb_Start_or_Continue_Ipt(self, person_id=person_id), - priority=1, - topen=self.sim.date, - tclose=None, - ) + # 1) Regular events + sim.schedule_event(TbActiveEvent(self), sim.date) + sim.schedule_event(TbTreatmentAndRelapseEvents(self), sim.date) + sim.schedule_event(TbSelfCureEvent(self), sim.date) + sim.schedule_event(TbActiveCasePoll(self), sim.date + DateOffset(years=1)) + # log at the end of the year + sim.schedule_event(TbLoggingEvent(self), sim.date + DateOffset(years=1)) -def relapse_event(self, population): - """The Tb Regular Relapse Event - runs every month to randomly sample amongst those previously infected with active tb - * Schedules persons who have previously been infected to relapse with a set probability - * Sets a scheduled_date_active which is picked up by TbActiveEvent - """ + # 2) Scenario change + sim.schedule_event(ScenarioSetupEvent(self), self.parameters["scenario_start_date"]) - df = population.props - rng = self.rng - now = self.sim.date - - # need a monthly relapse for every person in df - # should return risk=0 for everyone not eligible for relapse - - # risk of relapse if <2 years post treatment start, includes risk if HIV+ - risk_of_relapse_early = self.lm["risk_relapse_2yrs"].predict( - df.loc[df.is_alive - & df.tb_ever_treated - & (df.tb_inf == "latent") - & (now < (df.tb_date_treated + pd.DateOffset(years=2)))] - ) - - will_relapse = ( - rng.random_sample(len(risk_of_relapse_early)) < risk_of_relapse_early - ) - idx_will_relapse_early = will_relapse[will_relapse].index - - # risk of relapse if >=2 years post treatment start, includes risk if HIV+ - risk_of_relapse_later = self.lm["risk_relapse_late"].predict( - df.loc[df.is_alive - & df.tb_ever_treated - & (df.tb_inf == "latent") - & (now >= (df.tb_date_treated + pd.DateOffset(years=2)))] - ) - - will_relapse_later = ( - rng.random_sample(len(risk_of_relapse_later)) < risk_of_relapse_later - ) - idx_will_relapse_late2 = will_relapse_later[will_relapse_later].index - - # join both indices - idx_will_relapse = idx_will_relapse_early.union( - idx_will_relapse_late2 - ).drop_duplicates() - - # set date of scheduled active tb - # properties will be updated at TbActiveEvent every month - df.loc[idx_will_relapse, "tb_scheduled_date_active"] = now - - -def end_treatment(self, population): - """ - * check for those eligible to finish treatment - * sample for treatment failure and refer for follow-up screening/testing - * if treatment has finished, change individual properties - """ - - df = population.props - rng = self.rng - now = self.sim.date - p = self.parameters - - # check across population on tb treatment and end treatment if required - # if current date is after (treatment start date + treatment length) -> end tx - - # ---------------------- treatment end: first case ds-tb (6 months) ---------------------- # - # end treatment for new tb (ds) cases - end_ds_tx_idx = df.loc[ - df.is_alive - & df.tb_on_treatment - & ((df.tb_treatment_regimen == "tb_tx_adult") | (df.tb_treatment_regimen == "tb_tx_child")) - & ( - now - > (df.tb_date_treated + pd.DateOffset(months=p["ds_treatment_length"])) - ) - ].index - - # ---------------------- treatment end: retreatment ds-tb (7 months) ---------------------- # - # end treatment for retreatment cases - end_ds_retx_idx = df.loc[ - df.is_alive - & df.tb_on_treatment - & ((df.tb_treatment_regimen == "tb_retx_adult") | (df.tb_treatment_regimen == "tb_retx_child")) - & ( - now - > ( - df.tb_date_treated - + pd.DateOffset(months=p["ds_retreatment_length"]) + # 3) Define the DxTests and get the consumables required + self.get_consumables_for_dx_and_tx() + + # 4) (Optionally) Schedule the event to check the configuration of all properties + if self.run_with_checks: + sim.schedule_event( + TbCheckPropertiesEvent(self), sim.date + pd.DateOffset(months=1) ) + + + def on_birth(self, mother_id, child_id): + """Initialise properties for a newborn individual + allocate IPT for child if mother diagnosed with TB + """ + + df = self.sim.population.props + now = self.sim.date + + df.at[child_id, "tb_inf"] = "uninfected" + df.at[child_id, "tb_strain"] = "none" + + df.at[child_id, "tb_date_latent"] = pd.NaT + df.at[child_id, "tb_scheduled_date_active"] = pd.NaT + df.at[child_id, "tb_date_active"] = pd.NaT + df.at[child_id, "tb_smear"] = False + + # ------------------ testing status ------------------ # + df.at[child_id, "tb_date_tested"] = pd.NaT + + df.at[child_id, "tb_diagnosed"] = False + df.at[child_id, "tb_date_diagnosed"] = pd.NaT + df.at[child_id, "tb_diagnosed_mdr"] = False + + # ------------------ treatment status ------------------ # + df.at[child_id, "tb_on_treatment"] = False + df.at[child_id, "tb_date_treated"] = pd.NaT + df.at[child_id, "tb_treatment_regimen"] = "none" + df.at[child_id, "tb_treatment_failure"] = False + df.at[child_id, "tb_ever_treated"] = False + + df.at[child_id, "tb_on_ipt"] = False + df.at[child_id, "tb_date_ipt"] = pd.NaT + + if "Hiv" not in self.sim.modules: + df.at[child_id, "hv_inf"] = False + df.at[child_id, "sy_aids_symptoms"] = 0 + df.at[child_id, "hv_art"] = "not" + + # Not interested in whether true or direct birth + # give IPT to child of TB diagnosed mother if 2014 or later + if df.at[abs(mother_id), "tb_diagnosed"] and (now.year >= self.parameters["ipt_start_date"]): + event = HSI_Tb_Start_or_Continue_Ipt(self, person_id=child_id) + self.sim.modules["HealthSystem"].schedule_hsi_event( + event, + priority=1, + topen=now, + tclose=now + DateOffset(days=28), + ) + + + def report_daly_values(self): + """ + This must send back a pd.Series or pd.DataFrame that reports on the average daly-weights that have been + experienced by persons in the previous month. Only rows for alive-persons must be returned. + The names of the series of columns is taken to be the label of the cause of this disability. + It will be recorded by the healthburden module as _. + """ + df = self.sim.population.props # shortcut to population properties dataframe + + # to avoid errors when hiv module not running + df_tmp = df.loc[df.is_alive] + health_values = pd.Series(0, index=df_tmp.index) + + # hiv-negative + health_values.loc[ + (df_tmp.tb_inf == "active") + & (df_tmp.tb_strain == "ds") + & ~df_tmp.hv_inf + ] = self.daly_wts["daly_tb"] + + health_values.loc[ + (df_tmp.tb_inf == "active") + & (df_tmp.tb_strain == "mdr") + & ~df_tmp.hv_inf + ] = self.daly_wts["daly_tb"] + + # hiv-positive + health_values.loc[ + (df_tmp.tb_inf == "active") + & (df_tmp.tb_strain == "ds") + & df_tmp.hv_inf + ] = self.daly_wts["daly_tb_hiv"] + + health_values.loc[ + (df_tmp.tb_inf == "active") + & (df_tmp.tb_strain == "mdr") + & df_tmp.hv_inf + ] = self.daly_wts["daly_mdr_tb_hiv"] + + return health_values.loc[df.is_alive] + + + def calculate_untreated_proportion(self, population, strain): + """ + calculate the proportion of active TB cases not on correct treatment + if mdr-tb and on first-line treatment, count case as untreated + they will continue to contribute to transmission + """ + df = population.props + + # sum active tb cases + num_active_tb_cases = len(df[(df.tb_inf == "active") & + (df.tb_strain == strain) & + df.is_alive]) + + # sum treated active tb cases + # if mdr-tb must be on mdr treatment, otherwise consider as untreated case + if strain == "mdr": + num_treated_tb_cases = len(df[(df.tb_inf == "active") & + (df.tb_strain == strain) & + df.tb_on_treatment & + (df.tb_treatment_regimen == "tb_mdrtx") & + df.is_alive]) + else: + num_treated_tb_cases = len(df[(df.tb_inf == "active") & + (df.tb_strain == strain) & + df.tb_on_treatment & + df.is_alive]) + + prop_untreated = 1 - (num_treated_tb_cases / num_active_tb_cases) if num_active_tb_cases else 1 + + return prop_untreated + + + def assign_active_tb(self, population, strain, incidence): + """ + select individuals to be infected + assign scheduled date of active tb onset + update properties as needed + symptoms and smear status are assigned in the TbActiveEvent + """ + + df = population.props + rng = self.rng + now = self.sim.date + + # identify eligible people, not currently with active tb infection + eligible = df.loc[ + df.is_alive + & (df.tb_inf != "active") + ].index + + # weight risk by individual characteristics + # Compute chance that each susceptible person becomes infected: + rr_of_infection = self.lm["active_tb"].predict( + df.loc[eligible] ) - ].index - - # ---------------------- treatment end: mdr-tb (24 months) ---------------------- # - # end treatment for mdr-tb cases - end_mdr_tx_idx = df.loc[ - df.is_alive - & df.tb_on_treatment - & (df.tb_treatment_regimen == "tb_mdrtx") - & ( - now - > (df.tb_date_treated + pd.DateOffset(months=p["mdr_treatment_length"])) - ) - ].index - - # ---------------------- treatment end: shorter paediatric regimen ---------------------- # - # end treatment for paediatric cases on 4 month regimen - end_tx_shorter_idx = df.loc[ - df.is_alive - & df.tb_on_treatment - & (df.tb_treatment_regimen == "tb_tx_child_shorter") - & ( - now - > (df.tb_date_treated + pd.DateOffset(months=p["child_shorter_treatment_length"])) - ) - ].index - - # join indices - end_tx_idx = end_ds_tx_idx.union(end_ds_retx_idx) - end_tx_idx = end_tx_idx.union(end_mdr_tx_idx) - end_tx_idx = end_tx_idx.union(end_tx_shorter_idx) - - # ---------------------- treatment failure ---------------------- # - # sample some to have treatment failure - # assume all retreatment cases will cure - random_var = rng.random_sample(size=len(df)) - - # children aged 0-4 ds-tb - ds_tx_failure0_4_idx = df.loc[ - (df.index.isin(end_ds_tx_idx)) - & (df.age_years < 5) - & (random_var < (1 - p["prob_tx_success_0_4"])) - ].index - - # children aged 5-14 ds-tb - ds_tx_failure5_14_idx = df.loc[ - (df.index.isin(end_ds_tx_idx)) - & (df.age_years.between(5, 14)) - & (random_var < (1 - p["prob_tx_success_5_14"])) - ].index - - # children aged <16 and on shorter regimen - ds_tx_failure_shorter_idx = df.loc[ - (df.index.isin(end_tx_shorter_idx)) - & (df.age_years < 16) - & (random_var < (1 - p["prob_tx_success_shorter"])) - ].index - - # adults ds-tb - ds_tx_failure_adult_idx = df.loc[ - (df.index.isin(end_ds_tx_idx)) - & (df.age_years >= 15) - & (random_var < (1 - p["prob_tx_success_ds"])) - ].index - - # all mdr cases on ds tx will fail - failure_in_mdr_with_ds_tx_idx = df.loc[ - (df.index.isin(end_ds_tx_idx)) - & (df.tb_strain == "mdr") - ].index - - # some mdr cases on mdr treatment will fail - failure_due_to_mdr_idx = df.loc[ - (df.index.isin(end_mdr_tx_idx)) - & (df.tb_strain == "mdr") - & (random_var < (1 - p["prob_tx_success_mdr"])) - - ].index - - # join indices of failing cases together - tx_failure = reduce( - pd.Index.union, - ( - ds_tx_failure0_4_idx, - ds_tx_failure5_14_idx, - ds_tx_failure_shorter_idx, - ds_tx_failure_adult_idx, - failure_in_mdr_with_ds_tx_idx, - failure_due_to_mdr_idx, + + # probability of infection + p_infection = (rr_of_infection * incidence) + + # New infections: + will_be_infected = ( + self.rng.random_sample(len(p_infection)) < p_infection ) - ) + idx_new_infection = will_be_infected[will_be_infected].index + + df.loc[idx_new_infection, "tb_strain"] = strain + + # schedule onset of active tb, time now up to 1 year + for person_id in idx_new_infection: + date_progression = now + pd.DateOffset( + days=rng.randint(0, 365) + ) + + # set date of active tb - properties will be updated at TbActiveEvent poll daily + df.at[person_id, "tb_scheduled_date_active"] = date_progression + + + def consider_ipt_for_those_initiating_art(self, person_id): + """ + this is called by HIV when person is initiating ART + checks whether person is eligible for IPT + """ + df = self.sim.population.props + + if df.loc[person_id, "tb_diagnosed"] or df.loc[person_id, "tb_diagnosed_mdr"]: + pass + + high_risk_districts = self.parameters["tb_high_risk_distr"] + district = df.at[person_id, "district_of_residence"] + eligible = df.at[person_id, "tb_inf"] != "active" + + # select coverage rate by year: + now = self.sim.date + year = now.year if now.year <= 2050 else 2050 - if not tx_failure.empty: - df.loc[tx_failure, "tb_treatment_failure"] = True - df.loc[ - tx_failure, "tb_ever_treated" - ] = True # ensure classed as retreatment case + ipt = self.parameters["ipt_coverage"] + ipt_year = ipt.loc[ipt.year == year] + ipt_coverage_plhiv = ipt_year.coverage_plhiv - for person in tx_failure: + if ( + (district in high_risk_districts.district_name.values) + & eligible + & (self.rng.rand() < ipt_coverage_plhiv.values) + ): + # Schedule the TB treatment event: self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Tb_ScreeningAndRefer(person_id=person, module=self), + HSI_Tb_Start_or_Continue_Ipt(self, person_id=person_id), + priority=1, topen=self.sim.date, tclose=None, - priority=0, ) - # remove any treatment failure indices from the treatment end indices - cure_idx = end_tx_idx.difference(tx_failure) - - # change individual properties for all to off treatment - df.loc[end_tx_idx, "tb_diagnosed"] = False - df.loc[end_tx_idx, "tb_on_treatment"] = False - df.loc[end_tx_idx, "tb_treated_mdr"] = False - # this will indicate that this person has had one complete course of tb treatment - # subsequent infections will be classified as retreatment - df.loc[end_tx_idx, "tb_ever_treated"] = True - - # if cured, move infection status back to latent - # leave tb_strain property set in case of relapse - df.loc[cure_idx, "tb_inf"] = "latent" - df.loc[cure_idx, "tb_date_latent"] = now - df.loc[cure_idx, "tb_smear"] = False - - # this will clear all tb symptoms - self.sim.modules["SymptomManager"].clear_symptoms( - person_id=cure_idx, disease_module=self - ) - - # if HIV+ and on ART (virally suppressed), remove AIDS symptoms if cured of TB - hiv_tb_infected = cure_idx.intersection( - df.loc[ + + def relapse_event(self, population): + """The Tb Regular Relapse Event + runs every month to randomly sample amongst those previously infected with active tb + * Schedules persons who have previously been infected to relapse with a set probability + * Sets a scheduled_date_active which is picked up by TbActiveEvent + """ + + df = population.props + rng = self.rng + now = self.sim.date + + # need a monthly relapse for every person in df + # should return risk=0 for everyone not eligible for relapse + + # risk of relapse if <2 years post treatment start, includes risk if HIV+ + risk_of_relapse_early = self.lm["risk_relapse_2yrs"].predict( + df.loc[df.is_alive + & df.tb_ever_treated + & (df.tb_inf == "latent") + & (now < (df.tb_date_treated + pd.DateOffset(years=2)))] + ) + + will_relapse = ( + rng.random_sample(len(risk_of_relapse_early)) < risk_of_relapse_early + ) + idx_will_relapse_early = will_relapse[will_relapse].index + + # risk of relapse if >=2 years post treatment start, includes risk if HIV+ + risk_of_relapse_later = self.lm["risk_relapse_late"].predict( + df.loc[df.is_alive + & df.tb_ever_treated + & (df.tb_inf == "latent") + & (now >= (df.tb_date_treated + pd.DateOffset(years=2)))] + ) + + will_relapse_later = ( + rng.random_sample(len(risk_of_relapse_later)) < risk_of_relapse_later + ) + idx_will_relapse_late2 = will_relapse_later[will_relapse_later].index + + # join both indices + idx_will_relapse = idx_will_relapse_early.union( + idx_will_relapse_late2 + ).drop_duplicates() + + # set date of scheduled active tb + # properties will be updated at TbActiveEvent every month + df.loc[idx_will_relapse, "tb_scheduled_date_active"] = now + + + def end_treatment(self, population): + """ + * check for those eligible to finish treatment + * sample for treatment failure and refer for follow-up screening/testing + * if treatment has finished, change individual properties + """ + + df = population.props + rng = self.rng + now = self.sim.date + p = self.parameters + + # check across population on tb treatment and end treatment if required + # if current date is after (treatment start date + treatment length) -> end tx + + # ---------------------- treatment end: first case ds-tb (6 months) ---------------------- # + # end treatment for new tb (ds) cases + end_ds_tx_idx = df.loc[ df.is_alive - & df.hv_inf - & (df.hv_art == "on_VL_suppressed") + & df.tb_on_treatment + & ((df.tb_treatment_regimen == "tb_tx_adult") | (df.tb_treatment_regimen == "tb_tx_child")) + & ( + now + > (df.tb_date_treated + pd.DateOffset(months=p["ds_treatment_length"])) + ) ].index - ) - self.sim.modules["SymptomManager"].clear_symptoms( - person_id=hiv_tb_infected, disease_module=self.sim.modules["Hiv"] - ) + # ---------------------- treatment end: retreatment ds-tb (7 months) ---------------------- # + # end treatment for retreatment cases + end_ds_retx_idx = df.loc[ + df.is_alive + & df.tb_on_treatment + & ((df.tb_treatment_regimen == "tb_retx_adult") | (df.tb_treatment_regimen == "tb_retx_child")) + & ( + now + > ( + df.tb_date_treated + + pd.DateOffset(months=p["ds_retreatment_length"]) + ) + ) + ].index + + # ---------------------- treatment end: mdr-tb (24 months) ---------------------- # + # end treatment for mdr-tb cases + end_mdr_tx_idx = df.loc[ + df.is_alive + & df.tb_on_treatment + & (df.tb_treatment_regimen == "tb_mdrtx") + & ( + now + > (df.tb_date_treated + pd.DateOffset(months=p["mdr_treatment_length"])) + ) + ].index + + # ---------------------- treatment end: shorter paediatric regimen ---------------------- # + # end treatment for paediatric cases on 4 month regimen + end_tx_shorter_idx = df.loc[ + df.is_alive + & df.tb_on_treatment + & (df.tb_treatment_regimen == "tb_tx_child_shorter") + & ( + now + > (df.tb_date_treated + pd.DateOffset(months=p["child_shorter_treatment_length"])) + ) + ].index + + # join indices + end_tx_idx = end_ds_tx_idx.union(end_ds_retx_idx) + end_tx_idx = end_tx_idx.union(end_mdr_tx_idx) + end_tx_idx = end_tx_idx.union(end_tx_shorter_idx) + + # ---------------------- treatment failure ---------------------- # + # sample some to have treatment failure + # assume all retreatment cases will cure + random_var = rng.random_sample(size=len(df)) + + # children aged 0-4 ds-tb + ds_tx_failure0_4_idx = df.loc[ + (df.index.isin(end_ds_tx_idx)) + & (df.age_years < 5) + & (random_var < (1 - p["prob_tx_success_0_4"])) + ].index + + # children aged 5-14 ds-tb + ds_tx_failure5_14_idx = df.loc[ + (df.index.isin(end_ds_tx_idx)) + & (df.age_years.between(5, 14)) + & (random_var < (1 - p["prob_tx_success_5_14"])) + ].index + # children aged <16 and on shorter regimen + ds_tx_failure_shorter_idx = df.loc[ + (df.index.isin(end_tx_shorter_idx)) + & (df.age_years < 16) + & (random_var < (1 - p["prob_tx_success_shorter"])) + ].index -def check_config_of_properties(self): - """check that the properties are currently configured correctly""" - df = self.sim.population.props - df_alive = df.loc[df.is_alive] + # adults ds-tb + ds_tx_failure_adult_idx = df.loc[ + (df.index.isin(end_ds_tx_idx)) + & (df.age_years >= 15) + & (random_var < (1 - p["prob_tx_success_ds"])) + ].index + + # all mdr cases on ds tx will fail + failure_in_mdr_with_ds_tx_idx = df.loc[ + (df.index.isin(end_ds_tx_idx)) + & (df.tb_strain == "mdr") + ].index + + # some mdr cases on mdr treatment will fail + failure_due_to_mdr_idx = df.loc[ + (df.index.isin(end_mdr_tx_idx)) + & (df.tb_strain == "mdr") + & (random_var < (1 - p["prob_tx_success_mdr"])) + + ].index + + # join indices of failing cases together + tx_failure = reduce( + pd.Index.union, + ( + ds_tx_failure0_4_idx, + ds_tx_failure5_14_idx, + ds_tx_failure_shorter_idx, + ds_tx_failure_adult_idx, + failure_in_mdr_with_ds_tx_idx, + failure_due_to_mdr_idx, + ) + ) - # basic check types of columns and dtypes - orig = self.sim.population.new_row - assert (df.dtypes == orig.dtypes).all() + if not tx_failure.empty: + df.loc[tx_failure, "tb_treatment_failure"] = True + df.loc[ + tx_failure, "tb_ever_treated" + ] = True # ensure classed as retreatment case - def is_subset(col_for_set, col_for_subset): - # Confirms that the series of col_for_subset is true only for a subset of the series for col_for_set - return set(col_for_subset.loc[col_for_subset].index).issubset( - col_for_set.loc[col_for_set].index + for person in tx_failure: + self.sim.modules["HealthSystem"].schedule_hsi_event( + HSI_Tb_ScreeningAndRefer(person_id=person, module=self), + topen=self.sim.date, + tclose=None, + priority=0, + ) + + # remove any treatment failure indices from the treatment end indices + cure_idx = end_tx_idx.difference(tx_failure) + + # change individual properties for all to off treatment + df.loc[end_tx_idx, "tb_diagnosed"] = False + df.loc[end_tx_idx, "tb_on_treatment"] = False + df.loc[end_tx_idx, "tb_treated_mdr"] = False + # this will indicate that this person has had one complete course of tb treatment + # subsequent infections will be classified as retreatment + df.loc[end_tx_idx, "tb_ever_treated"] = True + + # if cured, move infection status back to latent + # leave tb_strain property set in case of relapse + df.loc[cure_idx, "tb_inf"] = "latent" + df.loc[cure_idx, "tb_date_latent"] = now + df.loc[cure_idx, "tb_smear"] = False + + # this will clear all tb symptoms + self.sim.modules["SymptomManager"].clear_symptoms( + person_id=cure_idx, disease_module=self + ) + + # if HIV+ and on ART (virally suppressed), remove AIDS symptoms if cured of TB + hiv_tb_infected = cure_idx.intersection( + df.loc[ + df.is_alive + & df.hv_inf + & (df.hv_art == "on_VL_suppressed") + ].index + ) + + self.sim.modules["SymptomManager"].clear_symptoms( + person_id=hiv_tb_infected, disease_module=self.sim.modules["Hiv"] + ) + + + def check_config_of_properties(self): + """check that the properties are currently configured correctly""" + df = self.sim.population.props + df_alive = df.loc[df.is_alive] + + # basic check types of columns and dtypes + orig = self.sim.population.new_row + assert (df.dtypes == orig.dtypes).all() + + def is_subset(col_for_set, col_for_subset): + # Confirms that the series of col_for_subset is true only for a subset of the series for col_for_set + return set(col_for_subset.loc[col_for_subset].index).issubset( + col_for_set.loc[col_for_set].index + ) + + # Check that core properties of current status are never None/NaN/NaT + assert not df_alive.tb_inf.isna().any() + assert not df_alive.tb_strain.isna().any() + assert not df_alive.tb_smear.isna().any() + assert not df_alive.tb_on_treatment.isna().any() + assert not df_alive.tb_treatment_regimen.isna().any() + assert not df_alive.tb_ever_treated.isna().any() + assert not df_alive.tb_on_ipt.isna().any() + + # Check that the core TB properties are 'nested' in the way expected. + assert is_subset( + col_for_set=(df_alive.tb_inf != "uninfected"), col_for_subset=df_alive.tb_diagnosed + ) + assert is_subset( + col_for_set=df_alive.tb_diagnosed, col_for_subset=df_alive.tb_on_treatment ) - # Check that core properties of current status are never None/NaN/NaT - assert not df_alive.tb_inf.isna().any() - assert not df_alive.tb_strain.isna().any() - assert not df_alive.tb_smear.isna().any() - assert not df_alive.tb_on_treatment.isna().any() - assert not df_alive.tb_treatment_regimen.isna().any() - assert not df_alive.tb_ever_treated.isna().any() - assert not df_alive.tb_on_ipt.isna().any() - - # Check that the core TB properties are 'nested' in the way expected. - assert is_subset( - col_for_set=(df_alive.tb_inf != "uninfected"), col_for_subset=df_alive.tb_diagnosed - ) - assert is_subset( - col_for_set=df_alive.tb_diagnosed, col_for_subset=df_alive.tb_on_treatment - ) - - # Check that if person is infected, the dates of active TB is NOT missing - assert not df.loc[(df.tb_inf == "active"), "tb_date_active"].isna().all() + # Check that if person is infected, the dates of active TB is NOT missing + assert not df.loc[(df.tb_inf == "active"), "tb_date_active"].isna().all() # # --------------------------------------------------------------------------- From b7862562c50be94bb483f2ed1c50c4b211fbf309 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 6 Mar 2024 12:12:58 +0000 Subject: [PATCH 051/131] add predictor high-intensity S. haematobium infection to risk of bladder cancer in initial population --- src/tlo/methods/bladder_cancer.py | 16 +++++++++++----- src/tlo/methods/hiv.py | 6 ++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/tlo/methods/bladder_cancer.py b/src/tlo/methods/bladder_cancer.py index d305135fbd..ad865c5062 100644 --- a/src/tlo/methods/bladder_cancer.py +++ b/src/tlo/methods/bladder_cancer.py @@ -234,17 +234,23 @@ def initialise_population(self, population): # check parameters are sensible: probability of having any cancer stage cannot exceed 1.0 assert sum(p['init_prop_bladder_cancer_stage']) <= 1.0 - lm_init_bc_status_any_stage = LinearModel( - LinearModelType.MULTIPLICATIVE, - sum(p['init_prop_bladder_cancer_stage']), + predictors = [ Predictor('li_tob').when(True, p['rp_bladder_cancer_tobacco']), - # todo: add line when schisto is merged - # Predictor('sh_infection_status').when('High-infection', p['rp_bladder_cancer_schisto_h']), Predictor('age_years', conditions_are_mutually_exclusive=True) .when('.between(30,49)', p['rp_bladder_cancer_age3049']) .when('.between(50,69)', p['rp_bladder_cancer_age5069']) .when('.between(70,120)', p['rp_bladder_cancer_agege70']) .when('.between(0,14)', 0.0) + ] + + conditional_predictors = [ + Predictor('ss_sh_infection_status').when('High-infection', p['rp_bladder_cancer_schisto_h']), + ] if "Schisto" in self.sim.modules else [] + + lm_init_bc_status_any_stage = LinearModel( + LinearModelType.MULTIPLICATIVE, + sum(p['init_prop_bladder_cancer_stage']), + *(predictors + conditional_predictors) ) bc_status_any_stage = lm_init_bc_status_any_stage.predict(df.loc[df.is_alive], self.rng) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index a8723549ef..0ba3af9be7 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -465,6 +465,12 @@ def pre_initialise_population(self): Predictor("hv_behaviour_change").when(True, p["rr_behaviour_change"]), ) + conditional_predictors = [ + Predictor('ss_sh_infection_status').when('High-infection', p['rr_schisto']), + ] if "Schisto" in self.sim.modules else [] + + + # LinearModels to give the shape and scale for the Weibull distribution describing time from infection to death self.lm["scale_parameter_for_infection_to_death"] = LinearModel.multiplicative( Predictor( From 956b09a5bfbf73b024a0098427dfc527344f8c3a Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 6 Mar 2024 12:16:13 +0000 Subject: [PATCH 052/131] add predictor high-intensity S. haematobium infection to risk of HIV acquisition --- resources/ResourceFile_HIV.xlsx | 4 ++-- src/tlo/methods/hiv.py | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index fc700a6185..db28d91a82 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e8b1907576ec0f1bf8a3d3ced1fca07b2a9fc598e0d2d1046061b7fcacad0096 -size 158370 +oid sha256:174c5f9ae422cf8daca8b1cc2838965bb4c9907c871388e7d034f2057e4c93a1 +size 158432 diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 0ba3af9be7..fead390b1d 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -187,6 +187,9 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "rr_edlevel_higher": Parameter( Types.REAL, "Relative risk of HIV with higher education" ), + "rr_schisto": Parameter( + Types.REAL, "Relative risk of HIV with high intensity S. haematobium infection" + ), # Natural history - transmission - relative risk of HIV acquisition (interventions) "rr_behaviour_change": Parameter( Types.REAL, "Relative risk of HIV with behaviour modification" @@ -447,7 +450,7 @@ def pre_initialise_population(self): # ---- LINEAR MODELS ----- # LinearModel for the relative risk of becoming infected during the simulation # N.B. age assumed not to have an effect on incidence - self.lm["rr_of_infection"] = LinearModel.multiplicative( + predictors = [ Predictor("age_years").when("<15", 0.0).when("<49", 1.0).otherwise(0.0), Predictor("sex").when("F", p["rr_sex_f"]), Predictor("li_is_circ").when(True, p["rr_circumcision"]), @@ -462,14 +465,15 @@ def pre_initialise_population(self): Predictor("li_ed_lev", conditions_are_mutually_exclusive=True) .when(2, p["rr_edlevel_primary"]) .when(3, p["rr_edlevel_secondary"]), - Predictor("hv_behaviour_change").when(True, p["rr_behaviour_change"]), - ) + Predictor("hv_behaviour_change").when(True, p["rr_behaviour_change"]) + ] conditional_predictors = [ Predictor('ss_sh_infection_status').when('High-infection', p['rr_schisto']), ] if "Schisto" in self.sim.modules else [] - + self.lm["rr_of_infection"] = LinearModel.multiplicative( + *(predictors + conditional_predictors)) # LinearModels to give the shape and scale for the Weibull distribution describing time from infection to death self.lm["scale_parameter_for_infection_to_death"] = LinearModel.multiplicative( From e375927a4733cb76a28452e19fe7d45efd832767 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Thu, 7 Mar 2024 12:22:04 +0000 Subject: [PATCH 053/131] fix indenting in HSI_Hiv_StartOrContinueTreatment --- src/tlo/methods/hiv.py | 78 +++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index fead390b1d..5d278181e8 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -2501,57 +2501,57 @@ def apply(self, person_id, squeeze_factor): } logger.info(key='hiv_arv_NA', data=person_details_for_tx) - # As drugs were not available, the person will default to being off ART (...if they were on ART at the - # beginning of the HSI.) - # NB. If the person was not on ART at the beginning of the HSI, then there is no need to stop them (which - # causes a new AIDSOnsetEvent to be scheduled.) - self.counter_for_drugs_not_available += 1 # The current appointment is included in the count. + # As drugs were not available, the person will default to being off ART (...if they were on ART at the + # beginning of the HSI.) + # NB. If the person was not on ART at the beginning of the HSI, then there is no need to stop them (which + # causes a new AIDSOnsetEvent to be scheduled.) + self.counter_for_drugs_not_available += 1 # The current appointment is included in the count. - if art_status_at_beginning_of_hsi != "not": - self.module.stops_treatment(person_id) + if art_status_at_beginning_of_hsi != "not": + self.module.stops_treatment(person_id) - p = self.module.parameters["probability_of_seeking_further_art_appointment_if_drug_not_available"] + p = self.module.parameters["probability_of_seeking_further_art_appointment_if_drug_not_available"] - if self.module.rng.random_sample() >= p: + if self.module.rng.random_sample() >= p: - # add in referral straight back to tx - # if defaulting, seek another treatment appointment in 6 months - self.sim.modules["HealthSystem"].schedule_hsi_event( - hsi_event=HSI_Hiv_StartOrContinueTreatment( - person_id=person_id, module=self.module, - facility_level_of_this_hsi="1a", - ), - topen=self.sim.date + pd.DateOffset(months=6), - priority=0, - ) - - else: - # If person 'decides to' seek another treatment appointment, - # schedule a new HSI appointment for next month - # NB. With a probability of 1.0, this will keep occurring, - # if person has already tried unsuccessfully to get ART at level 1a 2 times - # then refer to level 1b - if self.counter_for_drugs_not_available <= 2: - # repeat attempt for ARVs at level 1a + # add in referral straight back to tx + # if defaulting, seek another treatment appointment in 6 months self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_StartOrContinueTreatment( person_id=person_id, module=self.module, - facility_level_of_this_hsi="1a" + facility_level_of_this_hsi="1a", ), - topen=self.sim.date + pd.DateOffset(months=1), + topen=self.sim.date + pd.DateOffset(months=6), priority=0, ) else: - # refer to higher facility level - self.sim.modules["HealthSystem"].schedule_hsi_event( - hsi_event=HSI_Hiv_StartOrContinueTreatment( - person_id=person_id, module=self.module, - facility_level_of_this_hsi="2" - ), - topen=self.sim.date + pd.DateOffset(days=1), - priority=0, - ) + # If person 'decides to' seek another treatment appointment, + # schedule a new HSI appointment for next month + # NB. With a probability of 1.0, this will keep occurring, + # if person has already tried unsuccessfully to get ART at level 1a 2 times + # then refer to level 1b + if self.counter_for_drugs_not_available <= 2: + # repeat attempt for ARVs at level 1a + self.sim.modules["HealthSystem"].schedule_hsi_event( + hsi_event=HSI_Hiv_StartOrContinueTreatment( + person_id=person_id, module=self.module, + facility_level_of_this_hsi="1a" + ), + topen=self.sim.date + pd.DateOffset(months=1), + priority=0, + ) + + else: + # refer to higher facility level + self.sim.modules["HealthSystem"].schedule_hsi_event( + hsi_event=HSI_Hiv_StartOrContinueTreatment( + person_id=person_id, module=self.module, + facility_level_of_this_hsi="2" + ), + topen=self.sim.date + pd.DateOffset(days=1), + priority=0, + ) # also screen for tb if "Tb" in self.sim.modules: From 272558f560707f3f68c279981084f9069728ab5a Mon Sep 17 00:00:00 2001 From: tdm32 Date: Thu, 7 Mar 2024 12:26:02 +0000 Subject: [PATCH 054/131] add hv_date_treated abd hv_date_last_ART to baseline_art --- src/tlo/methods/hiv.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 5d278181e8..f215befd45 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -766,6 +766,11 @@ def split_into_vl_and_notvl(all_idx, prob): # this window is 1-90 days (3-monthly prescribing) for person in art_idx: days = self.rng.randint(low=1, high=self.parameters['dispensation_period_months'] * 30.5, dtype=np.int64) + + date_treated = (params['dispensation_period_months'] * 30.5) - days + df.at[person, "hv_date_treated"] = self.sim.date - pd.to_timedelta(date_treated, unit="days") + df.at[person, "hv_date_last_ART"] = self.sim.date - pd.to_timedelta(date_treated, unit="days") + self.sim.schedule_event( Hiv_DecisionToContinueTreatment(person_id=person, module=self), self.sim.date + pd.to_timedelta(days, unit="days"), From 8602f038c2b155584c645970e4134bdb67100611 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 11 Mar 2024 13:37:23 +0000 Subject: [PATCH 055/131] convert linear model in CMD to include conditional predictors --- .../bladder_cancer_analyses_single_run.py | 2 +- ...se_of_death_and_disability_calibrations.py | 16 +++++++++---- src/tlo/methods/cardio_metabolic_disorders.py | 15 +++++++++--- src/tlo/methods/tb.py | 23 ++++--------------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py b/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py index 722af57a2c..308d455f6e 100644 --- a/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py +++ b/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py @@ -14,8 +14,8 @@ healthburden, healthseekingbehaviour, healthsystem, - simplified_births, schisto, + simplified_births, symptommanager, ) diff --git a/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py b/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py index e6fa66603d..9f1d7e578f 100644 --- a/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py +++ b/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py @@ -465,12 +465,18 @@ def get_total_num_dalys_by_wealth_and_label(_df): if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("results_folder", type=Path) - args = parser.parse_args() + # parser = argparse.ArgumentParser() + # parser.add_argument("results_folder", type=Path) + # args = parser.parse_args() + # + # apply( + # results_folder=args.results_folder, + # output_folder=args.results_folder, + # resourcefilepath=Path('./resources') + # ) apply( - results_folder=args.results_folder, - output_folder=args.results_folder, + results_folder=Path("outputs/long_run_all_diseases-2024-03-05T114732Z"), + output_folder=Path("outputs/long_run_all_diseases-2024-03-05T114732Z"), resourcefilepath=Path('./resources') ) diff --git a/src/tlo/methods/cardio_metabolic_disorders.py b/src/tlo/methods/cardio_metabolic_disorders.py index 9c2ba8514c..3ef122fb71 100644 --- a/src/tlo/methods/cardio_metabolic_disorders.py +++ b/src/tlo/methods/cardio_metabolic_disorders.py @@ -594,9 +594,7 @@ def build_linear_model(self, condition, interval_between_polls, lm_type): # LinearModel expects native python types - if it's numpy type, convert it baseline_annual_probability = float(baseline_annual_probability) - linearmodel = LinearModel( - LinearModelType.MULTIPLICATIVE, - baseline_annual_probability, + predictors = [ Predictor('sex').when('M', p['rr_male']), Predictor( 'age_years', @@ -692,6 +690,17 @@ def build_linear_model(self, condition, interval_between_polls, lm_type): 'rr_chronic_ischemic_heart_disease_on_medication']), Predictor('nc_ever_stroke_on_medication').when(True, p['rr_stroke_on_medication']), Predictor('nc_ever_heart_attack_on_medication').when(True, p['rr_heart_attack_on_medication']) + ] + + conditional_predictors = [ + Predictor().when('hv_inf & ' + '(hv_art != "on_VL_suppressed")', p['rr_hiv']), + ] if "Hiv" in self.sim.modules else [] + + linearmodel = LinearModel( + LinearModelType.MULTIPLICATIVE, + baseline_annual_probability, + *(predictors + conditional_predictors) ) return linearmodel diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 552211d53d..235240816e 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -627,7 +627,6 @@ def pre_initialise_population(self): self.lm["death_rate"] = LinearModel.multiplicative( *(predictors + conditional_predictors)) - def send_for_screening_general(self, population): df = population.props p = self.parameters @@ -652,7 +651,6 @@ def send_for_screening_general(self, population): priority=0, ) - def select_tb_test(self, person_id): df = self.sim.population.props p = self.parameters @@ -669,7 +667,6 @@ def select_tb_test(self, person_id): else: return "sputum" - def get_consumables_for_dx_and_tx(self): p = self.parameters # consumables = self.sim.modules["HealthSystem"].parameters["Consumables"] @@ -787,7 +784,6 @@ def get_consumables_for_dx_and_tx(self): self.item_codes_for_consumables_required['tb_ipt'] = { hs.get_item_code_from_item_name("Isoniazid/Pyridoxine, tablet 300 mg"): 1} - def initialise_population(self, population): df = population.props p = self.parameters @@ -851,7 +847,6 @@ def initialise_population(self, population): population ) # send some baseline population for screening - def initialise_simulation(self, sim): """ * 1) Schedule the regular TB events @@ -880,7 +875,6 @@ def initialise_simulation(self, sim): TbCheckPropertiesEvent(self), sim.date + pd.DateOffset(months=1) ) - def on_birth(self, mother_id, child_id): """Initialise properties for a newborn individual allocate IPT for child if mother diagnosed with TB @@ -930,7 +924,6 @@ def on_birth(self, mother_id, child_id): tclose=now + DateOffset(days=28), ) - def report_daly_values(self): """ This must send back a pd.Series or pd.DataFrame that reports on the average daly-weights that have been @@ -972,7 +965,6 @@ def report_daly_values(self): return health_values.loc[df.is_alive] - def calculate_untreated_proportion(self, population, strain): """ calculate the proportion of active TB cases not on correct treatment @@ -1004,7 +996,6 @@ def calculate_untreated_proportion(self, population, strain): return prop_untreated - def assign_active_tb(self, population, strain, incidence): """ select individuals to be infected @@ -1049,7 +1040,6 @@ def assign_active_tb(self, population, strain, incidence): # set date of active tb - properties will be updated at TbActiveEvent poll daily df.at[person_id, "tb_scheduled_date_active"] = date_progression - def consider_ipt_for_those_initiating_art(self, person_id): """ this is called by HIV when person is initiating ART @@ -1085,7 +1075,6 @@ def consider_ipt_for_those_initiating_art(self, person_id): tclose=None, ) - def relapse_event(self, population): """The Tb Regular Relapse Event runs every month to randomly sample amongst those previously infected with active tb @@ -1135,7 +1124,6 @@ def relapse_event(self, population): # properties will be updated at TbActiveEvent every month df.loc[idx_will_relapse, "tb_scheduled_date_active"] = now - def end_treatment(self, population): """ * check for those eligible to finish treatment @@ -1316,7 +1304,6 @@ def end_treatment(self, population): person_id=hiv_tb_infected, disease_module=self.sim.modules["Hiv"] ) - def check_config_of_properties(self): """check that the properties are currently configured correctly""" df = self.sim.population.props @@ -1491,11 +1478,9 @@ def apply(self, population): prop_untreated_mdr = self.module.calculate_untreated_proportion(population, strain="mdr") scaled_incidence_ds = incidence_year * \ - p["scaling_factor_WHO"] * prop_untreated_ds + p["scaling_factor_WHO"] * prop_untreated_ds scaled_incidence_mdr = incidence_year * \ - p["prop_mdr2010"] * \ - p["scaling_factor_WHO"] * \ - prop_untreated_mdr + p["prop_mdr2010"] * p["scaling_factor_WHO"] * prop_untreated_mdr # transmission ds-tb self.module.assign_active_tb(population, strain="ds", incidence=scaled_incidence_ds) @@ -2311,7 +2296,7 @@ def select_treatment(self, person_id): & (person["age_years"] <= 16) \ & ~(person["tb_smear"]) \ & ~person["tb_ever_treated"] \ - & ~person["tb_diagnosed_mdr"]: + & ~person["tb_diagnosed_mdr"]: treatment_regimen = "tb_tx_child_shorter" return treatment_regimen @@ -2367,7 +2352,7 @@ def apply(self, person_id, squeeze_factor): # if previously treated: if ((person["tb_treatment_regimen"] == "tb_retx_adult") or - (person["tb_treatment_regimen"] == "tb_retx_child")): + (person["tb_treatment_regimen"] == "tb_retx_child")): # if strain is ds and person previously treated: sputum_fup = follow_up_times["ds_retreatment_sputum"].dropna() From 2f87289a18e7f6e6128f8bf917008a1047094e99 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 11 Mar 2024 13:40:40 +0000 Subject: [PATCH 056/131] delete resourcefile created in error --- resources/~$ResourceFile_Bladder_Cancer.xlsx | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 resources/~$ResourceFile_Bladder_Cancer.xlsx diff --git a/resources/~$ResourceFile_Bladder_Cancer.xlsx b/resources/~$ResourceFile_Bladder_Cancer.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/~$ResourceFile_Bladder_Cancer.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 From c8d65f4a52ca7678377855880f7c38a890befdd6 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 11 Mar 2024 13:44:23 +0000 Subject: [PATCH 057/131] comment out path-specific changes to analysis_cause_of_death_and_disability_calibrations.py --- ...se_of_death_and_disability_calibrations.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py b/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py index 9f1d7e578f..eaba50b485 100644 --- a/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py +++ b/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py @@ -465,18 +465,18 @@ def get_total_num_dalys_by_wealth_and_label(_df): if __name__ == "__main__": - # parser = argparse.ArgumentParser() - # parser.add_argument("results_folder", type=Path) - # args = parser.parse_args() - # - # apply( - # results_folder=args.results_folder, - # output_folder=args.results_folder, - # resourcefilepath=Path('./resources') - # ) + parser = argparse.ArgumentParser() + parser.add_argument("results_folder", type=Path) + args = parser.parse_args() apply( - results_folder=Path("outputs/long_run_all_diseases-2024-03-05T114732Z"), - output_folder=Path("outputs/long_run_all_diseases-2024-03-05T114732Z"), + results_folder=args.results_folder, + output_folder=args.results_folder, resourcefilepath=Path('./resources') ) + + # apply( + # results_folder=Path("outputs/long_run_all_diseases-2024-03-05T114732Z"), + # output_folder=Path("outputs/long_run_all_diseases-2024-03-05T114732Z"), + # resourcefilepath=Path('./resources') + # ) From 1574c3a388c3db600fdace2c9a1b568a196bd5cf Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 11 Mar 2024 13:53:54 +0000 Subject: [PATCH 058/131] fix CMD error if Hiv not registered --- src/tlo/methods/cardio_metabolic_disorders.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/tlo/methods/cardio_metabolic_disorders.py b/src/tlo/methods/cardio_metabolic_disorders.py index 3ef122fb71..0dda7fb894 100644 --- a/src/tlo/methods/cardio_metabolic_disorders.py +++ b/src/tlo/methods/cardio_metabolic_disorders.py @@ -675,9 +675,6 @@ def build_linear_model(self, condition, interval_between_polls, lm_type): Predictor('nc_chronic_kidney_disease').when(True, p['rr_chronic_kidney_disease']), Predictor('nc_chronic_lower_back_pain').when(True, p['rr_chronic_lower_back_pain']), Predictor('nc_chronic_ischemic_hd').when(True, p['rr_chronic_ischemic_heart_disease']), - Predictor().when('hv_inf & ' - '(hv_art != "on_VL_suppressed")', - p["rr_hiv"]), Predictor('nc_ever_stroke').when(True, p['rr_ever_stroke']), Predictor('nc_ever_heart_attack').when(True, p['rr_ever_heart_attack']), Predictor('nc_diabetes_on_medication').when(True, p['rr_diabetes_on_medication']), From 9901edd91d2e96de93b3d5e14e276d9c69ffe93b Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 19 Mar 2024 12:54:05 +0000 Subject: [PATCH 059/131] remove parameter rr_bcg_inf from tb.py --- resources/ResourceFile_TB.xlsx | 4 ++-- src/tlo/methods/tb.py | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index 266fa479c1..1126738df7 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4adad7f8861f069bc3515b34ec39e4aa50b8fe260caa60bf49274a2c9a1fbc6a -size 55722 +oid sha256:f7c4ba6c55147beed25fa910d875c298ac3da06980c66722b63c902226992721 +size 55686 diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 11295a5707..88dad94b1d 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -166,9 +166,6 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "rr_tb_child": Parameter( Types.REAL, "relative risk of tb infection if under 16 years of age" ), - "rr_bcg_inf": Parameter( - Types.REAL, "relative risk of tb infection with bcg vaccination" - ), "monthly_prob_relapse_tx_complete": Parameter( Types.REAL, "monthly probability of relapse once treatment complete" ), From 7e3e20fcb292dc488b0e69f4a0d30a9853840388 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 22 Mar 2024 11:13:14 +0000 Subject: [PATCH 060/131] edit comment in initialise_simulation --- .../hiv/projections_jan2023/analysis_logged_deviance.py | 4 ++-- src/tlo/methods/tb.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/scripts/hiv/projections_jan2023/analysis_logged_deviance.py b/src/scripts/hiv/projections_jan2023/analysis_logged_deviance.py index c004748ad4..eca9f999bc 100644 --- a/src/scripts/hiv/projections_jan2023/analysis_logged_deviance.py +++ b/src/scripts/hiv/projections_jan2023/analysis_logged_deviance.py @@ -34,8 +34,8 @@ # %% Run the simulation start_date = Date(2010, 1, 1) -end_date = Date(2015, 1, 1) -popsize = 5000 +end_date = Date(2014, 1, 1) +popsize = 1000 # scenario = 1 diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 88dad94b1d..f07d82c15b 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -813,7 +813,7 @@ def initialise_population(self, population): def initialise_simulation(self, sim): """ * 1) Schedule the regular TB events - * 2) Schedule the scenario change + * 2) schedule logging * 3) Define the DxTests and treatment options """ From 922ac8e66a7619aad1ddeb6772cf79e64d275dfd Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 22 Mar 2024 12:46:21 +0000 Subject: [PATCH 061/131] fix parameter name error --- resources/ResourceFile_TB.xlsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index 1126738df7..b46ef8fcba 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7c4ba6c55147beed25fa910d875c298ac3da06980c66722b63c902226992721 -size 55686 +oid sha256:b957221d5c10e28b25fbde9aa615914622277239a35a87c4df326854f6cb2341 +size 55683 From 1b61aafe12a5890a5271504ce191336405c61404 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 22 Mar 2024 12:51:47 +0000 Subject: [PATCH 062/131] update parameters --- resources/ResourceFile_HIV.xlsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index 9eb4f364b4..d9817854d9 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca9100bd4c97a8a28c38a64a897ada58a4cd915b555f4d86e94cc5fa1e24ad3a -size 160625 +oid sha256:6d86f313e34e8a19dc250ea6cd6ea826447b02ccf18d86ca42db2912447c70fd +size 160692 From 3aa7b464fea186a0af2911f826879c0c5dab5a44 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 22 Mar 2024 13:05:52 +0000 Subject: [PATCH 063/131] test runs --- resources/ResourceFile_HIV.xlsx | 4 ++-- resources/ResourceFile_TB.xlsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index d9817854d9..1b13ed0e29 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d86f313e34e8a19dc250ea6cd6ea826447b02ccf18d86ca42db2912447c70fd -size 160692 +oid sha256:716157fb7df66d8b2c5eacf5c4b9b18338ba7a5d82840a57a5a500f4db8313fd +size 160321 diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index b46ef8fcba..78733c27de 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b957221d5c10e28b25fbde9aa615914622277239a35a87c4df326854f6cb2341 -size 55683 +oid sha256:5aac2da9963a0c05e0929b32dfa3faf87fb790cdc68be8dd4bb34d3597702212 +size 55618 From 604bd144342c12ea40e1c6f7a65653a482b1ed6b Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 22 Mar 2024 19:12:12 +0000 Subject: [PATCH 064/131] edit and fix flake8 errors --- .../analysis_scenarios_draws.json | 245 ++++++++++++++++++ src/tlo/methods/hiv.py | 69 +++-- src/tlo/methods/malaria.py | 4 +- src/tlo/methods/tb.py | 8 +- 4 files changed, 283 insertions(+), 43 deletions(-) create mode 100644 src/scripts/malaria/impact_analysis/analysis_scenarios_draws.json diff --git a/src/scripts/malaria/impact_analysis/analysis_scenarios_draws.json b/src/scripts/malaria/impact_analysis/analysis_scenarios_draws.json new file mode 100644 index 0000000000..146169bf82 --- /dev/null +++ b/src/scripts/malaria/impact_analysis/analysis_scenarios_draws.json @@ -0,0 +1,245 @@ +{ + "scenario_script_path": "src/scripts/malaria/impact_analysis/analysis_scenarios.py", + "scenario_seed": 0, + "runs_per_draw": 5, + "draws": [ + { + "draw_number": 0, + "parameters": { + "HealthSystem": { + "Service_Availability": [ + "*" + ], + "use_funded_or_actual_staffing": "funded", + "mode_appt_constraints": 1, + "policy_name": "Naive" + }, + "Hiv": { + "scenario": 0 + } + } + }, + { + "draw_number": 1, + "parameters": { + "HealthSystem": { + "Service_Availability": [ + "*" + ], + "use_funded_or_actual_staffing": "funded", + "mode_appt_constraints": 1, + "policy_name": "Naive" + }, + "Hiv": { + "scenario": 1 + } + } + }, + { + "draw_number": 2, + "parameters": { + "HealthSystem": { + "Service_Availability": [ + "*" + ], + "use_funded_or_actual_staffing": "funded", + "mode_appt_constraints": 1, + "policy_name": "Naive" + }, + "Hiv": { + "scenario": 2 + } + } + }, + { + "draw_number": 3, + "parameters": { + "HealthSystem": { + "Service_Availability": [ + "*" + ], + "use_funded_or_actual_staffing": "funded", + "mode_appt_constraints": 1, + "policy_name": "Naive" + }, + "Hiv": { + "scenario": 3 + } + } + }, + { + "draw_number": 4, + "parameters": { + "HealthSystem": { + "Service_Availability": [ + "*" + ], + "use_funded_or_actual_staffing": "funded", + "mode_appt_constraints": 1, + "policy_name": "Naive" + }, + "Hiv": { + "scenario": 5 + } + } + }, + { + "draw_number": 5, + "parameters": { + "HealthSystem": { + "Service_Availability": [ + "Alri_*", + "AntenatalCare_*", + "BladderCancer_*", + "BreastCancer_*", + "CardioMetabolicDisorders_*", + "Contraception_*", + "Copd_*", + "DeliveryCare_*", + "Depression_*", + "Diarrhoea_*", + "Epi_*", + "Epilepsy_*", + "FirstAttendance_*", + "Malaria_*", + "Measles_*", + "OesophagealCancer_*", + "OtherAdultCancer_*", + "PostnatalCare_*", + "ProstateCancer_*", + "Rti_*", + "Schisto_*", + "Tb_*", + "Undernutrition_*", + "Hiv_PalliativeCare" + ], + "use_funded_or_actual_staffing": "funded", + "mode_appt_constraints": 1, + "policy_name": "Naive" + }, + "Hiv": { + "scenario": 0 + } + } + }, + { + "draw_number": 6, + "parameters": { + "HealthSystem": { + "Service_Availability": [ + "Alri_*", + "AntenatalCare_*", + "BladderCancer_*", + "BreastCancer_*", + "CardioMetabolicDisorders_*", + "Contraception_*", + "Copd_*", + "DeliveryCare_*", + "Depression_*", + "Diarrhoea_*", + "Epi_*", + "Epilepsy_*", + "FirstAttendance_*", + "Hiv_*", + "Malaria_*", + "Measles_*", + "OesophagealCancer_*", + "OtherAdultCancer_*", + "PostnatalCare_*", + "ProstateCancer_*", + "Rti_*", + "Schisto_*", + "Undernutrition_*", + "Tb_PalliativeCare" + ], + "use_funded_or_actual_staffing": "funded", + "mode_appt_constraints": 1, + "policy_name": "Naive" + }, + "Hiv": { + "scenario": 0 + } + } + }, + { + "draw_number": 7, + "parameters": { + "HealthSystem": { + "Service_Availability": [ + "Alri_*", + "AntenatalCare_*", + "BladderCancer_*", + "BreastCancer_*", + "CardioMetabolicDisorders_*", + "Contraception_*", + "Copd_*", + "DeliveryCare_*", + "Depression_*", + "Diarrhoea_*", + "Epi_*", + "Epilepsy_*", + "FirstAttendance_*", + "Hiv_*", + "Measles_*", + "OesophagealCancer_*", + "OtherAdultCancer_*", + "PostnatalCare_*", + "ProstateCancer_*", + "Rti_*", + "Schisto_*", + "Tb_*", + "Undernutrition_*", + "Malaria_Treatment_Complicated" + ], + "use_funded_or_actual_staffing": "funded", + "mode_appt_constraints": 1, + "policy_name": "Naive" + }, + "Hiv": { + "scenario": 3 + } + } + }, + { + "draw_number": 8, + "parameters": { + "HealthSystem": { + "Service_Availability": [ + "Alri_*", + "AntenatalCare_*", + "BladderCancer_*", + "BreastCancer_*", + "CardioMetabolicDisorders_*", + "Contraception_*", + "Copd_*", + "DeliveryCare_*", + "Depression_*", + "Diarrhoea_*", + "Epi_*", + "Epilepsy_*", + "FirstAttendance_*", + "Measles_*", + "OesophagealCancer_*", + "OtherAdultCancer_*", + "PostnatalCare_*", + "ProstateCancer_*", + "Rti_*", + "Schisto_*", + "Undernutrition_*", + "Hiv_PalliativeCare", + "Tb_PalliativeCare", + "Malaria_Treatment_Complicated" + ], + "use_funded_or_actual_staffing": "funded", + "mode_appt_constraints": 1, + "policy_name": "Naive" + }, + "Hiv": { + "scenario": 3 + } + } + } + ], + "commit": "3ddf42452012af61b613ef76082fff028e6992d6", + "github": "https://github.com/UCL/TLOmodel/tree/3ddf42452012af61b613ef76082fff028e6992d6" +} \ No newline at end of file diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 6c22d3770d..c9dd299e36 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -9,7 +9,7 @@ - with viral suppression: when the person with not develop AIDS, or if they have already, it is relieved and they will not die of AIDS; and the person is not infectious - without viral suppression: when there is no benefit in avoiding AIDS and infectiousness is unchanged. -Maintenance on ART and PrEP is re-assessed at periodic 'Decision Events', at which is it is determined if the person +Maintenance on ART and PrEP is re-assessed at periodic 'Decision Events', at which it is determined if the person will attend the "next" HSI for continuation of the service; and if not, they are removed from that service and "stop treatment". If a stock-out or non-availability of health system resources prevent treatment continuation, the person "stops treatment". Stopping treatment leads to a new AIDS Event being scheduled. Persons can restart treatment. If a @@ -667,9 +667,9 @@ def initialise_baseline_prevalence(self, population): ) # this needs to be series of True/False infec = ( - self.rng.random_sample(len(p["overall_prob_of_infec"])) - < p["overall_prob_of_infec"] - ) & df.is_alive + self.rng.random_sample(len(p["overall_prob_of_infec"])) + < p["overall_prob_of_infec"] + ) & df.is_alive # Assign the designated person as infected in the population.props dataframe: df.loc[infec, "hv_inf"] = True @@ -715,7 +715,7 @@ def initialise_baseline_art(self, population): rr_art = pd.Series(1, index=df.index) rr_art.loc[ df.is_alive & (df.hv_date_inf < (self.sim.date - pd.DateOffset(years=10))) - ] = params["rel_probability_art_baseline_aids"] + ] = params["rel_probability_art_baseline_aids"] # Rescale relative probability of infection so that its average is 1.0 within each age/sex group p = pd.DataFrame( @@ -739,7 +739,7 @@ def initialise_baseline_art(self, population): art_idx = df.index[ (random_draw < p["overall_prob_of_art"]) & df.is_alive & df.hv_inf - ] + ] # 2) Determine adherence levels for those currently on ART, for each of adult men, adult women and children adult_f_art_idx = df.loc[ @@ -834,7 +834,7 @@ def initialise_baseline_tested(self, population): # sample number_deficit from remaining undiagnosed pop adult_undiagnosed = df.loc[ df.is_alive & df.hv_inf & ~df.hv_diagnosed & (df.age_years >= 15) - ].index + ].index adult_test_index = self.rng.choice( adult_undiagnosed, size=number_deficit, replace=False @@ -858,7 +858,7 @@ def initialise_baseline_tested(self, population): if hiv_test_deficit > 0: child_undiagnosed = df.loc[ df.is_alive & df.hv_inf & ~df.hv_diagnosed & (df.age_years < 15) - ].index + ].index child_test_index = self.rng.choice( child_undiagnosed, size=number_deficit, replace=False @@ -901,7 +901,7 @@ def initialise_simulation(self, sim): & df.hv_inf & ((self.sim.date - df.hv_date_inf).dt.days > 10 * 365) & (df.hv_art == "not") - ].index + ].index # Those that are in neither category are "before AIDS" (will have AIDS Onset Event scheduled) before_aids_idx = ( @@ -936,6 +936,7 @@ def initialise_simulation(self, sim): # If any days_since_infection >= days_infection_to_aids are negative resample # these values until all are positive days_until_aids_is_negative = days_since_infection >= days_infection_to_aids + while np.any(days_until_aids_is_negative): days_infection_to_aids[days_until_aids_is_negative] = ( self.sample_time_from_infection_to_aids_given_parameters( @@ -1046,6 +1047,7 @@ def initialise_simulation(self, sim): self.item_codes_for_consumables_required["First-line ART regimen: adult"] = { hs.get_item_code_from_item_name("First-line ART regimen: adult"): 1 } + # Note incorrect spelling of Cotrimoxazole in consumables resourcefile - matched here self.item_codes_for_consumables_required[ "First-line ART regimen: adult: cotrimoxazole" ] = {hs.get_item_code_from_item_name("Cotrimoxizole, 960mg pppy"): 1} @@ -1165,7 +1167,7 @@ def on_birth(self, mother_id, child_id): # usually performed by care_of_women_during_pregnancy module if not mother.hv_diagnosed and \ mother.is_alive and ( - self.rng.random_sample() < p["prob_hiv_test_at_anc_or_delivery"]): + self.rng.random_sample() < p["prob_hiv_test_at_anc_or_delivery"]): self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_TestAndRefer( person_id=abs(mother_id), # Pass mother's id, whether from true or direct birth @@ -1190,7 +1192,7 @@ def on_birth(self, mother_id, child_id): ) if "newborn_outcomes" not in self.sim.modules and ( - self.rng.random_sample() < p['prob_hiv_test_for_newborn_infant']): + self.rng.random_sample() < p['prob_hiv_test_for_newborn_infant']): self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_TestAndRefer( person_id=child_id, @@ -1384,9 +1386,9 @@ def prob_art_start_after_test(self, year): # use iloc to index by position as index will change by year return_prob = (prob_art.loc[ - (prob_art.year == current_year) & - (prob_art.age == "adults"), - "prob_art_if_dx"].values[0] * self.parameters["treatment_initiation_adjustment"]) + (prob_art.year == current_year) & + (prob_art.age == "adults"), + "prob_art_if_dx"].values[0] * self.parameters["treatment_initiation_adjustment"]) return return_prob @@ -1483,7 +1485,7 @@ def decide_whether_hiv_test_for_mother(self, person_id, referred_from) -> bool: df = self.sim.population.props if not df.at[person_id, 'hv_diagnosed'] and ( - self.rng.random_sample() < self.parameters['prob_hiv_test_at_anc_or_delivery']): + self.rng.random_sample() < self.parameters['prob_hiv_test_at_anc_or_delivery']): self.sim.modules['HealthSystem'].schedule_hsi_event( HSI_Hiv_TestAndRefer( @@ -1512,12 +1514,8 @@ def decide_whether_hiv_test_for_infant(self, mother_id, child_id) -> None: not df.at[child_id, "hv_diagnosed"] and df.at[mother_id, "hv_diagnosed"] and (df.at[child_id, "nb_pnc_check"] == 1) - and ( - self.rng.random_sample() - < self.parameters["prob_hiv_test_for_newborn_infant"] - ) + and (self.rng.random_sample() < self.parameters["prob_hiv_test_for_newborn_infant"]) ): - self.sim.modules["HealthSystem"].schedule_hsi_event( HSI_Hiv_TestAndRefer( person_id=child_id, @@ -1625,7 +1623,7 @@ def horizontal_transmission(to_sex, from_sex): & df.hv_inf & (df.hv_art != "on_VL_suppressed") & (df.sex == from_sex) - ] + ] ) if n_infectious > 0: @@ -1636,7 +1634,7 @@ def horizontal_transmission(to_sex, from_sex): & ~df.hv_inf & df.age_years.between(15, 80) & (df.sex == to_sex) - ].index + ].index n_susceptible = len(susc_idx) # Compute chance that each susceptible person becomes infected: @@ -1668,7 +1666,7 @@ def horizontal_transmission(to_sex, from_sex): & df.li_is_sexworker & ~df.hv_is_on_prep & df.age_years.between(15, 80) - ].index + ].index # - probability of infection - relative risk applies only to fsw p_infection_fsw = ( @@ -1725,7 +1723,7 @@ def spontaneous_testing(current_year): ) adult_tests_idx = df.loc[ df.is_alive & (df.age_years >= 15) & (random_draw < overall_prob_test) - ].index + ].index idx_will_test = adult_tests_idx @@ -1743,8 +1741,7 @@ def spontaneous_testing(current_year): ), priority=1, topen=date_test, - tclose=date_test - + pd.DateOffset( + tclose=date_test + pd.DateOffset( months=self.frequency.months ), # (to occur before next polling) ) @@ -1759,7 +1756,7 @@ def prep_for_agyw(): & df.age_years.between(15, 30) & (df.sex == "F") & ~df.hv_is_on_prep - ].index + ].index rr_of_infection_in_agyw = self.module.lm["rr_of_infection"].predict( df.loc[agyw_idx] @@ -1802,7 +1799,7 @@ def vmmc_for_child(): & (df.sex == "M") & (df.age_years < 15) & (~df.li_is_circ) - ], + ], self.module.rng, year=self.sim.date.year, ) @@ -1864,7 +1861,7 @@ def apply(self, person_id): # Consider mother-to-child-transmission (MTCT) from this person to their children: children_of_this_person_being_breastfed = df.loc[ (df.mother_id == person_id) & (df.nb_breastfeeding_status != "none") - ].index + ].index # - Do the MTCT routine for each child: for child_id in children_of_this_person_being_breastfed: self.module.mtct_during_breastfeeding(person_id, child_id) @@ -1930,7 +1927,7 @@ def apply(self, person_id): # need to delay onset of AIDS (non-tb) to compensate for AIDS-TB if (self.cause == "AIDS_non_TB") and ( - self.sim.modules["Hiv"].rng.rand() < self.sim.modules["Hiv"].parameters["prop_delayed_aids_onset"]): + self.sim.modules["Hiv"].rng.rand() < self.sim.modules["Hiv"].parameters["prop_delayed_aids_onset"]): # redraw time to aids and reschedule months_to_aids = int( @@ -2040,12 +2037,12 @@ def apply(self, person_id): # Do nothing if person is now on ART and VL suppressed (non VL suppressed has no effect) # only if no current TB infection if (df.at[person_id, "hv_art"] == "on_VL_suppressed") and ( - df.at[person_id, "tb_inf"] != "active"): + df.at[person_id, "tb_inf"] != "active"): return # off ART, no TB infection if (df.at[person_id, "hv_art"] != "on_VL_suppressed") and ( - df.at[person_id, "tb_inf"] != "active"): + df.at[person_id, "tb_inf"] != "active"): # cause is HIV (no TB) self.sim.modules["Demography"].do_death( individual_id=person_id, @@ -2246,7 +2243,7 @@ def apply(self, person_id): class HSI_Hiv_TestAndRefer(HSI_Event, IndividualScopeEventMixin): """ - The is the Test-and-Refer HSI. Individuals may seek an HIV test at any time. From this, they can be referred on to + This is the Test-and-Refer HSI. Individuals may seek an HIV test at any time. From this, they can be referred on to other services. This event is scheduled by: * the main event poll, @@ -2500,7 +2497,7 @@ def apply(self, person_id, squeeze_factor): # if breastfeeding has ceased or child >18 months, no further prophylaxis required if (df.at[person_id, "nb_breastfeeding_status"] == "none") \ - or (df.at[person_id, "age_years"] >= 1.5): + or (df.at[person_id, "age_years"] >= 1.5): return self.sim.modules["HealthSystem"].get_blank_appt_footprint() # Check that infant prophylaxis is available and if it is, initiate: @@ -2608,7 +2605,6 @@ def apply(self, person_id, squeeze_factor): self.counter_for_drugs_not_available <= self.module.parameters["hiv_healthseekingbehaviour_cap"] ): - # Schedule repeat visit for one week's time self.sim.modules["HealthSystem"].schedule_hsi_event( self, @@ -2650,7 +2646,7 @@ def apply(self, person_id, squeeze_factor): # check whether person had Rx at least 3 months ago and is now due repeat prescription # alternate routes into testing/tx may mean person already has recent ARV dispensation if person['hv_date_last_ART'] >= ( - self.sim.date - pd.DateOffset(months=self.module.parameters['dispensation_period_months'])): + self.sim.date - pd.DateOffset(months=self.module.parameters['dispensation_period_months'])): return self.sim.modules["HealthSystem"].get_blank_appt_footprint() if art_status_at_beginning_of_hsi == "not": @@ -2956,6 +2952,7 @@ def apply(self, person_id, squeeze_factor): data=f"HSI_Hiv_EndOfLifeCare: inpatient admission for {person_id}", ) + # --------------------------------------------------------------------------- # Logging # --------------------------------------------------------------------------- diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 0e2f29e641..521bfe90bf 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -459,7 +459,7 @@ def _draw_incidence_for(_col, _where): ) # get the monthly incidence probabilities for these individuals monthly_prob = curr_inc.loc[district_age_lookup, _col] - # update the index so it"s the same as the original population dataframe for these individuals + # update the index so it's the same as the original population dataframe for these individuals monthly_prob = monthly_prob.set_axis(df.index[_where]) # the linear models only apply to clinical and severe malaria risk @@ -723,7 +723,7 @@ def on_birth(self, mother_id, child_id): df.at[child_id, "ma_tx_counter"] = 0 df.at[child_id, "ma_iptp"] = False - # reset mother"s IPTp status to False + # reset mother's IPTp status to False if mother_id >= 0: # exclude direct births df.at[mother_id, "ma_iptp"] = False diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 1c95b7583f..37b55d20cc 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -15,7 +15,6 @@ from tlo.methods import Metadata, hiv from tlo.methods.causes import Cause from tlo.methods.dxmanager import DxTest -from tlo.methods.healthsystem import HealthSystemChangeParameters, HSI_Event from tlo.methods.hsi_event import HSI_Event from tlo.methods.symptommanager import Symptom from tlo.util import random_date @@ -1317,7 +1316,7 @@ def apply(self, population): inc_estimates = p["who_incidence_estimates"] incidence_year = (inc_estimates.loc[ - (inc_estimates.year == self.sim.date.year), "incidence_per_100k" + (inc_estimates.year == current_year), "incidence_per_100k" ].values[0]) / 100000 prop_untreated_ds = self.module.calculate_untreated_proportion(population, strain="ds") @@ -1561,7 +1560,7 @@ def apply(self, population): class HSI_Tb_ScreeningAndRefer(HSI_Event, IndividualScopeEventMixin): """ - The is the Screening-and-Refer HSI. + This is the Screening-and-Refer HSI. A positive outcome from symptom-based screening will prompt referral to tb tests (sputum/xpert/xray) no consumables are required for screening (4 clinical questions) @@ -1980,7 +1979,7 @@ def apply(self, person_id, squeeze_factor): class HSI_Tb_Xray_level2(HSI_Event, IndividualScopeEventMixin): """ - The is the x-ray HSI performed at level 2 + This is the x-ray HSI performed at level 2 usually used for testing children unable to produce sputum positive result will prompt referral to start treatment """ @@ -2197,7 +2196,6 @@ def select_treatment(self, person_id): # treatment for reinfection ds-tb: child treatment_regimen = "tb_retx_child" - return treatment_regimen From 629e2e0ea5a8f80ec6a570638f54761d7b3b6dc2 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Fri, 22 Mar 2024 19:40:01 +0000 Subject: [PATCH 065/131] fix failing test --- src/tlo/methods/hiv.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index c9dd299e36..dcf0912e6a 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -2552,6 +2552,7 @@ def __init__(self, module, person_id): self.TREATMENT_ID = "Hiv_Prevention_Prep" self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"PharmDispensing": 1, "VCTNegative": 1}) self.ACCEPTED_FACILITY_LEVEL = '1a' + self.counter_for_drugs_not_available = 0 def apply(self, person_id, squeeze_factor): """Start PrEP for this person; or continue them on PrEP for 3 more months""" From 8bba11cb896b5ca78c1dee0fd883d0bf5cf78ea2 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 25 Mar 2024 11:41:30 +0000 Subject: [PATCH 066/131] update ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx --- ...urceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx index 9efe4c8d66..5b2d55972e 100644 --- a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx +++ b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0b17e025cd9de74b38f78e70ac55e868ed168daa0f317e819fcc48dd9dd8a35 -size 48829 +oid sha256:f18f5a91e48fc846c332b55f1742d148816b1fee08ee34383c9995ea36dafdb3 +size 48167 From 6543bd0656b969e63de4e421e91094ad7d321ae7 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 25 Mar 2024 11:54:16 +0000 Subject: [PATCH 067/131] update ResourceFile_PriorityRanking_ALLPOLICIES.xlsx --- .../ResourceFile_PriorityRanking_ALLPOLICIES.xlsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/healthsystem/priority_policies/ResourceFile_PriorityRanking_ALLPOLICIES.xlsx b/resources/healthsystem/priority_policies/ResourceFile_PriorityRanking_ALLPOLICIES.xlsx index 4b6c6a144b..d9dbac2e99 100644 --- a/resources/healthsystem/priority_policies/ResourceFile_PriorityRanking_ALLPOLICIES.xlsx +++ b/resources/healthsystem/priority_policies/ResourceFile_PriorityRanking_ALLPOLICIES.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1012950be2346232550e7d2efaa5efe936b77a34a4bcb3e713dd12c4abc434d5 -size 42381 +oid sha256:734d46d83dccf15bf38ee171a487664f01035da6cf68660d4af62097a6160fb6 +size 42716 From 5edc77d5b087c1c4c17de54c8c253a5824557b57 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 25 Mar 2024 12:03:46 +0000 Subject: [PATCH 068/131] updated ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx --- ...urceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx index 5b2d55972e..ff88584e3f 100644 --- a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx +++ b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f18f5a91e48fc846c332b55f1742d148816b1fee08ee34383c9995ea36dafdb3 -size 48167 +oid sha256:da900375dda86f999e744bfb6d6dec7347d5b13f176046ba182740421a43d256 +size 48274 From b34d67024ea0cf348e815d394f1b9a63a33711e8 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Thu, 2 May 2024 12:08:45 +0100 Subject: [PATCH 069/131] edit check for last ART when new dispensation occurs --- src/tlo/methods/hiv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 683ef65c7f..1292dd6649 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -2666,7 +2666,7 @@ def apply(self, person_id, squeeze_factor): # check whether person had Rx at least 3 months ago and is now due repeat prescription # alternate routes into testing/tx may mean person already has recent ARV dispensation - if person['hv_date_last_ART'] >= ( + if person['hv_date_last_ART'] > ( self.sim.date - pd.DateOffset(months=self.module.parameters['dispensation_period_months'])): return self.sim.modules["HealthSystem"].get_blank_appt_footprint() From d56c3d827046feb087fd7a91f4ca07ed986e31ab Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 13 May 2024 12:08:27 +0100 Subject: [PATCH 070/131] update schisto risk on HIV to only include women --- src/tlo/methods/hiv.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 1292dd6649..9d4ca582af 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -476,7 +476,11 @@ def pre_initialise_population(self): ] conditional_predictors = [ - Predictor('ss_sh_infection_status').when('High-infection', p['rr_schisto']), + Predictor().when( + '(ss_sh_infection_status == "High-infection") &' + '(sex == "F")', + p["rr_schisto"] + ), ] if "Schisto" in self.sim.modules else [] self.lm["rr_of_infection"] = LinearModel.multiplicative( From 9667bd3047f0ff87a735aa4fbb291fd1a6eccf93 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 13 May 2024 17:57:04 +0100 Subject: [PATCH 071/131] add scale-up parameter for htm programs add event to hiv module to switch parameters include new keywords in hiv module for scale-up separately for hiv, tb and malaria add new sheet in ResourceFile_HIV.xlsx containing new scale-up parameter values --- resources/ResourceFile_HIV.xlsx | 4 +- resources/malaria/~$ResourceFile_malaria.xlsx | 3 + resources/~$ResourceFile_HIV.xlsx | 3 + src/tlo/methods/hiv.py | 179 +++++++++++++++++- 4 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 resources/malaria/~$ResourceFile_malaria.xlsx create mode 100644 resources/~$ResourceFile_HIV.xlsx diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index 1b13ed0e29..caf29229aa 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:716157fb7df66d8b2c5eacf5c4b9b18338ba7a5d82840a57a5a500f4db8313fd -size 160321 +oid sha256:57ca89f6bc386abd90363f63b9f0a097d5a1fb5dab1e81b7ac4e3a30353dc663 +size 161902 diff --git a/resources/malaria/~$ResourceFile_malaria.xlsx b/resources/malaria/~$ResourceFile_malaria.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/malaria/~$ResourceFile_malaria.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/resources/~$ResourceFile_HIV.xlsx b/resources/~$ResourceFile_HIV.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/~$ResourceFile_HIV.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 9d4ca582af..05a484f62d 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -30,7 +30,7 @@ import numpy as np import pandas as pd -from tlo import DAYS_IN_YEAR, DateOffset, Module, Parameter, Property, Types, logging +from tlo import DAYS_IN_YEAR, DateOffset, Module, Parameter, Property, Types, logging, Date from tlo.core import IndividualPropertyUpdates from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent from tlo.lm import LinearModel, LinearModelType, Predictor @@ -50,13 +50,22 @@ class Hiv(Module): The HIV Disease Module """ - def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): + def __init__(self, name=None, resourcefilepath=None, run_with_checks=False, + scaleup_hiv=False, scaleup_tb=False, scaleup_malaria=False, + scaleup_start_date=pd.NaT): + super().__init__(name) self.resourcefilepath = resourcefilepath assert isinstance(run_with_checks, bool) self.run_with_checks = run_with_checks + # determine whether to scale-up programs on a specified date + self.scaleup_hiv = scaleup_hiv + self.scaleup_tb = scaleup_tb + self.scaleup_malaria = scaleup_malaria + self.scaleup_start_date = scaleup_start_date + self.stored_test_numbers = [] # create empty list for storing hiv test numbers # hiv outputs needed for calibration @@ -389,6 +398,11 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): Types.REAL, "length of prescription for ARVs in months, same for all PLHIV", ), + # ------------------ scale-up parameters for scenario analysis ------------------ # + "scaleup_parameters": Parameter( + Types.DATA_FRAME, + "list of parameters and values changed in scenario analysis", + ), } def read_parameters(self, data_folder): @@ -426,6 +440,9 @@ def read_parameters(self, data_folder): # Load spectrum estimates of treatment cascade p["treatment_cascade"] = workbook["spectrum_treatment_cascade"] + # load parameters for scale-up projections + p["scaleup_parameters"] = workbook["scaleup_parameters"] + # DALY weights # get the DALY weight that this module will use from the weight database (these codes are just random!) if "HealthBurden" in self.sim.modules.keys(): @@ -895,6 +912,14 @@ def initialise_simulation(self, sim): # 2) Schedule the Logging Event sim.schedule_event(HivLoggingEvent(self), sim.date + DateOffset(years=1)) + # Optional: Schedule the scale-up of programs + assert isinstance(self.scaleup_start_date, Date), "Value is not a Date object" + # Check if scale-up start date is on or after sim start date + assert self.scaleup_start_date >= Date(2010, 1, 1), \ + f"Date {self.scaleup_start_date} is before January 1, 2010" + + sim.schedule_event(ScaleUpSetupEvent(self), self.scaleup_start_date) + # 3) Determine who has AIDS and impose the Symptoms 'aids_symptoms' # Those on ART currently (will not get any further events scheduled): @@ -1098,6 +1123,141 @@ def initialise_simulation(self, sim): ) ) + def update_parameters(self): + + p = self.parameters + scaled_params = p["scaleup_parameters"] + + scaleup_hiv = self.scaleup_hiv + scaleup_tb = self.scaleup_tb + scaleup_malaria = self.scaleup_malaria + + if scaleup_hiv: + # scale-up HIV program + # reduce risk of HIV - applies to whole adult population + self.sim.modules["Hiv"].parameters["beta"] = self.sim.modules["Hiv"].parameters["beta"] * \ + scaled_params.loc[ + scaled_params.parameter == "reduction_in_hiv_beta", "scaleup_value"].value[ + 0] + + # increase PrEP coverage for FSW after HIV test + self.sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] = scaled_params.loc[ + scaled_params.parameter == "prob_prep_for_fsw_after_hiv_test", "scaleup_value"].value[0] + + # prep poll for AGYW - target to the highest risk + # increase retention to 75% for FSW and AGYW + self.sim.modules["Hiv"].parameters["prob_prep_for_agyw"] = scaled_params.loc[ + scaled_params.parameter == "prob_prep_for_agyw", "scaleup_value"].value[0] + self.sim.modules["Hiv"].parameters[ + "probability_of_being_retained_on_prep_every_3_months"] = scaled_params.loc[ + scaled_params.parameter == "probability_of_being_retained_on_prep_every_3_months", "scaleup_value"].value[ + 0] + + # increase probability of VMMC after hiv test + self.sim.modules["Hiv"].parameters["prob_circ_after_hiv_test"] = scaled_params.loc[ + scaled_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].value[0] + + # increase testing/diagnosis rates, default 2020 0.03/0.25 -> 93% dx + self.sim.modules["Hiv"].parameters["hiv_testing_rates"]["annual_testing_rate_children"] = \ + scaled_params.loc[ + scaled_params.parameter == "annual_testing_rate_children", "scaleup_value"].value[0] + self.sim.modules["Hiv"].parameters["hiv_testing_rates"]["annual_testing_rate_adults"] = \ + scaled_params.loc[ + scaled_params.parameter == "annual_testing_rate_adults", "scaleup_value"].value[0] + + # ANC testing - value for mothers and infants testing + self.sim.modules["Hiv"].parameters["prob_hiv_test_at_anc_or_delivery"] = scaled_params.loc[ + scaled_params.parameter == "prob_hiv_test_at_anc_or_delivery", "scaleup_value"].value[0] + self.sim.modules["Hiv"].parameters["prob_hiv_test_for_newborn_infant"] = scaled_params.loc[ + scaled_params.parameter == "prob_hiv_test_for_newborn_infant", "scaleup_value"].value[0] + + # prob ART start if dx, this is already 95% at 2020 + # self.sim.modules["Hiv"].parameters["prob_start_art_after_hiv_test"] = scaled_params.loc[ + # scaled_params.parameter == + # "prob_start_art_after_hiv_test", "value"].values[0] + + # viral suppression rates + # adults already at 95% by 2020 + # change all column values + self.sim.modules["Hiv"].parameters["prob_start_art_or_vs"]["virally_suppressed_on_art"] = \ + scaled_params.loc[ + scaled_params.parameter == "virally_suppressed_on_art", "scaleup_value"].value[0] + + if scaleup_tb: + # scale-up TB program + # use NTP treatment rates + self.sim.modules["Tb"].parameters["rate_testing_active_tb"]["treatment_coverage"] = scaled_params.loc[ + scaled_params.parameter == "tb_treatment_coverage", "scaleup_value"].value[0] + + # increase tb treatment success rates + self.sim.modules["Tb"].parameters["prob_tx_success_ds"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_ds", "scaleup_value"].value[0] + self.sim.modules["Tb"].parameters["prob_tx_success_mdr"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_mdr", "scaleup_value"].value[0] + self.sim.modules["Tb"].parameters["prob_tx_success_0_4"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_0_4", "scaleup_value"].value[0] + self.sim.modules["Tb"].parameters["prob_tx_success_5_14"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_5_14", "scaleup_value"].value[0] + self.sim.modules["Tb"].parameters["prob_tx_success_shorter"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_shorter", "scaleup_value"].value[0] + + # change first-line testing for TB to xpert + p["first_line_test"] = scaled_params.loc[ + scaled_params.parameter == "first_line_test", "scaleup_value"].value[0] + p["second_line_test"] = scaled_params.loc[ + scaled_params.parameter == "second_line_test", "scaleup_value"].value[0] + + # increase coverage of IPT + p["ipt_coverage"]["coverage_plhiv"] = scaled_params.loc[ + scaled_params.parameter == "ipt_coverage_plhiv", "scaleup_value"].value[0] + p["ipt_coverage"]["coverage_paediatric"] = scaled_params.loc[ + scaled_params.parameter == "ipt_coverage_paediatric", "scaleup_value"].value[0] + + if scaleup_malaria: + # scale-up malaria program + # testing reaches XX + prob_malaria_case_tests=0.4 + + # gen pop testing rates + # annual Rate_rdt_testing=0.47 at 2023 + self.sim.modules["Malaria"].parameters["rdt_testing_rates"]["Rate_rdt_testing"] = \ + scaled_params.loc[ + scaled_params.parameter == "rdt_testing_rates", "scaleup_value"].value[0] + + + # treatment reaches XX + # no default between testing and treatment, governed by tx availability + + # coverage IPTp reaches XX + + # treatment success reaches 1 + self.sim.modules["Malaria"].parameters["prob_of_treatment_success"] = scaled_params.loc[ + scaled_params.parameter == "prob_of_treatment_success", "scaleup_value"].value[0] + + + # bednet and ITN coverage + # set IRS to 0 for all districts + # lookup table created in malaria read_parameters + # produces self.itn_irs called by malaria poll to draw incidence + # need to overwrite this + self.sim.modules["Malaria"].itn_irs['irs_rate'] = treatment_effects.loc[ + treatment_effects.parameter == "irs_district", "no_effect"].values[0] + + # set ITN to 0 for all districts + # itn_district + self.sim.modules["Malaria"].itn_irs['itn_rate'] = treatment_effects.loc[ + treatment_effects.parameter == "itn_district", "no_effect"].values[0] + + # itn rates for 2019 onwards + self.sim.modules["Malaria"].parameters["itn"] = treatment_effects.loc[ + treatment_effects.parameter == "itn", "no_effect"].values[0] + + + + + + + def on_birth(self, mother_id, child_id): """ * Initialise our properties for a newborn individual; @@ -2260,6 +2420,21 @@ def apply(self, person_id): ) +class ScaleUpSetupEvent(RegularEvent, PopulationScopeEventMixin): + """ This event exists to change parameters or functions + depending on the scenario for projections which has been set + It only occurs once on date: scaleup_start_date, + called by initialise_simulation + """ + + def __init__(self, module): + super().__init__(module, frequency=DateOffset(years=100)) + + def apply(self, population): + + self.module.update_parameters(self) + + # --------------------------------------------------------------------------- # Health System Interactions (HSI) # --------------------------------------------------------------------------- From 73c6abff1995e70c0e28ec329bd83e926896997c Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 14 May 2024 09:58:49 +0100 Subject: [PATCH 072/131] add catch for malaria rdt_testing_rates post-2024 --- resources/ResourceFile_HIV.xlsx | 4 +- resources/malaria/ResourceFile_malaria.xlsx | 4 +- resources/~$ResourceFile_TB.xlsx | 3 + src/scripts/malaria/analysis_malaria.py | 6 +- src/tlo/methods/hiv.py | 143 ++++++++++---------- src/tlo/methods/malaria.py | 8 +- 6 files changed, 88 insertions(+), 80 deletions(-) create mode 100644 resources/~$ResourceFile_TB.xlsx diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index caf29229aa..c6ad16721d 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57ca89f6bc386abd90363f63b9f0a097d5a1fb5dab1e81b7ac4e3a30353dc663 -size 161902 +oid sha256:26103cfd265a1d570ad71aff17b8cea55568eb732e17beb83e8780c529bd008a +size 162030 diff --git a/resources/malaria/ResourceFile_malaria.xlsx b/resources/malaria/ResourceFile_malaria.xlsx index 70902b7480..8ea15f92f6 100644 --- a/resources/malaria/ResourceFile_malaria.xlsx +++ b/resources/malaria/ResourceFile_malaria.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ba5849e265103ee799d1982325b6fed1ef4d3df559ffce9d6790395c201fcaf -size 67562 +oid sha256:924528f79e315c390b940881944e4756bbbba60ad05f6bbc925b0c1a10977d9b +size 67588 diff --git a/resources/~$ResourceFile_TB.xlsx b/resources/~$ResourceFile_TB.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/~$ResourceFile_TB.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/src/scripts/malaria/analysis_malaria.py b/src/scripts/malaria/analysis_malaria.py index 56d05cf3ae..d59182e3af 100644 --- a/src/scripts/malaria/analysis_malaria.py +++ b/src/scripts/malaria/analysis_malaria.py @@ -34,8 +34,8 @@ resourcefilepath = Path("./resources") start_date = Date(2010, 1, 1) -end_date = Date(2016, 1, 1) -popsize = 300 +end_date = Date(2014, 1, 1) +popsize = 100 # set up the log config @@ -78,6 +78,8 @@ ), hiv.Hiv( resourcefilepath=resourcefilepath, + scaleup_hiv=True, scaleup_tb=True, scaleup_malaria=True, + scaleup_start_date=Date(2010, 2, 1) ), epi.Epi( resourcefilepath=resourcefilepath, diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 05a484f62d..cf75247b6d 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -1125,93 +1125,94 @@ def initialise_simulation(self, sim): def update_parameters(self): - p = self.parameters - scaled_params = p["scaleup_parameters"] + p_hiv = self.parameters + scaled_params = p_hiv["scaleup_parameters"] + + p_tb = self.sim.modules['Tb'].parameters + p_mal = self.sim.modules['Malaria'].parameters scaleup_hiv = self.scaleup_hiv scaleup_tb = self.scaleup_tb scaleup_malaria = self.scaleup_malaria + print(scaleup_malaria) + if scaleup_hiv: + # scale-up HIV program # reduce risk of HIV - applies to whole adult population - self.sim.modules["Hiv"].parameters["beta"] = self.sim.modules["Hiv"].parameters["beta"] * \ - scaled_params.loc[ - scaled_params.parameter == "reduction_in_hiv_beta", "scaleup_value"].value[ - 0] + p_hiv["beta"] = p_hiv["beta"] * scaled_params.loc[( + scaled_params.parameter == "reduction_in_hiv_beta"), "scaleup_value"].values[0] # increase PrEP coverage for FSW after HIV test - self.sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] = scaled_params.loc[ - scaled_params.parameter == "prob_prep_for_fsw_after_hiv_test", "scaleup_value"].value[0] + p_hiv["prob_prep_for_fsw_after_hiv_test"] = scaled_params.loc[ + scaled_params.parameter == "prob_prep_for_fsw_after_hiv_test", "scaleup_value"].values[0] # prep poll for AGYW - target to the highest risk # increase retention to 75% for FSW and AGYW - self.sim.modules["Hiv"].parameters["prob_prep_for_agyw"] = scaled_params.loc[ - scaled_params.parameter == "prob_prep_for_agyw", "scaleup_value"].value[0] - self.sim.modules["Hiv"].parameters[ - "probability_of_being_retained_on_prep_every_3_months"] = scaled_params.loc[ - scaled_params.parameter == "probability_of_being_retained_on_prep_every_3_months", "scaleup_value"].value[ + p_hiv["prob_prep_for_agyw"] = scaled_params.loc[ + scaled_params.parameter == "prob_prep_for_agyw", "scaleup_value"].values[0] + p_hiv["probability_of_being_retained_on_prep_every_3_months"] = scaled_params.loc[ + scaled_params.parameter == "probability_of_being_retained_on_prep_every_3_months", "scaleup_value"].values[ 0] # increase probability of VMMC after hiv test - self.sim.modules["Hiv"].parameters["prob_circ_after_hiv_test"] = scaled_params.loc[ - scaled_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].value[0] + p_hiv["prob_circ_after_hiv_test"] = scaled_params.loc[ + scaled_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] # increase testing/diagnosis rates, default 2020 0.03/0.25 -> 93% dx - self.sim.modules["Hiv"].parameters["hiv_testing_rates"]["annual_testing_rate_children"] = \ + p_hiv["hiv_testing_rates"]["annual_testing_rate_children"] = \ scaled_params.loc[ - scaled_params.parameter == "annual_testing_rate_children", "scaleup_value"].value[0] - self.sim.modules["Hiv"].parameters["hiv_testing_rates"]["annual_testing_rate_adults"] = \ + scaled_params.parameter == "annual_testing_rate_children", "scaleup_value"].values[0] + p_hiv["hiv_testing_rates"]["annual_testing_rate_adults"] = \ scaled_params.loc[ - scaled_params.parameter == "annual_testing_rate_adults", "scaleup_value"].value[0] + scaled_params.parameter == "annual_testing_rate_adults", "scaleup_value"].values[0] # ANC testing - value for mothers and infants testing - self.sim.modules["Hiv"].parameters["prob_hiv_test_at_anc_or_delivery"] = scaled_params.loc[ - scaled_params.parameter == "prob_hiv_test_at_anc_or_delivery", "scaleup_value"].value[0] - self.sim.modules["Hiv"].parameters["prob_hiv_test_for_newborn_infant"] = scaled_params.loc[ - scaled_params.parameter == "prob_hiv_test_for_newborn_infant", "scaleup_value"].value[0] + p_hiv["prob_hiv_test_at_anc_or_delivery"] = scaled_params.loc[ + scaled_params.parameter == "prob_hiv_test_at_anc_or_delivery", "scaleup_value"].values[0] + p_hiv["prob_hiv_test_for_newborn_infant"] = scaled_params.loc[ + scaled_params.parameter == "prob_hiv_test_for_newborn_infant", "scaleup_value"].values[0] # prob ART start if dx, this is already 95% at 2020 - # self.sim.modules["Hiv"].parameters["prob_start_art_after_hiv_test"] = scaled_params.loc[ - # scaled_params.parameter == - # "prob_start_art_after_hiv_test", "value"].values[0] + p_hiv["prob_start_art_after_hiv_test"] = scaled_params.loc[ + scaled_params.parameter == + "prob_start_art_after_hiv_test", "scaleup_value"].values[0] # viral suppression rates # adults already at 95% by 2020 # change all column values - self.sim.modules["Hiv"].parameters["prob_start_art_or_vs"]["virally_suppressed_on_art"] = \ + p_hiv["prob_start_art_or_vs"]["virally_suppressed_on_art"] = \ scaled_params.loc[ - scaled_params.parameter == "virally_suppressed_on_art", "scaleup_value"].value[0] + scaled_params.parameter == "virally_suppressed_on_art", "scaleup_value"].values[0] if scaleup_tb: # scale-up TB program # use NTP treatment rates - self.sim.modules["Tb"].parameters["rate_testing_active_tb"]["treatment_coverage"] = scaled_params.loc[ - scaled_params.parameter == "tb_treatment_coverage", "scaleup_value"].value[0] + p_tb["rate_testing_active_tb"]["treatment_coverage"] = scaled_params.loc[ + scaled_params.parameter == "tb_treatment_coverage", "scaleup_value"].values[0] # increase tb treatment success rates - self.sim.modules["Tb"].parameters["prob_tx_success_ds"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_ds", "scaleup_value"].value[0] - self.sim.modules["Tb"].parameters["prob_tx_success_mdr"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_mdr", "scaleup_value"].value[0] - self.sim.modules["Tb"].parameters["prob_tx_success_0_4"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_0_4", "scaleup_value"].value[0] - self.sim.modules["Tb"].parameters["prob_tx_success_5_14"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_5_14", "scaleup_value"].value[0] - self.sim.modules["Tb"].parameters["prob_tx_success_shorter"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_shorter", "scaleup_value"].value[0] + p_tb["prob_tx_success_ds"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_ds", "scaleup_value"].values[0] + p_tb["prob_tx_success_mdr"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_mdr", "scaleup_value"].values[0] + p_tb["prob_tx_success_0_4"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_0_4", "scaleup_value"].values[0] + p_tb["prob_tx_success_5_14"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_5_14", "scaleup_value"].values[0] # change first-line testing for TB to xpert - p["first_line_test"] = scaled_params.loc[ - scaled_params.parameter == "first_line_test", "scaleup_value"].value[0] - p["second_line_test"] = scaled_params.loc[ - scaled_params.parameter == "second_line_test", "scaleup_value"].value[0] + p_tb["first_line_test"] = scaled_params.loc[ + scaled_params.parameter == "first_line_test", "scaleup_value"].values[0] + p_tb["second_line_test"] = scaled_params.loc[ + scaled_params.parameter == "second_line_test", "scaleup_value"].values[0] # increase coverage of IPT - p["ipt_coverage"]["coverage_plhiv"] = scaled_params.loc[ - scaled_params.parameter == "ipt_coverage_plhiv", "scaleup_value"].value[0] - p["ipt_coverage"]["coverage_paediatric"] = scaled_params.loc[ - scaled_params.parameter == "ipt_coverage_paediatric", "scaleup_value"].value[0] + p_tb["ipt_coverage"]["coverage_plhiv"] = scaled_params.loc[ + scaled_params.parameter == "ipt_coverage_plhiv", "scaleup_value"].values[0] + p_tb["ipt_coverage"]["coverage_paediatric"] = scaled_params.loc[ + scaled_params.parameter == "ipt_coverage_paediatric", "scaleup_value"].values[0] if scaleup_malaria: # scale-up malaria program @@ -1219,20 +1220,20 @@ def update_parameters(self): prob_malaria_case_tests=0.4 # gen pop testing rates - # annual Rate_rdt_testing=0.47 at 2023 - self.sim.modules["Malaria"].parameters["rdt_testing_rates"]["Rate_rdt_testing"] = \ + # annual Rate_rdt_testing=0.64 at 2023 + p_mal["rdt_testing_rates"]["Rate_rdt_testing"] = \ scaled_params.loc[ - scaled_params.parameter == "rdt_testing_rates", "scaleup_value"].value[0] + scaled_params.parameter == "rdt_testing_rates", "scaleup_value"].values[0] - # treatment reaches XX - # no default between testing and treatment, governed by tx availability - - # coverage IPTp reaches XX - - # treatment success reaches 1 - self.sim.modules["Malaria"].parameters["prob_of_treatment_success"] = scaled_params.loc[ - scaled_params.parameter == "prob_of_treatment_success", "scaleup_value"].value[0] + # # treatment reaches XX + # # no default between testing and treatment, governed by tx availability + # + # # coverage IPTp reaches XX + # + # # treatment success reaches 1 + # self.sim.modules["Malaria"].parameters["prob_of_treatment_success"] = scaled_params.loc[ + # scaled_params.parameter == "prob_of_treatment_success", "scaleup_value"].value[0] # bednet and ITN coverage @@ -1240,17 +1241,17 @@ def update_parameters(self): # lookup table created in malaria read_parameters # produces self.itn_irs called by malaria poll to draw incidence # need to overwrite this - self.sim.modules["Malaria"].itn_irs['irs_rate'] = treatment_effects.loc[ - treatment_effects.parameter == "irs_district", "no_effect"].values[0] - - # set ITN to 0 for all districts - # itn_district - self.sim.modules["Malaria"].itn_irs['itn_rate'] = treatment_effects.loc[ - treatment_effects.parameter == "itn_district", "no_effect"].values[0] - - # itn rates for 2019 onwards - self.sim.modules["Malaria"].parameters["itn"] = treatment_effects.loc[ - treatment_effects.parameter == "itn", "no_effect"].values[0] + # self.sim.modules["Malaria"].itn_irs['irs_rate'] = treatment_effects.loc[ + # treatment_effects.parameter == "irs_district", "no_effect"].values[0] + # + # # set ITN to 0 for all districts + # # itn_district + # self.sim.modules["Malaria"].itn_irs['itn_rate'] = treatment_effects.loc[ + # treatment_effects.parameter == "itn_district", "no_effect"].values[0] + # + # # itn rates for 2019 onwards + # self.sim.modules["Malaria"].parameters["itn"] = treatment_effects.loc[ + # treatment_effects.parameter == "itn", "no_effect"].values[0] @@ -2432,7 +2433,7 @@ def __init__(self, module): def apply(self, population): - self.module.update_parameters(self) + self.module.update_parameters() # --------------------------------------------------------------------------- diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 0ff501c40c..bfb1abc051 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -596,10 +596,12 @@ def general_population_rdt_scheduler(self, population): # extract annual testing rates from NMCP reports # this is the # rdts issued divided by population size + year = self.sim.date.year if self.sim.date.year <= 2024 else 2024 + test_rates = ( p["rdt_testing_rates"].set_index("Year")["Rate_rdt_testing"].dropna() ) - rdt_rate = test_rates.loc[min(test_rates.index.max(), self.sim.date.year)] / 12 + rdt_rate = test_rates.loc[min(test_rates.index.max(), year)] / 12 # adjust rdt usage reported rate to reflect consumables availability rdt_rate = rdt_rate * p["scaling_factor_for_rdt_availability"] @@ -773,7 +775,7 @@ def check_if_fever_is_caused_by_malaria( Optional arguments are used by the logger, and are not needed in the diagnosis. """ - + # Call the DxTest RDT to diagnose malaria dx_result = diagnosis_function('malaria_rdt') @@ -797,7 +799,7 @@ def check_if_fever_is_caused_by_malaria( return 'clinical_malaria' else: return "negative_malaria_test" - + def do_at_generic_first_appt( self, patient_id: int, From 39cce9b9406ddd0eb5db0234a7a50c1f3401edb2 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 14 May 2024 10:07:26 +0100 Subject: [PATCH 073/131] malaria parameters scale-up included --- src/tlo/methods/hiv.py | 47 +++++++++++++++++--------------------- src/tlo/methods/malaria.py | 1 + 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index cf75247b6d..a22be5f220 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -1216,8 +1216,10 @@ def update_parameters(self): if scaleup_malaria: # scale-up malaria program - # testing reaches XX - prob_malaria_case_tests=0.4 + # increase testing + # prob_malaria_case_tests=0.4 default + p_mal["prob_malaria_case_tests"] = scaled_params.loc[ + scaled_params.parameter == "prob_malaria_case_tests", "scaleup_value"].values[0] # gen pop testing rates # annual Rate_rdt_testing=0.64 at 2023 @@ -1226,38 +1228,31 @@ def update_parameters(self): scaled_params.parameter == "rdt_testing_rates", "scaleup_value"].values[0] - # # treatment reaches XX - # # no default between testing and treatment, governed by tx availability - # - # # coverage IPTp reaches XX - # - # # treatment success reaches 1 - # self.sim.modules["Malaria"].parameters["prob_of_treatment_success"] = scaled_params.loc[ - # scaled_params.parameter == "prob_of_treatment_success", "scaleup_value"].value[0] + # treatment reaches XX + # no default between testing and treatment, governed by tx availability + # coverage IPTp reaches XX + + # treatment success reaches 1 + self.sim.modules["Malaria"].parameters["prob_of_treatment_success"] = scaled_params.loc[ + scaled_params.parameter == "prob_of_treatment_success", "scaleup_value"].values[0] # bednet and ITN coverage - # set IRS to 0 for all districts + # set IRS for all districts # lookup table created in malaria read_parameters # produces self.itn_irs called by malaria poll to draw incidence # need to overwrite this - # self.sim.modules["Malaria"].itn_irs['irs_rate'] = treatment_effects.loc[ - # treatment_effects.parameter == "irs_district", "no_effect"].values[0] - # - # # set ITN to 0 for all districts - # # itn_district - # self.sim.modules["Malaria"].itn_irs['itn_rate'] = treatment_effects.loc[ - # treatment_effects.parameter == "itn_district", "no_effect"].values[0] - # - # # itn rates for 2019 onwards - # self.sim.modules["Malaria"].parameters["itn"] = treatment_effects.loc[ - # treatment_effects.parameter == "itn", "no_effect"].values[0] - - - - + self.sim.modules["Malaria"].itn_irs['irs_rate'] = scaled_params.loc[ + scaled_params.parameter == "irs_district", "scaleup_value"].values[0] + # set ITN or all districts + # itn_district + self.sim.modules["Malaria"].itn_irs['itn_rate'] = scaled_params.loc[ + scaled_params.parameter == "itn_district", "scaleup_value"].values[0] + # itn rates for 2019 onwards + self.sim.modules["Malaria"].parameters["itn"] = scaled_params.loc[ + scaled_params.parameter == "itn", "scaleup_value"].values[0] def on_birth(self, mother_id, child_id): """ diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index bfb1abc051..75fccf8c66 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -591,6 +591,7 @@ def general_population_rdt_scheduler(self, population): rates are set to match rdt usage reports from WHO / NMCP """ df = population.props + df = population.props rng = self.rng p = self.parameters From 15d63bc895da478b914df6129d6d13f349f7a494 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 27 May 2024 21:36:34 +0100 Subject: [PATCH 074/131] set up test for HTM scenario scale-up --- resources/ResourceFile_HIV.xlsx | 4 +- src/tlo/methods/enhanced_lifestyle.py | 2 +- src/tlo/methods/hiv.py | 13 ++- tests/test_HTMscaleup.py | 130 ++++++++++++++++++++++++++ 4 files changed, 139 insertions(+), 10 deletions(-) create mode 100644 tests/test_HTMscaleup.py diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index c6ad16721d..670addd342 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26103cfd265a1d570ad71aff17b8cea55568eb732e17beb83e8780c529bd008a -size 162030 +oid sha256:4374824d53560c242209c1bc9de234ef5f1558b7274652efe1781167e44ad0ba +size 161921 diff --git a/src/tlo/methods/enhanced_lifestyle.py b/src/tlo/methods/enhanced_lifestyle.py index 008424ec2b..ffe1e5bac7 100644 --- a/src/tlo/methods/enhanced_lifestyle.py +++ b/src/tlo/methods/enhanced_lifestyle.py @@ -660,7 +660,7 @@ def update_all_properties(self, df): :param df: The population dataframe """ # get months since last poll now = self.module.sim.date - months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "M")) + months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "m")) # loop through linear models dictionary and initialise each property in the population dataframe for _property_name, _model in self._models.items(): if _model['update'] is not None: diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index a22be5f220..05a64263ae 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -913,12 +913,13 @@ def initialise_simulation(self, sim): sim.schedule_event(HivLoggingEvent(self), sim.date + DateOffset(years=1)) # Optional: Schedule the scale-up of programs - assert isinstance(self.scaleup_start_date, Date), "Value is not a Date object" - # Check if scale-up start date is on or after sim start date - assert self.scaleup_start_date >= Date(2010, 1, 1), \ - f"Date {self.scaleup_start_date} is before January 1, 2010" + if self.scaleup_start_date is not pd.NaT: + assert isinstance(self.scaleup_start_date, Date), "Value is not a Date object" + # Check if scale-up start date is on or after sim start date + assert self.scaleup_start_date >= Date(2010, 1, 1), \ + f"Date {self.scaleup_start_date} is before January 1, 2010" - sim.schedule_event(ScaleUpSetupEvent(self), self.scaleup_start_date) + sim.schedule_event(ScaleUpSetupEvent(self), self.scaleup_start_date) # 3) Determine who has AIDS and impose the Symptoms 'aids_symptoms' @@ -1135,8 +1136,6 @@ def update_parameters(self): scaleup_tb = self.scaleup_tb scaleup_malaria = self.scaleup_malaria - print(scaleup_malaria) - if scaleup_hiv: # scale-up HIV program diff --git a/tests/test_HTMscaleup.py b/tests/test_HTMscaleup.py new file mode 100644 index 0000000000..e0d368cc6a --- /dev/null +++ b/tests/test_HTMscaleup.py @@ -0,0 +1,130 @@ +""" Tests for setting up the HIV, TB and malaria scenarios used for projections """ + +import os +from pathlib import Path + +import pandas as pd +import pytest + +from tlo import Date, Simulation +from tlo.methods import ( + demography, + enhanced_lifestyle, + epi, + healthburden, + healthseekingbehaviour, + healthsystem, + hiv, + malaria, + simplified_births, + symptommanager, + tb, +) + +try: + resourcefilepath = Path(os.path.dirname(__file__)) / "../resources" +except NameError: + # running interactively + resourcefilepath = "resources" + + +def get_sim(seed, scaleup_hiv=False, scaleup_tb=False, scaleup_malaria=False, scaleup_start_date=pd.NaT): + """ + register all necessary modules for the tests to run + """ + + start_date = Date(2010, 1, 1) + sim = Simulation(start_date=start_date, seed=seed) + + # Register the appropriate modules + sim.register( + demography.Demography(resourcefilepath=resourcefilepath), + simplified_births.SimplifiedBirths(resourcefilepath=resourcefilepath), + enhanced_lifestyle.Lifestyle(resourcefilepath=resourcefilepath), + healthsystem.HealthSystem( + resourcefilepath=resourcefilepath, + service_availability=["*"], # all treatment allowed + mode_appt_constraints=1, # mode of constraints to do with officer numbers and time + cons_availability="default", # mode for consumable constraints (if ignored, all consumables available) + ignore_priority=True, # do not use the priority information in HSI event to schedule + capabilities_coefficient=1.0, # multiplier for the capabilities of health officers + disable=False, # disables the healthsystem (no constraints and no logging) and every HSI runs + disable_and_reject_all=False, # disable healthsystem and no HSI runs + ), + symptommanager.SymptomManager(resourcefilepath=resourcefilepath), + healthseekingbehaviour.HealthSeekingBehaviour(resourcefilepath=resourcefilepath), + healthburden.HealthBurden(resourcefilepath=resourcefilepath), + epi.Epi(resourcefilepath=resourcefilepath), + hiv.Hiv(resourcefilepath=resourcefilepath, + scaleup_hiv=scaleup_hiv, + scaleup_tb=scaleup_tb, + scaleup_malaria=scaleup_malaria, + scaleup_start_date=scaleup_start_date), + tb.Tb(resourcefilepath=resourcefilepath), + malaria.Malaria(resourcefilepath=resourcefilepath), + ) + + return sim + + +def test_hiv_scale_up(seed): + """ test hiv program scale-up changes parameters correctly + and on correct date """ + + workbook = pd.read_excel( + os.path.join(resourcefilepath, "ResourceFile_HIV.xlsx"), + sheet_name=None, + ) + + # Load data on HIV prevalence + original_params = workbook["parameters"] + new_params = workbook["scaleup_parameters"] + scaleup_start_date = Date(2011, 1, 1) + + popsize = 100 + + sim = get_sim(seed=seed, scaleup_hiv=True, scaleup_start_date=scaleup_start_date) + + # check initial parameters + # todo do we need to be exhaustive and check every parameter here? + assert sim.modules["Hiv"].parameters["beta"] == \ + original_params.loc[original_params.parameter_name == "beta", "value"].values[0] + assert sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] == original_params.loc[ + original_params.parameter_name == "prob_prep_for_fsw_after_hiv_test", "value"].values[0] + assert sim.modules["Hiv"].parameters["prob_prep_for_agyw"] == original_params.loc[ + original_params.parameter_name == "prob_prep_for_agyw", "value"].values[0] + assert sim.modules["Hiv"].parameters["probability_of_being_retained_on_prep_every_3_months"] == original_params.loc[ + original_params.parameter_name == "probability_of_being_retained_on_prep_every_3_months", "value"].values[0] + assert sim.modules["Hiv"].parameters["prob_circ_after_hiv_test"] == original_params.loc[ + original_params.parameter_name == "prob_circ_after_hiv_test", "value"].values[0] + + # Make the population + sim.make_initial_population(n=popsize) + sim.simulate(end_date=scaleup_start_date + pd.DateOffset(days=1)) + + # check HIV parameters changed + assert sim.modules["Hiv"].parameters["beta"] < original_params.loc[original_params.parameter_name == "beta", "value"].values[0] + assert sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] == new_params.loc[ + new_params.parameter == "prob_prep_for_fsw_after_hiv_test", "scaleup_value"].values[0] + assert sim.modules["Hiv"].parameters["prob_prep_for_agyw"] == new_params.loc[ + new_params.parameter == "prob_prep_for_agyw", "scaleup_value"].values[0] + assert sim.modules["Hiv"].parameters["probability_of_being_retained_on_prep_every_3_months"] == new_params.loc[ + new_params.parameter == "probability_of_being_retained_on_prep_every_3_months", "scaleup_value"].values[0] + assert sim.modules["Hiv"].parameters["prob_circ_after_hiv_test"] == new_params.loc[ + new_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] + + # check TB and malaria parameters unchanged + mal_workbook = pd.read_excel( + os.path.join(resourcefilepath, "malaria/ResourceFile_Malaria.xlsx"), + sheet_name=None, + ) + mal_original_params = mal_workbook["parameters"] + mal_rdt_testing = mal_workbook["WHO_TestData2023"] + + assert sim.modules["Malaria"].parameters["prob_malaria_case_tests"] == mal_original_params.loc[ + mal_original_params.parameter_name == "prob_malaria_case_tests", "value"].values[0] + pd.testing.assert_series_equal(sim.modules["Malaria"].parameters["rdt_testing_rates"]["Rate_rdt_testing"], + mal_rdt_testing["Rate_rdt_testing"]) + + + From e8699b803e043f21de76da616b93c808b27f0e96 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 28 May 2024 15:32:42 +0100 Subject: [PATCH 075/131] add tests for HTM scale-up --- resources/ResourceFile_HIV.xlsx | 4 +- resources/malaria/ResourceFile_malaria.xlsx | 4 +- src/tlo/methods/hiv.py | 21 +++- src/tlo/methods/malaria.py | 3 + tests/test_HTMscaleup.py | 125 ++++++++++++++++++-- 5 files changed, 134 insertions(+), 23 deletions(-) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index 670addd342..841d0f10ff 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4374824d53560c242209c1bc9de234ef5f1558b7274652efe1781167e44ad0ba -size 161921 +oid sha256:e2e350ab8e94335c48220455cd573386b67064675ec4ebe126a345234aeb70d5 +size 161944 diff --git a/resources/malaria/ResourceFile_malaria.xlsx b/resources/malaria/ResourceFile_malaria.xlsx index 8ea15f92f6..751ce04876 100644 --- a/resources/malaria/ResourceFile_malaria.xlsx +++ b/resources/malaria/ResourceFile_malaria.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:924528f79e315c390b940881944e4756bbbba60ad05f6bbc925b0c1a10977d9b -size 67588 +oid sha256:d9cb6ef00e472cf0951585fdbc2d6b3398ef7ef7f16b126cc435d67df42189f2 +size 68529 diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 05a64263ae..ee73fa9224 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -1226,26 +1226,35 @@ def update_parameters(self): scaled_params.loc[ scaled_params.parameter == "rdt_testing_rates", "scaleup_value"].values[0] - # treatment reaches XX # no default between testing and treatment, governed by tx availability # coverage IPTp reaches XX + # given during ANC visits and MalariaIPTp Event which selects ALL eligible women - # treatment success reaches 1 + # treatment success reaches 1 - default is currently 1 also self.sim.modules["Malaria"].parameters["prob_of_treatment_success"] = scaled_params.loc[ scaled_params.parameter == "prob_of_treatment_success", "scaleup_value"].values[0] # bednet and ITN coverage - # set IRS for all districts + # set IRS for 4 high-risk districts # lookup table created in malaria read_parameters # produces self.itn_irs called by malaria poll to draw incidence # need to overwrite this - self.sim.modules["Malaria"].itn_irs['irs_rate'] = scaled_params.loc[ + highrisk_distr_num = self.sim.modules["Malaria"].parameters["highrisk_districts"]["district_num"] + + # Find indices where District_Num is in highrisk_distr_num + mask = self.sim.modules["Malaria"].itn_irs['irs_rate'].index.get_level_values('District_Num').isin( + highrisk_distr_num) + + # IRS values can be 0 or 0.8 - no other value in lookup table + self.sim.modules["Malaria"].itn_irs['irs_rate'].loc[mask] = scaled_params.loc[ scaled_params.parameter == "irs_district", "scaleup_value"].values[0] - # set ITN or all districts - # itn_district + # set ITN for all districts + # Set these values to 0.7 - this is the max value possible in lookup table + # equivalent to 0.7 of all pop sleeping under bednet + # household coverage could be 100%, but not everyone in household sleeping under bednet self.sim.modules["Malaria"].itn_irs['itn_rate'] = scaled_params.loc[ scaled_params.parameter == "itn_district", "scaleup_value"].values[0] diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 75fccf8c66..06542676df 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -180,6 +180,8 @@ def __init__(self, name=None, resourcefilepath=None): "prob_of_treatment_success": Parameter( Types.REAL, "probability malaria treatment cures and clears parasitaemia" ), + "highrisk_districts": Parameter(Types.LIST, "list of four malaria high-risk districts" + ), } PROPERTIES = { @@ -234,6 +236,7 @@ def read_parameters(self, data_folder): p["sev_symp_prob"] = workbook["severe_symptoms"] p["rdt_testing_rates"] = workbook["WHO_TestData2023"] + p["highrisk_districts"] = workbook["highrisk_districts"] p["inf_inc"] = pd.read_csv(self.resourcefilepath / "malaria" / "ResourceFile_malaria_InfInc_expanded.csv") p["clin_inc"] = pd.read_csv(self.resourcefilepath / "malaria" / "ResourceFile_malaria_ClinInc_expanded.csv") diff --git a/tests/test_HTMscaleup.py b/tests/test_HTMscaleup.py index e0d368cc6a..322b99006b 100644 --- a/tests/test_HTMscaleup.py +++ b/tests/test_HTMscaleup.py @@ -4,7 +4,6 @@ from pathlib import Path import pandas as pd -import pytest from tlo import Date, Simulation from tlo.methods import ( @@ -67,6 +66,27 @@ def get_sim(seed, scaleup_hiv=False, scaleup_tb=False, scaleup_malaria=False, sc return sim +def check_initial_params(sim): + + original_params = pd.read_excel( + os.path.join(resourcefilepath, "ResourceFile_HIV.xlsx"), + sheet_name="parameters", + ) + + # todo do we need to be exhaustive and check every parameter here? + # check initial parameters + assert sim.modules["Hiv"].parameters["beta"] == \ + original_params.loc[original_params.parameter_name == "beta", "value"].values[0] + assert sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] == original_params.loc[ + original_params.parameter_name == "prob_prep_for_fsw_after_hiv_test", "value"].values[0] + assert sim.modules["Hiv"].parameters["prob_prep_for_agyw"] == original_params.loc[ + original_params.parameter_name == "prob_prep_for_agyw", "value"].values[0] + assert sim.modules["Hiv"].parameters["probability_of_being_retained_on_prep_every_3_months"] == original_params.loc[ + original_params.parameter_name == "probability_of_being_retained_on_prep_every_3_months", "value"].values[0] + assert sim.modules["Hiv"].parameters["prob_circ_after_hiv_test"] == original_params.loc[ + original_params.parameter_name == "prob_circ_after_hiv_test", "value"].values[0] + + def test_hiv_scale_up(seed): """ test hiv program scale-up changes parameters correctly and on correct date """ @@ -86,17 +106,7 @@ def test_hiv_scale_up(seed): sim = get_sim(seed=seed, scaleup_hiv=True, scaleup_start_date=scaleup_start_date) # check initial parameters - # todo do we need to be exhaustive and check every parameter here? - assert sim.modules["Hiv"].parameters["beta"] == \ - original_params.loc[original_params.parameter_name == "beta", "value"].values[0] - assert sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] == original_params.loc[ - original_params.parameter_name == "prob_prep_for_fsw_after_hiv_test", "value"].values[0] - assert sim.modules["Hiv"].parameters["prob_prep_for_agyw"] == original_params.loc[ - original_params.parameter_name == "prob_prep_for_agyw", "value"].values[0] - assert sim.modules["Hiv"].parameters["probability_of_being_retained_on_prep_every_3_months"] == original_params.loc[ - original_params.parameter_name == "probability_of_being_retained_on_prep_every_3_months", "value"].values[0] - assert sim.modules["Hiv"].parameters["prob_circ_after_hiv_test"] == original_params.loc[ - original_params.parameter_name == "prob_circ_after_hiv_test", "value"].values[0] + check_initial_params(sim) # Make the population sim.make_initial_population(n=popsize) @@ -113,7 +123,7 @@ def test_hiv_scale_up(seed): assert sim.modules["Hiv"].parameters["prob_circ_after_hiv_test"] == new_params.loc[ new_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] - # check TB and malaria parameters unchanged + # check malaria parameters unchanged mal_workbook = pd.read_excel( os.path.join(resourcefilepath, "malaria/ResourceFile_Malaria.xlsx"), sheet_name=None, @@ -126,5 +136,94 @@ def test_hiv_scale_up(seed): pd.testing.assert_series_equal(sim.modules["Malaria"].parameters["rdt_testing_rates"]["Rate_rdt_testing"], mal_rdt_testing["Rate_rdt_testing"]) + # all irs coverage levels should be < 1.0 + assert sim.modules["Malaria"].itn_irs['irs_rate'].all() < 1.0 + # itn rates for 2019 onwards + assert sim.modules["Malaria"].parameters["itn"] == mal_original_params.loc[ + mal_original_params.parameter_name == "itn", "value"].values[0] + + # check tb parameters unchanged + tb_workbook = pd.read_excel( + os.path.join(resourcefilepath, "ResourceFile_TB.xlsx"), + sheet_name=None, + ) + tb_original_params = tb_workbook["parameters"] + tb_testing = tb_workbook["NTP2019"] + + pd.testing.assert_series_equal(sim.modules["Tb"].parameters["rate_testing_active_tb"]["treatment_coverage"], + tb_testing["treatment_coverage"]) + assert sim.modules["Tb"].parameters["prob_tx_success_ds"] == tb_original_params.loc[ + tb_original_params.parameter_name == "prob_tx_success_ds", "value"].values[0] + assert sim.modules["Tb"].parameters["prob_tx_success_mdr"] == tb_original_params.loc[ + tb_original_params.parameter_name == "prob_tx_success_mdr", "value"].values[0] + assert sim.modules["Tb"].parameters["prob_tx_success_0_4"] == tb_original_params.loc[ + tb_original_params.parameter_name == "prob_tx_success_0_4", "value"].values[0] + assert sim.modules["Tb"].parameters["prob_tx_success_5_14"] == tb_original_params.loc[ + tb_original_params.parameter_name == "prob_tx_success_5_14", "value"].values[0] + assert sim.modules["Tb"].parameters["first_line_test"] == tb_original_params.loc[ + tb_original_params.parameter_name == "first_line_test", "value"].values[0] + + +def test_htm_scale_up(seed): + """ test hiv/tb/malaria program scale-up changes parameters correctly + and on correct date """ + + workbook = pd.read_excel( + os.path.join(resourcefilepath, "ResourceFile_HIV.xlsx"), + sheet_name=None, + ) + + # Load data on HIV prevalence + original_params = workbook["parameters"] + new_params = workbook["scaleup_parameters"] + scaleup_start_date = Date(2011, 1, 1) + + popsize = 100 + + sim = get_sim(seed=seed, scaleup_hiv=True, scaleup_tb=True, scaleup_malaria=True, + scaleup_start_date=scaleup_start_date) + + # check initial parameters + check_initial_params(sim) + + # Make the population + sim.make_initial_population(n=popsize) + sim.simulate(end_date=scaleup_start_date + pd.DateOffset(days=1)) + + # check HIV parameters changed + assert sim.modules["Hiv"].parameters["beta"] < original_params.loc[original_params.parameter_name == "beta", "value"].values[0] + assert sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] == new_params.loc[ + new_params.parameter == "prob_prep_for_fsw_after_hiv_test", "scaleup_value"].values[0] + assert sim.modules["Hiv"].parameters["prob_prep_for_agyw"] == new_params.loc[ + new_params.parameter == "prob_prep_for_agyw", "scaleup_value"].values[0] + assert sim.modules["Hiv"].parameters["probability_of_being_retained_on_prep_every_3_months"] == new_params.loc[ + new_params.parameter == "probability_of_being_retained_on_prep_every_3_months", "scaleup_value"].values[0] + assert sim.modules["Hiv"].parameters["prob_circ_after_hiv_test"] == new_params.loc[ + new_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] + # check malaria parameters changed + assert sim.modules["Malaria"].parameters["prob_malaria_case_tests"] == new_params.loc[ + new_params.parameter == "prob_malaria_case_tests", "scaleup_value"].values[0] + assert sim.modules["Malaria"].parameters["rdt_testing_rates"]["Rate_rdt_testing"].eq(new_params.loc[ + new_params.parameter == "rdt_testing_rates", "scaleup_value"].values[0]).all() + + # some irs coverage levels should now = 1.0 + assert sim.modules["Malaria"].itn_irs['irs_rate'].any() == 1.0 + # itn rates for 2019 onwards + assert sim.modules["Malaria"].parameters["itn"] == new_params.loc[ + new_params.parameter == "itn", "scaleup_value"].values[0] + + # check tb parameters changed + assert sim.modules["Tb"].parameters["rate_testing_active_tb"]["treatment_coverage"].eq(new_params.loc[ + new_params.parameter == "tb_treatment_coverage", "scaleup_value"].values[0]).all() + assert sim.modules["Tb"].parameters["prob_tx_success_ds"] == new_params.loc[ + new_params.parameter == "tb_prob_tx_success_ds", "scaleup_value"].values[0] + assert sim.modules["Tb"].parameters["prob_tx_success_mdr"] == new_params.loc[ + new_params.parameter == "tb_prob_tx_success_mdr", "scaleup_value"].values[0] + assert sim.modules["Tb"].parameters["prob_tx_success_0_4"] == new_params.loc[ + new_params.parameter == "tb_prob_tx_success_0_4", "scaleup_value"].values[0] + assert sim.modules["Tb"].parameters["prob_tx_success_5_14"] == new_params.loc[ + new_params.parameter == "tb_prob_tx_success_5_14", "scaleup_value"].values[0] + assert sim.modules["Tb"].parameters["first_line_test"] == new_params.loc[ + new_params.parameter == "first_line_test", "scaleup_value"].values[0] From 89435ad04129dcf8967916d0b5fd1cbbdc25b64c Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 28 May 2024 15:33:12 +0100 Subject: [PATCH 076/131] check resourcefiles updated --- resources/ResourceFile_TB.xlsx | 4 ++-- resources/malaria/ResourceFile_malaria.xlsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index 78733c27de..21bf5ac66c 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5aac2da9963a0c05e0929b32dfa3faf87fb790cdc68be8dd4bb34d3597702212 -size 55618 +oid sha256:ccb3e12476fd218e7c460de4ec9ea289dae447ace28b7dd1b0803347091f7690 +size 55619 diff --git a/resources/malaria/ResourceFile_malaria.xlsx b/resources/malaria/ResourceFile_malaria.xlsx index 751ce04876..b7fb10682f 100644 --- a/resources/malaria/ResourceFile_malaria.xlsx +++ b/resources/malaria/ResourceFile_malaria.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d9cb6ef00e472cf0951585fdbc2d6b3398ef7ef7f16b126cc435d67df42189f2 -size 68529 +oid sha256:d120e4a196a1907c995b712f77c0f5bb16e8dd6ea24ecb7d27157e4b6afb0bc5 +size 68549 From 1f261b98b77506b43ea45436caff113db8e64924 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 28 May 2024 15:40:50 +0100 Subject: [PATCH 077/131] check the usage of '' versus "" --- src/tlo/methods/malaria.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 06542676df..0fdd8b1f41 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -781,7 +781,7 @@ def check_if_fever_is_caused_by_malaria( """ # Call the DxTest RDT to diagnose malaria - dx_result = diagnosis_function('malaria_rdt') + dx_result = diagnosis_function("malaria_rdt") # Log the test: line-list of summary information about each test logger.info( @@ -797,10 +797,10 @@ def check_if_fever_is_caused_by_malaria( ) # Severe malaria infection always returns positive RDT - if true_malaria_infection_type == 'severe': - return 'severe_malaria' - elif dx_result and true_malaria_infection_type in ('clinical', 'asym'): - return 'clinical_malaria' + if true_malaria_infection_type == "severe": + return "severe_malaria" + elif dx_result and true_malaria_infection_type in ("clinical", "asym"): + return "clinical_malaria" else: return "negative_malaria_test" @@ -844,7 +844,7 @@ def do_at_generic_first_appt( event, priority=0, topen=self.sim.date ) - # return type 'clinical_malaria' includes asymptomatic infection + # return type "clinical_malaria" includes asymptomatic infection elif malaria_test_result == "clinical_malaria": patient_details_updates["ma_dx_counter"] = patient_details.ma_dx_counter + 1 event = HSI_Malaria_Treatment(person_id=patient_id, module=self) @@ -869,8 +869,8 @@ def do_at_generic_first_appt_emergency( # symptoms of severe malaria.) patient_details_updates = {} - if 'severe_malaria' in symptoms: - if patient_details.ma_tx == 'none': + if "severe_malaria" in symptoms: + if patient_details.ma_tx == "none": # Check if malaria parasitaemia: malaria_test_result = self.check_if_fever_is_caused_by_malaria( true_malaria_infection_type=patient_details.ma_inf_type, @@ -883,8 +883,8 @@ def do_at_generic_first_appt_emergency( ) # if any symptoms indicative of malaria and they have parasitaemia (would return a positive rdt) - if malaria_test_result in ('severe_malaria', 'clinical_malaria'): - patient_details_updates['ma_dx_counter'] = patient_details.ma_dx_counter + 1 + if malaria_test_result in ("severe_malaria", "clinical_malaria"): + patient_details_updates["ma_dx_counter"] = patient_details.ma_dx_counter + 1 # Launch the HSI for treatment for Malaria, HSI_Malaria_Treatment will determine correct treatment event = HSI_Malaria_Treatment_Complicated( From ac58f0a9a159e3de989dc29ceb51c825b5d96823 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 28 May 2024 16:25:47 +0100 Subject: [PATCH 078/131] reset to single quotes to match PR #1273 --- src/tlo/methods/malaria.py | 1086 ++++++++++++++++++------------------ 1 file changed, 543 insertions(+), 543 deletions(-) diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 0fdd8b1f41..03327d9689 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -46,13 +46,13 @@ def __init__(self, name=None, resourcefilepath=None): self.lm = dict() INIT_DEPENDENCIES = { - "Contraception", - "Demography", - "HealthSystem", - "SymptomManager", + 'Contraception', + 'Demography', + 'HealthSystem', + 'SymptomManager', } - OPTIONAL_INIT_DEPENDENCIES = {"HealthBurden"} + OPTIONAL_INIT_DEPENDENCIES = {'HealthBurden'} METADATA = { Metadata.DISEASE_MODULE, @@ -63,265 +63,265 @@ def __init__(self, name=None, resourcefilepath=None): # Declare Causes of Death CAUSES_OF_DEATH = { - "Malaria": Cause(gbd_causes="Malaria", label="Malaria"), + 'Malaria': Cause(gbd_causes='Malaria', label='Malaria'), } # Declare Causes of Disability - CAUSES_OF_DISABILITY = {"Malaria": Cause(gbd_causes="Malaria", label="Malaria")} + CAUSES_OF_DISABILITY = {'Malaria': Cause(gbd_causes='Malaria', label='Malaria')} PARAMETERS = { - "interv": Parameter(Types.REAL, "data frame of intervention coverage by year"), - "clin_inc": Parameter( + 'interv': Parameter(Types.REAL, 'data frame of intervention coverage by year'), + 'clin_inc': Parameter( Types.REAL, - "data frame of clinical incidence by age, district, intervention coverage", + 'data frame of clinical incidence by age, district, intervention coverage', ), - "inf_inc": Parameter( + 'inf_inc': Parameter( Types.REAL, - "data frame of infection incidence by age, district, intervention coverage", + 'data frame of infection incidence by age, district, intervention coverage', ), - "sev_inc": Parameter( + 'sev_inc': Parameter( Types.REAL, - "data frame of severe case incidence by age, district, intervention coverage", + 'data frame of severe case incidence by age, district, intervention coverage', ), - "itn_district": Parameter( - Types.REAL, "data frame of ITN usage rates by district" + 'itn_district': Parameter( + Types.REAL, 'data frame of ITN usage rates by district' ), - "irs_district": Parameter( - Types.REAL, "data frame of IRS usage rates by district" + 'irs_district': Parameter( + Types.REAL, 'data frame of IRS usage rates by district' ), - "sev_symp_prob": Parameter( - Types.REAL, "probabilities of each symptom for severe malaria cases" + 'sev_symp_prob': Parameter( + Types.REAL, 'probabilities of each symptom for severe malaria cases' ), - "sensitivity_rdt": Parameter(Types.REAL, "Sensitivity of rdt"), - "cfr": Parameter(Types.REAL, "case-fatality rate for severe malaria"), - "dur_asym": Parameter(Types.REAL, "duration (days) of asymptomatic malaria"), - "dur_clin": Parameter( - Types.REAL, "duration (days) of clinical symptoms of malaria" + 'sensitivity_rdt': Parameter(Types.REAL, 'Sensitivity of rdt'), + 'cfr': Parameter(Types.REAL, 'case-fatality rate for severe malaria'), + 'dur_asym': Parameter(Types.REAL, 'duration (days) of asymptomatic malaria'), + 'dur_clin': Parameter( + Types.REAL, 'duration (days) of clinical symptoms of malaria' ), - "dur_clin_para": Parameter( - Types.REAL, "duration (days) of parasitaemia for clinical malaria cases" + 'dur_clin_para': Parameter( + Types.REAL, 'duration (days) of parasitaemia for clinical malaria cases' ), - "treatment_adjustment": Parameter( - Types.REAL, "probability of death from severe malaria if on treatment" + 'treatment_adjustment': Parameter( + Types.REAL, 'probability of death from severe malaria if on treatment' ), - "p_sev_anaemia_preg": Parameter( + 'p_sev_anaemia_preg': Parameter( Types.REAL, - "probability of severe anaemia in pregnant women with clinical malaria", + 'probability of severe anaemia in pregnant women with clinical malaria', ), - "itn_proj": Parameter( - Types.REAL, "coverage of ITN for projections 2020 onwards" + 'itn_proj': Parameter( + Types.REAL, 'coverage of ITN for projections 2020 onwards' ), - "mortality_adjust": Parameter( - Types.REAL, "adjustment of case-fatality rate to match WHO/MAP" + 'mortality_adjust': Parameter( + Types.REAL, 'adjustment of case-fatality rate to match WHO/MAP' ), - "data_end": Parameter( + 'data_end': Parameter( Types.REAL, - "final year of ICL malaria model outputs, after 2018 = projections", + 'final year of ICL malaria model outputs, after 2018 = projections', ), - "irs_rates_boundary": Parameter( - Types.REAL, "threshold for indoor residual spraying coverage" + 'irs_rates_boundary': Parameter( + Types.REAL, 'threshold for indoor residual spraying coverage' ), - "irs_rates_upper": Parameter( - Types.REAL, "indoor residual spraying high coverage" + 'irs_rates_upper': Parameter( + Types.REAL, 'indoor residual spraying high coverage' ), - "irs_rates_lower": Parameter( - Types.REAL, "indoor residual spraying low coverage" + 'irs_rates_lower': Parameter( + Types.REAL, 'indoor residual spraying low coverage' ), - "prob_malaria_case_tests": Parameter( - Types.REAL, "probability that a malaria case will have a scheduled rdt" + 'prob_malaria_case_tests': Parameter( + Types.REAL, 'probability that a malaria case will have a scheduled rdt' ), - "itn": Parameter(Types.REAL, "projected future itn coverage"), - "rdt_testing_rates": Parameter( + 'itn': Parameter(Types.REAL, 'projected future itn coverage'), + 'rdt_testing_rates': Parameter( Types.REAL, - "per capita rdt testing rate of general population", + 'per capita rdt testing rate of general population', ), - "scaling_factor_for_rdt_availability": Parameter( + 'scaling_factor_for_rdt_availability': Parameter( Types.REAL, - "scaling factor applied to the reports of rdt usage to compensate for" - "non-availability of rdts at some facilities", + 'scaling factor applied to the reports of rdt usage to compensate for' + 'non-availability of rdts at some facilities', ), - "duration_iptp_protection_weeks": Parameter( + 'duration_iptp_protection_weeks': Parameter( Types.REAL, - "duration of protection against clinical malaria conferred by each dose of IPTp", + 'duration of protection against clinical malaria conferred by each dose of IPTp', ), - "rr_clinical_malaria_hiv_under5": Parameter( + 'rr_clinical_malaria_hiv_under5': Parameter( Types.REAL, - "relative risk of clinical malaria if HIV+ and aged under 5 years", + 'relative risk of clinical malaria if HIV+ and aged under 5 years', ), - "rr_clinical_malaria_hiv_over5": Parameter( + 'rr_clinical_malaria_hiv_over5': Parameter( Types.REAL, - "relative risk of clinical malaria if HIV+ and aged over 5 years", + 'relative risk of clinical malaria if HIV+ and aged over 5 years', ), - "rr_clinical_malaria_hiv_pregnant": Parameter( - Types.REAL, "relative risk of clinical malaria if HIV+ and pregnant" + 'rr_clinical_malaria_hiv_pregnant': Parameter( + Types.REAL, 'relative risk of clinical malaria if HIV+ and pregnant' ), - "rr_clinical_malaria_cotrimoxazole": Parameter( - Types.REAL, "relative risk of clinical malaria if on cotrimoxazole" + 'rr_clinical_malaria_cotrimoxazole': Parameter( + Types.REAL, 'relative risk of clinical malaria if on cotrimoxazole' ), - "rr_clinical_malaria_art": Parameter( + 'rr_clinical_malaria_art': Parameter( Types.REAL, - "relative risk of clinical malaria if HIV+ and on ART and virally suppressed", + 'relative risk of clinical malaria if HIV+ and on ART and virally suppressed', ), - "rr_clinical_malaria_iptp": Parameter( - Types.REAL, "relative risk of clinical malaria with each dose of IPTp" + 'rr_clinical_malaria_iptp': Parameter( + Types.REAL, 'relative risk of clinical malaria with each dose of IPTp' ), - "rr_severe_malaria_hiv_under5": Parameter( - Types.REAL, "relative risk of severe malaria if HIV+ and aged under 5 years" + 'rr_severe_malaria_hiv_under5': Parameter( + Types.REAL, 'relative risk of severe malaria if HIV+ and aged under 5 years' ), - "rr_severe_malaria_hiv_over5": Parameter( - Types.REAL, "relative risk of severe malaria if HIV+ and aged over 5 years" + 'rr_severe_malaria_hiv_over5': Parameter( + Types.REAL, 'relative risk of severe malaria if HIV+ and aged over 5 years' ), - "rr_severe_malaria_hiv_pregnant": Parameter( - Types.REAL, "relative risk of clinical malaria if HIV+ and pregnant" + 'rr_severe_malaria_hiv_pregnant': Parameter( + Types.REAL, 'relative risk of clinical malaria if HIV+ and pregnant' ), - "rr_severe_malaria_iptp": Parameter( - Types.REAL, "relative risk of severe malaria with each dose of IPTp" + 'rr_severe_malaria_iptp': Parameter( + Types.REAL, 'relative risk of severe malaria with each dose of IPTp' ), - "prob_of_treatment_success": Parameter( - Types.REAL, "probability malaria treatment cures and clears parasitaemia" + 'prob_of_treatment_success': Parameter( + Types.REAL, 'probability malaria treatment cures and clears parasitaemia' ), - "highrisk_districts": Parameter(Types.LIST, "list of four malaria high-risk districts" + 'highrisk_districts': Parameter(Types.LIST, 'list of four malaria high-risk districts' ), } PROPERTIES = { - "ma_is_infected": Property( - Types.BOOL, "Current status of malaria, infected with malaria parasitaemia" + 'ma_is_infected': Property( + Types.BOOL, 'Current status of malaria, infected with malaria parasitaemia' ), - "ma_date_infected": Property(Types.DATE, "Date of latest infection"), - "ma_date_symptoms": Property( - Types.DATE, "Date of symptom start for clinical infection" + 'ma_date_infected': Property(Types.DATE, 'Date of latest infection'), + 'ma_date_symptoms': Property( + Types.DATE, 'Date of symptom start for clinical infection' ), - "ma_date_death": Property(Types.DATE, "Date of death due to malaria"), - "ma_tx": Property( + 'ma_date_death': Property(Types.DATE, 'Date of death due to malaria'), + 'ma_tx': Property( Types.CATEGORICAL, - "Type of anti-malarial treatment person is currently using", - categories=["none", "uncomplicated", "complicated"], + 'Type of anti-malarial treatment person is currently using', + categories=['none', 'uncomplicated', 'complicated'], ), - "ma_date_tx": Property( - Types.DATE, "Date treatment started for most recent malaria episode" + 'ma_date_tx': Property( + Types.DATE, 'Date treatment started for most recent malaria episode' ), - "ma_inf_type": Property( + 'ma_inf_type': Property( Types.CATEGORICAL, - "specific symptoms with malaria infection", - categories=["none", "asym", "clinical", "severe"], + 'specific symptoms with malaria infection', + categories=['none', 'asym', 'clinical', 'severe'], ), - "ma_age_edited": Property( - Types.REAL, "age values redefined to match with malaria data" + 'ma_age_edited': Property( + Types.REAL, 'age values redefined to match with malaria data' ), - "ma_clinical_counter": Property( - Types.INT, "annual counter for malaria clinical episodes" + 'ma_clinical_counter': Property( + Types.INT, 'annual counter for malaria clinical episodes' ), - "ma_dx_counter": Property(Types.INT, "annual counter for malaria diagnoses"), - "ma_tx_counter": Property( - Types.INT, "annual counter for malaria treatment episodes" + 'ma_dx_counter': Property(Types.INT, 'annual counter for malaria diagnoses'), + 'ma_tx_counter': Property( + Types.INT, 'annual counter for malaria treatment episodes' ), - "ma_clinical_preg_counter": Property( - Types.INT, "annual counter for malaria clinical episodes in pregnant women" + 'ma_clinical_preg_counter': Property( + Types.INT, 'annual counter for malaria clinical episodes in pregnant women' ), - "ma_iptp": Property(Types.BOOL, "if woman has IPTp in current pregnancy"), + 'ma_iptp': Property(Types.BOOL, 'if woman has IPTp in current pregnancy'), } def read_parameters(self, data_folder): - workbook = pd.read_excel(self.resourcefilepath / "malaria" / "ResourceFile_malaria.xlsx", sheet_name=None) + workbook = pd.read_excel(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria.xlsx', sheet_name=None) - self.load_parameters_from_dataframe(workbook["parameters"]) + self.load_parameters_from_dataframe(workbook['parameters']) p = self.parameters # baseline characteristics - p["interv"] = workbook["interventions"] - p["itn_district"] = workbook["MAP_ITNrates"] - p["irs_district"] = workbook["MAP_IRSrates"] + p['interv'] = workbook['interventions'] + p['itn_district'] = workbook['MAP_ITNrates'] + p['irs_district'] = workbook['MAP_IRSrates'] - p["sev_symp_prob"] = workbook["severe_symptoms"] - p["rdt_testing_rates"] = workbook["WHO_TestData2023"] - p["highrisk_districts"] = workbook["highrisk_districts"] + p['sev_symp_prob'] = workbook['severe_symptoms'] + p['rdt_testing_rates'] = workbook['WHO_TestData2023'] + p['highrisk_districts'] = workbook['highrisk_districts'] - p["inf_inc"] = pd.read_csv(self.resourcefilepath / "malaria" / "ResourceFile_malaria_InfInc_expanded.csv") - p["clin_inc"] = pd.read_csv(self.resourcefilepath / "malaria" / "ResourceFile_malaria_ClinInc_expanded.csv") - p["sev_inc"] = pd.read_csv(self.resourcefilepath / "malaria" / "ResourceFile_malaria_SevInc_expanded.csv") + p['inf_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_InfInc_expanded.csv') + p['clin_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_ClinInc_expanded.csv') + p['sev_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_SevInc_expanded.csv') # check itn projected values are <=0.7 and rounded to 1dp for matching to incidence tables - p["itn"] = round(p["itn"], 1) - assert p["itn"] <= 0.7 + p['itn'] = round(p['itn'], 1) + assert p['itn'] <= 0.7 # =============================================================================== # single dataframe for itn and irs district/year data; set index for fast lookup # =============================================================================== - itn_curr = p["itn_district"] - itn_curr.rename(columns={"itn_rates": "itn_rate"}, inplace=True) - itn_curr["itn_rate"] = itn_curr["itn_rate"].round(decimals=1) + itn_curr = p['itn_district'] + itn_curr.rename(columns={'itn_rates': 'itn_rate'}, inplace=True) + itn_curr['itn_rate'] = itn_curr['itn_rate'].round(decimals=1) # maximum itn is 0.7; see comment https://github.com/UCL/TLOmodel/pull/165#issuecomment-699625290 - itn_curr.loc[itn_curr.itn_rate > 0.7, "itn_rate"] = 0.7 - itn_curr = itn_curr.set_index(["District", "Year"]) - - irs_curr = p["irs_district"] - irs_curr.rename(columns={"irs_rates": "irs_rate"}, inplace=True) - irs_curr.drop(["Region"], axis=1, inplace=True) - irs_curr["irs_rate"] = irs_curr["irs_rate"].round(decimals=1) - irs_curr.loc[irs_curr.irs_rate > p["irs_rates_boundary"], "irs_rate"] = p[ - "irs_rates_upper" + itn_curr.loc[itn_curr.itn_rate > 0.7, 'itn_rate'] = 0.7 + itn_curr = itn_curr.set_index(['District', 'Year']) + + irs_curr = p['irs_district'] + irs_curr.rename(columns={'irs_rates': 'irs_rate'}, inplace=True) + irs_curr.drop(['Region'], axis=1, inplace=True) + irs_curr['irs_rate'] = irs_curr['irs_rate'].round(decimals=1) + irs_curr.loc[irs_curr.irs_rate > p['irs_rates_boundary'], 'irs_rate'] = p[ + 'irs_rates_upper' ] - irs_curr.loc[irs_curr.irs_rate <= p["irs_rates_boundary"], "irs_rate"] = p[ - "irs_rates_lower" + irs_curr.loc[irs_curr.irs_rate <= p['irs_rates_boundary'], 'irs_rate'] = p[ + 'irs_rates_lower' ] - irs_curr = irs_curr.set_index(["District", "Year"]) + irs_curr = irs_curr.set_index(['District', 'Year']) itn_irs = pd.concat([itn_curr, irs_curr], axis=1) # Substitute District Num for District Name mapper_district_name_to_num = { v: k - for k, v in self.sim.modules["Demography"] - .parameters["district_num_to_district_name"] + for k, v in self.sim.modules['Demography'] + .parameters['district_num_to_district_name'] .items() } self.itn_irs = ( itn_irs.reset_index() .assign( - District_Num=lambda x: x["District"].map(mapper_district_name_to_num) + District_Num=lambda x: x['District'].map(mapper_district_name_to_num) ) - .drop(columns=["District"]) - .set_index(["District_Num", "Year"]) + .drop(columns=['District']) + .set_index(['District_Num', 'Year']) ) # =============================================================================== # put the all incidence data into single table with month/admin/llin/irs index # =============================================================================== - inf_inc = p["inf_inc"].set_index(["month", "admin", "llin", "irs", "age"]) - inf_inc = inf_inc.loc[:, ["monthly_prob_inf"]] + inf_inc = p['inf_inc'].set_index(['month', 'admin', 'llin', 'irs', 'age']) + inf_inc = inf_inc.loc[:, ['monthly_prob_inf']] - clin_inc = p["clin_inc"].set_index(["month", "admin", "llin", "irs", "age"]) - clin_inc = clin_inc.loc[:, ["monthly_prob_clin"]] + clin_inc = p['clin_inc'].set_index(['month', 'admin', 'llin', 'irs', 'age']) + clin_inc = clin_inc.loc[:, ['monthly_prob_clin']] - sev_inc = p["sev_inc"].set_index(["month", "admin", "llin", "irs", "age"]) - sev_inc = sev_inc.loc[:, ["monthly_prob_sev"]] + sev_inc = p['sev_inc'].set_index(['month', 'admin', 'llin', 'irs', 'age']) + sev_inc = sev_inc.loc[:, ['monthly_prob_sev']] all_inc = pd.concat([inf_inc, clin_inc, sev_inc], axis=1) - # we don"t want age to be part of index + # we don't want age to be part of index all_inc = all_inc.reset_index() - all_inc["district_num"] = all_inc["admin"].map(mapper_district_name_to_num) - assert not all_inc["district_num"].isna().any() + all_inc['district_num'] = all_inc['admin'].map(mapper_district_name_to_num) + assert not all_inc['district_num'].isna().any() - self.all_inc = all_inc.drop(columns=["admin"]).set_index( - ["month", "district_num", "llin", "irs"] + self.all_inc = all_inc.drop(columns=['admin']).set_index( + ['month', 'district_num', 'llin', 'irs'] ) # get the DALY weight that this module will use from the weight database - if "HealthBurden" in self.sim.modules: - p["daly_wt_clinical"] = self.sim.modules["HealthBurden"].get_daly_weight( + if 'HealthBurden' in self.sim.modules: + p['daly_wt_clinical'] = self.sim.modules['HealthBurden'].get_daly_weight( 218 ) - p["daly_wt_severe"] = self.sim.modules["HealthBurden"].get_daly_weight(213) + p['daly_wt_severe'] = self.sim.modules['HealthBurden'].get_daly_weight(213) # ----------------------------------- DECLARE THE SYMPTOMS ------------------------------------------- - self.sim.modules["SymptomManager"].register_symptom( + self.sim.modules['SymptomManager'].register_symptom( Symptom( - "severe_anaemia" + 'severe_anaemia' ), # nb. will cause care seeking as much as a typical symptom - Symptom.emergency("severe_malaria"), # emergency + Symptom.emergency('severe_malaria'), # emergency ) def pre_initialise_population(self): @@ -337,67 +337,67 @@ def pre_initialise_population(self): # ---- LINEAR MODELS ----- # LinearModel for the relative risk of clinical malaria infection predictors = [ - Predictor("ma_iptp").when(True, p["rr_clinical_malaria_iptp"]), + Predictor('ma_iptp').when(True, p['rr_clinical_malaria_iptp']), ] # people with HIV conditional_predictors = ( [ Predictor().when( - "(hv_inf == True) & (age_years <= 5) & (is_pregnant == False)", - p["rr_clinical_malaria_hiv_under5"], + '(hv_inf == True) & (age_years <= 5) & (is_pregnant == False)', + p['rr_clinical_malaria_hiv_under5'], ), Predictor().when( - "(hv_inf == True) & (age_years > 5) & (is_pregnant == False)", - p["rr_clinical_malaria_hiv_over5"], + '(hv_inf == True) & (age_years > 5) & (is_pregnant == False)', + p['rr_clinical_malaria_hiv_over5'], ), Predictor().when( - "(hv_inf == True) & (is_pregnant == True)", - p["rr_clinical_malaria_hiv_pregnant"], + '(hv_inf == True) & (is_pregnant == True)', + p['rr_clinical_malaria_hiv_pregnant'], ), # treatment effects # assume same effect of cotrim if pregnant - Predictor("hv_art") - .when("on_VL_suppressed", p["rr_clinical_malaria_art"]) + Predictor('hv_art') + .when('on_VL_suppressed', p['rr_clinical_malaria_art']) .otherwise(1.0), - Predictor("hv_on_cotrimoxazole").when( - True, p["rr_clinical_malaria_cotrimoxazole"] + Predictor('hv_on_cotrimoxazole').when( + True, p['rr_clinical_malaria_cotrimoxazole'] ), ] - if "Hiv" in self.sim.modules + if 'Hiv' in self.sim.modules else [] ) - self.lm["rr_of_clinical_malaria"] = LinearModel.multiplicative( + self.lm['rr_of_clinical_malaria'] = LinearModel.multiplicative( *(predictors + conditional_predictors) ) # LinearModel for the relative risk of severe malaria infection predictors = [ - Predictor("ma_iptp").when(True, p["rr_severe_malaria_iptp"]), + Predictor('ma_iptp').when(True, p['rr_severe_malaria_iptp']), ] # people with HIV conditional_predictors = ( [ Predictor().when( - "(hv_inf == True) & (age_years <= 5) & (is_pregnant == False)", - p["rr_severe_malaria_hiv_under5"], + '(hv_inf == True) & (age_years <= 5) & (is_pregnant == False)', + p['rr_severe_malaria_hiv_under5'], ), Predictor().when( - "(hv_inf == True) & (age_years > 5) & (is_pregnant == False)", - p["rr_severe_malaria_hiv_over5"], + '(hv_inf == True) & (age_years > 5) & (is_pregnant == False)', + p['rr_severe_malaria_hiv_over5'], ), Predictor().when( - "(hv_inf == True) & (is_pregnant == True)", - p["rr_severe_malaria_hiv_pregnant"], + '(hv_inf == True) & (is_pregnant == True)', + p['rr_severe_malaria_hiv_pregnant'], ), ] - if "hiv" in self.sim.modules + if 'hiv' in self.sim.modules else [] ) - self.lm["rr_of_severe_malaria"] = LinearModel.multiplicative( + self.lm['rr_of_severe_malaria'] = LinearModel.multiplicative( *(predictors + conditional_predictors) ) @@ -406,20 +406,20 @@ def initialise_population(self, population): # ----------------------------------- INITIALISE THE POPULATION----------------------------------- # Set default for properties - df.loc[df.is_alive, "ma_is_infected"] = False - df.loc[df.is_alive, "ma_date_infected"] = pd.NaT - df.loc[df.is_alive, "ma_date_symptoms"] = pd.NaT - df.loc[df.is_alive, "ma_date_death"] = pd.NaT - df.loc[df.is_alive, "ma_tx"] = "none" - df.loc[df.is_alive, "ma_date_tx"] = pd.NaT - df.loc[df.is_alive, "ma_inf_type"] = "none" - df.loc[df.is_alive, "ma_age_edited"] = 0.0 - - df.loc[df.is_alive, "ma_clinical_counter"] = 0 - df.loc[df.is_alive, "ma_dx_counter"] = 0 - df.loc[df.is_alive, "ma_tx_counter"] = 0 - df.loc[df.is_alive, "ma_clinical_preg_counter"] = 0 - df.loc[df.is_alive, "ma_iptp"] = False + df.loc[df.is_alive, 'ma_is_infected'] = False + df.loc[df.is_alive, 'ma_date_infected'] = pd.NaT + df.loc[df.is_alive, 'ma_date_symptoms'] = pd.NaT + df.loc[df.is_alive, 'ma_date_death'] = pd.NaT + df.loc[df.is_alive, 'ma_tx'] = 'none' + df.loc[df.is_alive, 'ma_date_tx'] = pd.NaT + df.loc[df.is_alive, 'ma_inf_type'] = 'none' + df.loc[df.is_alive, 'ma_age_edited'] = 0.0 + + df.loc[df.is_alive, 'ma_clinical_counter'] = 0 + df.loc[df.is_alive, 'ma_dx_counter'] = 0 + df.loc[df.is_alive, 'ma_tx_counter'] = 0 + df.loc[df.is_alive, 'ma_clinical_preg_counter'] = 0 + df.loc[df.is_alive, 'ma_iptp'] = False def malaria_poll2(self, population): df = population.props @@ -429,20 +429,20 @@ def malaria_poll2(self, population): # ----------------------------------- DISTRICT INTERVENTION COVERAGE ----------------------------------- # fix values for 2018 onwards - current_year = min(now.year, p["data_end"]) + current_year = min(now.year, p['data_end']) # get itn_irs rows for current year; slice multiindex for all districts & current_year itn_irs_curr = self.itn_irs.loc[pd.IndexSlice[:, current_year], :] itn_irs_curr = itn_irs_curr.reset_index().drop( - "Year", axis=1 - ) # we don"t use the year column + 'Year', axis=1 + ) # we don't use the year column itn_irs_curr.insert( - 0, "month", now.month + 0, 'month', now.month ) # add current month for the incidence index lookup # replace itn coverage with projected coverage levels from 2019 onwards - if now.year > p["data_end"]: - itn_irs_curr["itn_rate"] = self.parameters["itn"] + if now.year > p['data_end']: + itn_irs_curr['itn_rate'] = self.parameters['itn'] month_districtnum_itn_irs_lookup = [ tuple(r) for r in itn_irs_curr.values @@ -453,8 +453,8 @@ def malaria_poll2(self, population): curr_inc = self.all_inc.loc[month_districtnum_itn_irs_lookup] curr_inc = ( curr_inc.reset_index() - .drop(["month", "llin", "irs"], axis=1) - .set_index(["district_num", "age"]) + .drop(['month', 'llin', 'irs'], axis=1) + .set_index(['district_num', 'age']) ) # ----------------------------------- DISTRICT NEW INFECTIONS ----------------------------------- @@ -463,7 +463,7 @@ def _draw_incidence_for(_col, _where): # create an index from the individuals to lookup entries in the current incidence table district_age_lookup = ( df[_where] - .set_index(["district_num_of_residence", "ma_age_edited"]) + .set_index(['district_num_of_residence', 'ma_age_edited']) .index ) # get the monthly incidence probabilities for these individuals @@ -472,15 +472,15 @@ def _draw_incidence_for(_col, _where): monthly_prob = monthly_prob.set_axis(df.index[_where]) # the linear models only apply to clinical and severe malaria risk - if _col == "monthly_prob_inf": + if _col == 'monthly_prob_inf': # select individuals for infection random_draw = rng.random_sample(_where.sum()) < monthly_prob else: linear_model = ( - self.lm["rr_of_clinical_malaria"] - if _col == "monthly_prob_clin" - else self.lm["rr_of_severe_malaria"] + self.lm['rr_of_clinical_malaria'] + if _col == 'monthly_prob_clin' + else self.lm['rr_of_severe_malaria'] ) # apply linear model to get individual risk @@ -494,32 +494,32 @@ def _draw_incidence_for(_col, _where): return selected - # we don"t have incidence data for over 80s + # we don't have incidence data for over 80s alive = df.is_alive & (df.age_years < 80) alive_over_one = alive & (df.age_exact_years >= 1) - df.loc[alive & df.age_exact_years.between(0, 0.5), "ma_age_edited"] = 0.0 - df.loc[alive & df.age_exact_years.between(0.5, 1), "ma_age_edited"] = 0.5 - df.loc[alive_over_one, "ma_age_edited"] = df.loc[ - alive_over_one, "age_years" + df.loc[alive & df.age_exact_years.between(0, 0.5), 'ma_age_edited'] = 0.0 + df.loc[alive & df.age_exact_years.between(0.5, 1), 'ma_age_edited'] = 0.5 + df.loc[alive_over_one, 'ma_age_edited'] = df.loc[ + alive_over_one, 'age_years' ].astype(float) # select new infections # eligible: uninfected or asym - alive_uninfected = alive & df.ma_inf_type.isin(["none", "asym"]) - now_infected = _draw_incidence_for("monthly_prob_inf", alive_uninfected) - df.loc[now_infected, "ma_inf_type"] = "asym" + alive_uninfected = alive & df.ma_inf_type.isin(['none', 'asym']) + now_infected = _draw_incidence_for('monthly_prob_inf', alive_uninfected) + df.loc[now_infected, 'ma_inf_type'] = 'asym' # draw from currently asymptomatic to allocate clinical cases # this can include people who became infected/asym in previous polls - alive_infected_asym = alive & (df.ma_inf_type == "asym") - now_clinical = _draw_incidence_for("monthly_prob_clin", alive_infected_asym) - df.loc[now_clinical, "ma_inf_type"] = "clinical" + alive_infected_asym = alive & (df.ma_inf_type == 'asym') + now_clinical = _draw_incidence_for('monthly_prob_clin', alive_infected_asym) + df.loc[now_clinical, 'ma_inf_type'] = 'clinical' # draw from clinical cases to allocate severe cases - draw from all currently clinical cases - alive_infected_clinical = alive & (df.ma_inf_type == "clinical") - now_severe = _draw_incidence_for("monthly_prob_sev", alive_infected_clinical) - df.loc[now_severe, "ma_inf_type"] = "severe" + alive_infected_clinical = alive & (df.ma_inf_type == 'clinical') + now_severe = _draw_incidence_for('monthly_prob_sev', alive_infected_clinical) + df.loc[now_severe, 'ma_inf_type'] = 'severe' # ----------------------------------- ASSIGN INFECTION DATES ----------------------------------- @@ -541,16 +541,16 @@ def _draw_incidence_for(_col, _where): # join all indices (some clinical infections drawn from asymptomatic infections from previous months) for idx in all_new_infections: date_of_infection = now + pd.DateOffset(days=self.rng.randint(1, 30)) - df.at[idx, "ma_date_infected"] = date_of_infection + df.at[idx, 'ma_date_infected'] = date_of_infection - assert (df.loc[all_new_infections, "ma_date_infected"] >= self.sim.date).all() + assert (df.loc[all_new_infections, 'ma_date_infected'] >= self.sim.date).all() # assign date of symptom onset - df.loc[new_clinical, "ma_date_symptoms"] = df.loc[ - new_clinical, "ma_date_infected" + df.loc[new_clinical, 'ma_date_symptoms'] = df.loc[ + new_clinical, 'ma_date_infected' ] + DateOffset(days=7) - df.loc[new_severe, "ma_date_symptoms"] = df.loc[ - new_severe, "ma_date_infected" + df.loc[new_severe, 'ma_date_symptoms'] = df.loc[ + new_severe, 'ma_date_infected' ] + DateOffset(days=7) # ----------------------------------- CLINICAL MALARIA SYMPTOMS ----------------------------------- @@ -558,10 +558,10 @@ def _draw_incidence_for(_col, _where): # check symptom onset occurs in one week if len(new_clinical): assert ( - df.loc[new_clinical, "ma_date_infected"] - < df.loc[new_clinical, "ma_date_symptoms"] + df.loc[new_clinical, 'ma_date_infected'] + < df.loc[new_clinical, 'ma_date_symptoms'] ).all() - assert not pd.isnull(df.loc[new_clinical, "ma_date_symptoms"]).all() + assert not pd.isnull(df.loc[new_clinical, 'ma_date_symptoms']).all() # ----------------------------------- SCHEDULED DEATHS ----------------------------------- # schedule deaths within the next week @@ -569,21 +569,21 @@ def _draw_incidence_for(_col, _where): # the cfr applies to all severe malaria random_draw = rng.random_sample(size=len(new_severe)) - death = df.index[new_severe][random_draw < (p["cfr"] * p["mortality_adjust"])] + death = df.index[new_severe][random_draw < (p['cfr'] * p['mortality_adjust'])] for person in death: logger.debug( - key="message", - data=f"MalariaEvent: scheduling malaria death for person {person}", + key='message', + data=f'MalariaEvent: scheduling malaria death for person {person}', ) # death occurs 1-7 days after symptom onset - date_death = df.at[person, "ma_date_symptoms"] + DateOffset( + date_death = df.at[person, 'ma_date_symptoms'] + DateOffset( days=rng.randint(low=1, high=7) ) death_event = MalariaDeathEvent( - self, person_id=person, cause="Malaria" + self, person_id=person, cause='Malaria' ) # make that death event self.sim.schedule_event(death_event, date_death) # schedule the death @@ -603,23 +603,23 @@ def general_population_rdt_scheduler(self, population): year = self.sim.date.year if self.sim.date.year <= 2024 else 2024 test_rates = ( - p["rdt_testing_rates"].set_index("Year")["Rate_rdt_testing"].dropna() + p['rdt_testing_rates'].set_index('Year')['Rate_rdt_testing'].dropna() ) rdt_rate = test_rates.loc[min(test_rates.index.max(), year)] / 12 # adjust rdt usage reported rate to reflect consumables availability - rdt_rate = rdt_rate * p["scaling_factor_for_rdt_availability"] + rdt_rate = rdt_rate * p['scaling_factor_for_rdt_availability'] # testing trends independent of any demographic characteristics # no rdt offered if currently on anti-malarials random_draw = rng.random_sample(size=len(df)) will_test_idx = df.loc[ - df.is_alive & (df.ma_tx == "none") & (random_draw < rdt_rate) + df.is_alive & (df.ma_tx == 'none') & (random_draw < rdt_rate) ].index for person_id in will_test_idx: date_test = self.sim.date + pd.DateOffset(days=self.rng.randint(0, 30)) - self.sim.modules["HealthSystem"].schedule_hsi_event( + self.sim.modules['HealthSystem'].schedule_hsi_event( hsi_event=HSI_Malaria_rdt_community(person_id=person_id, module=self), priority=1, topen=date_test, @@ -644,7 +644,7 @@ def initialise_simulation(self, sim): MalariaParasiteClearanceEvent(self), sim.date + DateOffset(months=1) ) - if "CareOfWomenDuringPregnancy" not in self.sim.modules: + if 'CareOfWomenDuringPregnancy' not in self.sim.modules: sim.schedule_event(MalariaIPTp(self), sim.date + DateOffset(days=30.5)) # add logger events @@ -658,86 +658,86 @@ def initialise_simulation(self, sim): # Create the diagnostic test representing the use of RDT for malaria diagnosis # and registers it with the Diagnostic Test Manager - self.sim.modules["HealthSystem"].dx_manager.register_dx_test( + self.sim.modules['HealthSystem'].dx_manager.register_dx_test( malaria_rdt=DxTest( - property="ma_is_infected", + property='ma_is_infected', item_codes=self.sim.modules[ - "HealthSystem" - ].get_item_code_from_item_name("Malaria test kit (RDT)"), - sensitivity=self.parameters["sensitivity_rdt"], + 'HealthSystem' + ].get_item_code_from_item_name('Malaria test kit (RDT)'), + sensitivity=self.parameters['sensitivity_rdt'], ) ) # 3) ----------------------------------- CONSUMABLES ----------------------------------- - get_item_code = self.sim.modules["HealthSystem"].get_item_code_from_item_name + get_item_code = self.sim.modules['HealthSystem'].get_item_code_from_item_name # malaria rdt - self.item_codes_for_consumables_required["malaria_rdt"] = get_item_code( - "Malaria test kit (RDT)" + self.item_codes_for_consumables_required['malaria_rdt'] = get_item_code( + 'Malaria test kit (RDT)' ) # malaria treatment uncomplicated children <15kg self.item_codes_for_consumables_required[ - "malaria_uncomplicated_young_children" - ] = get_item_code("Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST") + 'malaria_uncomplicated_young_children' + ] = get_item_code('Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST') - self.item_codes_for_consumables_required["paracetamol_syrup"] = get_item_code( - "Paracetamol syrup 120mg/5ml_0.0119047619047619_CMST" + self.item_codes_for_consumables_required['paracetamol_syrup'] = get_item_code( + 'Paracetamol syrup 120mg/5ml_0.0119047619047619_CMST' ) # malaria treatment uncomplicated children >15kg self.item_codes_for_consumables_required[ - "malaria_uncomplicated_older_children" - ] = get_item_code("Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST") + 'malaria_uncomplicated_older_children' + ] = get_item_code('Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST') # malaria treatment uncomplicated adults >36kg - self.item_codes_for_consumables_required["malaria_uncomplicated_adult"] = ( - get_item_code("Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST") + self.item_codes_for_consumables_required['malaria_uncomplicated_adult'] = ( + get_item_code('Lumefantrine 120mg/Artemether 20mg, 30x18_540_CMST') ) - self.item_codes_for_consumables_required["paracetamol"] = get_item_code( - "Paracetamol 500mg_1000_CMST" + self.item_codes_for_consumables_required['paracetamol'] = get_item_code( + 'Paracetamol 500mg_1000_CMST' ) # malaria treatment complicated - same consumables for adults and children - self.item_codes_for_consumables_required["malaria_complicated"] = get_item_code( - "Injectable artesunate" + self.item_codes_for_consumables_required['malaria_complicated'] = get_item_code( + 'Injectable artesunate' ) self.item_codes_for_consumables_required[ - "malaria_complicated_optional_items" + 'malaria_complicated_optional_items' ] = [ - get_item_code("Malaria test kit (RDT)"), - get_item_code("Cannula iv (winged with injection pot) 18_each_CMST"), - get_item_code("Disposables gloves, powder free, 100 pieces per box"), - get_item_code("Gauze, absorbent 90cm x 40m_each_CMST"), - get_item_code("Water for injection, 10ml_Each_CMST"), + get_item_code('Malaria test kit (RDT)'), + get_item_code('Cannula iv (winged with injection pot) 18_each_CMST'), + get_item_code('Disposables gloves, powder free, 100 pieces per box'), + get_item_code('Gauze, absorbent 90cm x 40m_each_CMST'), + get_item_code('Water for injection, 10ml_Each_CMST'), ] # malaria IPTp for pregnant women - self.item_codes_for_consumables_required["malaria_iptp"] = get_item_code( - "Sulfamethoxazole + trimethropin, tablet 400 mg + 80 mg" + self.item_codes_for_consumables_required['malaria_iptp'] = get_item_code( + 'Sulfamethoxazole + trimethropin, tablet 400 mg + 80 mg' ) def on_birth(self, mother_id, child_id): df = self.sim.population.props - df.at[child_id, "ma_is_infected"] = False - df.at[child_id, "ma_date_infected"] = pd.NaT - df.at[child_id, "ma_date_symptoms"] = pd.NaT - df.at[child_id, "ma_date_death"] = pd.NaT - df.at[child_id, "ma_tx"] = "none" - df.at[child_id, "ma_date_tx"] = pd.NaT - df.at[child_id, "ma_inf_type"] = "none" - df.at[child_id, "ma_age_edited"] = 0.0 - df.at[child_id, "ma_clinical_counter"] = 0 - df.at[child_id, "ma_clinical_preg_counter"] = 0 - df.at[child_id, "ma_dx_counter"] = 0 - df.at[child_id, "ma_tx_counter"] = 0 - df.at[child_id, "ma_iptp"] = False + df.at[child_id, 'ma_is_infected'] = False + df.at[child_id, 'ma_date_infected'] = pd.NaT + df.at[child_id, 'ma_date_symptoms'] = pd.NaT + df.at[child_id, 'ma_date_death'] = pd.NaT + df.at[child_id, 'ma_tx'] = 'none' + df.at[child_id, 'ma_date_tx'] = pd.NaT + df.at[child_id, 'ma_inf_type'] = 'none' + df.at[child_id, 'ma_age_edited'] = 0.0 + df.at[child_id, 'ma_clinical_counter'] = 0 + df.at[child_id, 'ma_clinical_preg_counter'] = 0 + df.at[child_id, 'ma_dx_counter'] = 0 + df.at[child_id, 'ma_tx_counter'] = 0 + df.at[child_id, 'ma_iptp'] = False # reset mother's IPTp status to False if mother_id >= 0: # exclude direct births - df.at[mother_id, "ma_iptp"] = False + df.at[mother_id, 'ma_iptp'] = False def report_daly_values(self): # This must send back a pd.Series or pd.DataFrame that reports on the average daly-weights that have been @@ -745,20 +745,20 @@ def report_daly_values(self): # The names of the series of columns is taken to be the label of the cause of this disability. # It will be recorded by the healthburden module as _. - logger.debug(key="message", data="This is malaria reporting my health values") + logger.debug(key='message', data='This is malaria reporting my health values') df = self.sim.population.props # shortcut to population properties dataframe p = self.parameters - health_values = df.loc[df.is_alive, "ma_inf_type"].map( + health_values = df.loc[df.is_alive, 'ma_inf_type'].map( { - "none": 0, - "asym": 0, - "clinical": p["daly_wt_clinical"], - "severe": p["daly_wt_severe"], + 'none': 0, + 'asym': 0, + 'clinical': p['daly_wt_clinical'], + 'severe': p['daly_wt_severe'], } ) - health_values.name = "Malaria" # label the cause of this disability + health_values.name = 'Malaria' # label the cause of this disability return health_values.loc[df.is_alive] # returns the series @@ -771,7 +771,7 @@ def check_if_fever_is_caused_by_malaria( patient_age: Optional[Union[int, float]] = None, facility_level: Optional[str] = None, treatment_id: Optional[str] = None, - ) -> Literal["severe_malaria", "clinical_malaria", "negative_malaria_test"]: + ) -> Literal['severe_malaria', 'clinical_malaria', 'negative_malaria_test']: """ Run by an HSI when an adult presents with fever. Determine if the cause is malaria. @@ -781,28 +781,28 @@ def check_if_fever_is_caused_by_malaria( """ # Call the DxTest RDT to diagnose malaria - dx_result = diagnosis_function("malaria_rdt") + dx_result = diagnosis_function('malaria_rdt') # Log the test: line-list of summary information about each test logger.info( - key="rdt_log", + key='rdt_log', data={ - "person_id": patient_id, - "age": patient_age, - "fever_present": fever_is_a_symptom, - "rdt_result": dx_result, - "facility_level": facility_level, - "called_by": treatment_id, + 'person_id': patient_id, + 'age': patient_age, + 'fever_present': fever_is_a_symptom, + 'rdt_result': dx_result, + 'facility_level': facility_level, + 'called_by': treatment_id, }, ) # Severe malaria infection always returns positive RDT - if true_malaria_infection_type == "severe": - return "severe_malaria" - elif dx_result and true_malaria_infection_type in ("clinical", "asym"): - return "clinical_malaria" + if true_malaria_infection_type == 'severe': + return 'severe_malaria' + elif dx_result and true_malaria_infection_type in ('clinical', 'asym'): + return 'clinical_malaria' else: - return "negative_malaria_test" + return 'negative_malaria_test' def do_at_generic_first_appt( self, @@ -817,36 +817,36 @@ def do_at_generic_first_appt( patient_details_updates = {} malaria_associated_symptoms = { - "fever", - "headache", - "stomachache", - "diarrhoea", - "vomiting", + 'fever', + 'headache', + 'stomachache', + 'diarrhoea', + 'vomiting', } if ( bool(set(symptoms) & malaria_associated_symptoms) - and patient_details.ma_tx == "none" + and patient_details.ma_tx == 'none' ): malaria_test_result = self.check_if_fever_is_caused_by_malaria( true_malaria_infection_type=patient_details.ma_inf_type, diagnosis_function=diagnosis_function, patient_id=patient_id, - fever_is_a_symptom="fever" in symptoms, + fever_is_a_symptom='fever' in symptoms, patient_age=patient_details.age_years, facility_level=facility_level, treatment_id=treatment_id, ) # Treat / refer based on diagnosis - if malaria_test_result == "severe_malaria": - patient_details_updates["ma_dx_counter"] = patient_details.ma_dx_counter + 1 + if malaria_test_result == 'severe_malaria': + patient_details_updates['ma_dx_counter'] = patient_details.ma_dx_counter + 1 event = HSI_Malaria_Treatment_Complicated(person_id=patient_id, module=self) self.healthsystem.schedule_hsi_event( event, priority=0, topen=self.sim.date ) - # return type "clinical_malaria" includes asymptomatic infection - elif malaria_test_result == "clinical_malaria": - patient_details_updates["ma_dx_counter"] = patient_details.ma_dx_counter + 1 + # return type 'clinical_malaria' includes asymptomatic infection + elif malaria_test_result == 'clinical_malaria': + patient_details_updates['ma_dx_counter'] = patient_details.ma_dx_counter + 1 event = HSI_Malaria_Treatment(person_id=patient_id, module=self) self.healthsystem.schedule_hsi_event( event, priority=1, topen=self.sim.date @@ -869,22 +869,22 @@ def do_at_generic_first_appt_emergency( # symptoms of severe malaria.) patient_details_updates = {} - if "severe_malaria" in symptoms: - if patient_details.ma_tx == "none": + if 'severe_malaria' in symptoms: + if patient_details.ma_tx == 'none': # Check if malaria parasitaemia: malaria_test_result = self.check_if_fever_is_caused_by_malaria( true_malaria_infection_type=patient_details.ma_inf_type, diagnosis_function=diagnosis_function, patient_id=patient_id, - fever_is_a_symptom="fever" in symptoms, + fever_is_a_symptom='fever' in symptoms, patient_age=patient_details.age_years, facility_level=facility_level, treatment_id=treatment_id, ) # if any symptoms indicative of malaria and they have parasitaemia (would return a positive rdt) - if malaria_test_result in ("severe_malaria", "clinical_malaria"): - patient_details_updates["ma_dx_counter"] = patient_details.ma_dx_counter + 1 + if malaria_test_result in ('severe_malaria', 'clinical_malaria'): + patient_details_updates['ma_dx_counter'] = patient_details.ma_dx_counter + 1 # Launch the HSI for treatment for Malaria, HSI_Malaria_Treatment will determine correct treatment event = HSI_Malaria_Treatment_Complicated( @@ -906,8 +906,8 @@ def __init__(self, module): def apply(self, population): logger.debug( - key="message", - data="MalariaEvent: tracking the disease progression of the population", + key='message', + data='MalariaEvent: tracking the disease progression of the population', ) # assigns new malaria infections @@ -937,7 +937,7 @@ def apply(self, population): & ~df.ma_iptp & ( ~df.hv_on_cotrimoxazole - if "Hiv" in self.sim.modules + if 'Hiv' in self.sim.modules else True ) ) @@ -946,12 +946,12 @@ def apply(self, population): for person_index in p1: logger.debug( - key="message", - data=f"MalariaIPTp: scheduling HSI_Malaria_IPTp for person {person_index}", + key='message', + data=f'MalariaIPTp: scheduling HSI_Malaria_IPTp for person {person_index}', ) event = HSI_MalariaIPTp(self.module, person_id=person_index) - self.sim.modules["HealthSystem"].schedule_hsi_event( + self.sim.modules['HealthSystem'].schedule_hsi_event( event, priority=1, topen=now, tclose=None ) @@ -973,11 +973,11 @@ def __init__( def apply(self, person_id): df = self.sim.population.props - if not df.at[person_id, "is_alive"] or not df.at[person_id, "ma_iptp"]: + if not df.at[person_id, 'is_alive'] or not df.at[person_id, 'ma_iptp']: return # reset the IPTp property - df.at[person_id, "ma_iptp"] = False + df.at[person_id, 'ma_iptp'] = False class MalariaDeathEvent(Event, IndividualScopeEventMixin): @@ -992,46 +992,46 @@ def __init__(self, module, person_id, cause): def apply(self, person_id): df = self.sim.population.props - if not df.at[person_id, "is_alive"] or ( - df.at[person_id, "ma_inf_type"] == "none" + if not df.at[person_id, 'is_alive'] or ( + df.at[person_id, 'ma_inf_type'] == 'none' ): return # if on treatment for severe malaria, will reduce probability of death # use random number generator - currently param treatment_adjustment set to 0.5 - if df.at[person_id, "ma_tx"] == "complicated": + if df.at[person_id, 'ma_tx'] == 'complicated': prob = self.module.rng.rand() # if draw -> death - if prob < self.module.parameters["treatment_adjustment"]: - self.sim.modules["Demography"].do_death( + if prob < self.module.parameters['treatment_adjustment']: + self.sim.modules['Demography'].do_death( individual_id=person_id, cause=self.cause, originating_module=self.module, ) - df.at[person_id, "ma_date_death"] = self.sim.date + df.at[person_id, 'ma_date_death'] = self.sim.date # else if draw does not result in death -> cure else: - df.at[person_id, "ma_tx"] = "none" - df.at[person_id, "ma_inf_type"] = "none" - df.at[person_id, "ma_is_infected"] = False + df.at[person_id, 'ma_tx'] = 'none' + df.at[person_id, 'ma_inf_type'] = 'none' + df.at[person_id, 'ma_is_infected'] = False # clear symptoms - self.sim.modules["SymptomManager"].clear_symptoms( + self.sim.modules['SymptomManager'].clear_symptoms( person_id=person_id, disease_module=self.module ) # if not on treatment - death will occur else: - self.sim.modules["Demography"].do_death( + self.sim.modules['Demography'].do_death( individual_id=person_id, cause=self.cause, originating_module=self.module, ) - df.at[person_id, "ma_date_death"] = self.sim.date + df.at[person_id, 'ma_date_death'] = self.sim.date # --------------------------------------------------------------------------------- @@ -1045,74 +1045,74 @@ class HSI_Malaria_rdt(HSI_Event, IndividualScopeEventMixin): default facility level is 1a unless specified """ - def __init__(self, module, person_id, facility_level="1a"): + def __init__(self, module, person_id, facility_level='1a'): super().__init__(module, person_id=person_id) assert isinstance(module, Malaria) - self.TREATMENT_ID = "Malaria_Test" + self.TREATMENT_ID = 'Malaria_Test' self.facility_level = facility_level df = self.sim.population.props - person_age_years = df.at[self.target, "age_years"] + person_age_years = df.at[self.target, 'age_years'] self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint( - {"Under5OPD" if person_age_years < 5 else "Over5OPD": 1} + {'Under5OPD' if person_age_years < 5 else 'Over5OPD': 1} ) - self.ACCEPTED_FACILITY_LEVEL = "1a" if (self.facility_level == "1a") else "1b" + self.ACCEPTED_FACILITY_LEVEL = '1a' if (self.facility_level == '1a') else '1b' def apply(self, person_id, squeeze_factor): df = self.sim.population.props - hs = self.sim.modules["HealthSystem"] + hs = self.sim.modules['HealthSystem'] # Ignore this event if the person is no longer alive or already on treatment - if not df.at[person_id, "is_alive"] or (df.at[person_id, "ma_tx"] != "none"): + if not df.at[person_id, 'is_alive'] or (df.at[person_id, 'ma_tx'] != 'none'): return hs.get_blank_appt_footprint() - district = df.at[person_id, "district_num_of_residence"] + district = df.at[person_id, 'district_num_of_residence'] logger.debug( - key="message", - data=f"HSI_Malaria_rdt: rdt test for person {person_id} " - f"in district num {district}", + key='message', + data=f'HSI_Malaria_rdt: rdt test for person {person_id} ' + f'in district num {district}', ) # call the DxTest RDT to diagnose malaria dx_result = hs.dx_manager.run_dx_test( - dx_tests_to_run="malaria_rdt", hsi_event=self + dx_tests_to_run='malaria_rdt', hsi_event=self ) # Log the test: line-list of summary information about each test - fever_present = "fever" in self.sim.modules["SymptomManager"].has_what( + fever_present = 'fever' in self.sim.modules['SymptomManager'].has_what( person_id ) person_details_for_test = { - "person_id": person_id, - "age": df.at[person_id, "age_years"], - "fever_present": fever_present, - "rdt_result": dx_result, - "facility_level": self.ACCEPTED_FACILITY_LEVEL, - "called_by": self.TREATMENT_ID, + 'person_id': person_id, + 'age': df.at[person_id, 'age_years'], + 'fever_present': fever_present, + 'rdt_result': dx_result, + 'facility_level': self.ACCEPTED_FACILITY_LEVEL, + 'called_by': self.TREATMENT_ID, } - logger.info(key="rdt_log", data=person_details_for_test) + logger.info(key='rdt_log', data=person_details_for_test) if dx_result: # ----------------------------------- SEVERE MALARIA ----------------------------------- - df.at[person_id, "ma_dx_counter"] += 1 + df.at[person_id, 'ma_dx_counter'] += 1 # if severe malaria, treat for complicated malaria - if df.at[person_id, "ma_inf_type"] == "severe": + if df.at[person_id, 'ma_inf_type'] == 'severe': logger.debug( - key="message", - data=f"HSI_Malaria_rdt: scheduling HSI_Malaria_Treatment_Complicated {person_id}" - f"on date {self.sim.date}", + key='message', + data=f'HSI_Malaria_rdt: scheduling HSI_Malaria_Treatment_Complicated {person_id}' + f'on date {self.sim.date}', ) treat = HSI_Malaria_Treatment_Complicated( - self.sim.modules["Malaria"], person_id=person_id + self.sim.modules['Malaria'], person_id=person_id ) - self.sim.modules["HealthSystem"].schedule_hsi_event( + self.sim.modules['HealthSystem'].schedule_hsi_event( treat, priority=0, topen=self.sim.date, tclose=None ) @@ -1122,22 +1122,22 @@ def apply(self, person_id, squeeze_factor): # this will allow those with asym malaria (positive RDT) to also be treated else: logger.debug( - key="message", - data=f"HSI_Malaria_rdt scheduling HSI_Malaria_Treatment for person {person_id}" - f"on date {self.sim.date}", + key='message', + data=f'HSI_Malaria_rdt scheduling HSI_Malaria_Treatment for person {person_id}' + f'on date {self.sim.date}', ) treat = HSI_Malaria_Treatment(self.module, person_id=person_id) - self.sim.modules["HealthSystem"].schedule_hsi_event( + self.sim.modules['HealthSystem'].schedule_hsi_event( treat, priority=1, topen=self.sim.date, tclose=None ) elif dx_result is None: # repeat appt for rdt and move to level 1b regardless of current facility level - self.sim.modules["HealthSystem"].schedule_hsi_event( + self.sim.modules['HealthSystem'].schedule_hsi_event( HSI_Malaria_rdt( - person_id=person_id, module=self.module, facility_level="1b" + person_id=person_id, module=self.module, facility_level='1b' ), topen=self.sim.date + pd.DateOffset(days=1), tclose=None, @@ -1162,45 +1162,45 @@ def __init__(self, module, person_id): super().__init__(module, person_id=person_id) assert isinstance(module, Malaria) - self.TREATMENT_ID = "Malaria_Test" - self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"ConWithDCSA": 1}) - self.ACCEPTED_FACILITY_LEVEL = "0" + self.TREATMENT_ID = 'Malaria_Test' + self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({'ConWithDCSA': 1}) + self.ACCEPTED_FACILITY_LEVEL = '0' def apply(self, person_id, squeeze_factor): df = self.sim.population.props - hs = self.sim.modules["HealthSystem"] + hs = self.sim.modules['HealthSystem'] # Ignore this event if the person is no longer alive or already on treatment - if not df.at[person_id, "is_alive"] or not ( - df.at[person_id, "ma_tx"] == "none" + if not df.at[person_id, 'is_alive'] or not ( + df.at[person_id, 'ma_tx'] == 'none' ): return hs.get_blank_appt_footprint() # call the DxTest RDT to diagnose malaria dx_result = hs.dx_manager.run_dx_test( - dx_tests_to_run="malaria_rdt", hsi_event=self + dx_tests_to_run='malaria_rdt', hsi_event=self ) # Log the test: line-list of summary information about each test - fever_present = "fever" in self.sim.modules["SymptomManager"].has_what( + fever_present = 'fever' in self.sim.modules['SymptomManager'].has_what( person_id ) person_details_for_test = { - "person_id": person_id, - "age": df.at[person_id, "age_years"], - "fever_present": fever_present, - "rdt_result": dx_result, - "facility_level": self.ACCEPTED_FACILITY_LEVEL, - "called_by": self.TREATMENT_ID, + 'person_id': person_id, + 'age': df.at[person_id, 'age_years'], + 'fever_present': fever_present, + 'rdt_result': dx_result, + 'facility_level': self.ACCEPTED_FACILITY_LEVEL, + 'called_by': self.TREATMENT_ID, } - logger.info(key="rdt_log", data=person_details_for_test) + logger.info(key='rdt_log', data=person_details_for_test) # if positive, refer for a confirmatory test at level 1a if dx_result: - self.sim.modules["HealthSystem"].schedule_hsi_event( + self.sim.modules['HealthSystem'].schedule_hsi_event( hsi_event=HSI_Malaria_rdt( - person_id=person_id, module=self.module, facility_level="1a" + person_id=person_id, module=self.module, facility_level='1a' ), priority=1, topen=self.sim.date, @@ -1217,18 +1217,18 @@ def __init__(self, module, person_id): super().__init__(module, person_id=person_id) assert isinstance(module, Malaria) - self.TREATMENT_ID = "Malaria_Treatment" + self.TREATMENT_ID = 'Malaria_Treatment' self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint( { ( - "Under5OPD" - if self.sim.population.props.at[person_id, "age_years"] < 5 - else "Over5OPD" + 'Under5OPD' + if self.sim.population.props.at[person_id, 'age_years'] < 5 + else 'Over5OPD' ): 1 } ) - self.ACCEPTED_FACILITY_LEVEL = "1a" + self.ACCEPTED_FACILITY_LEVEL = '1a' def apply(self, person_id, squeeze_factor): @@ -1236,42 +1236,42 @@ def apply(self, person_id, squeeze_factor): person = df.loc[person_id] # if not on treatment already - request treatment - if person["ma_tx"] == "none": + if person['ma_tx'] == 'none': logger.debug( - key="message", - data=f"HSI_Malaria_Treatment: requesting malaria treatment for {person_id}", + key='message', + data=f'HSI_Malaria_Treatment: requesting malaria treatment for {person_id}', ) # Check if drugs are available, and provide drugs: - drugs_available = self.get_drugs(age_of_person=person["age_years"]) + drugs_available = self.get_drugs(age_of_person=person['age_years']) if drugs_available: logger.debug( - key="message", - data=f"HSI_Malaria_Treatment: giving malaria treatment for {person_id}", + key='message', + data=f'HSI_Malaria_Treatment: giving malaria treatment for {person_id}', ) - if df.at[person_id, "is_alive"]: - df.at[person_id, "ma_tx"] = "uncomplicated" - df.at[person_id, "ma_date_tx"] = self.sim.date - df.at[person_id, "ma_tx_counter"] += 1 + if df.at[person_id, 'is_alive']: + df.at[person_id, 'ma_tx'] = 'uncomplicated' + df.at[person_id, 'ma_date_tx'] = self.sim.date + df.at[person_id, 'ma_tx_counter'] += 1 # rdt is offered as part of the treatment package # Log the test: line-list of summary information about each test - fever_present = "fever" in self.sim.modules["SymptomManager"].has_what( + fever_present = 'fever' in self.sim.modules['SymptomManager'].has_what( person_id ) person_details_for_test = { - "person_id": person_id, - "age": df.at[person_id, "age_years"], - "fever_present": fever_present, - "rdt_result": True, - "facility_level": self.ACCEPTED_FACILITY_LEVEL, - "called_by": self.TREATMENT_ID, + 'person_id': person_id, + 'age': df.at[person_id, 'age_years'], + 'fever_present': fever_present, + 'rdt_result': True, + 'facility_level': self.ACCEPTED_FACILITY_LEVEL, + 'called_by': self.TREATMENT_ID, } - logger.info(key="rdt_log", data=person_details_for_test) + logger.info(key='rdt_log', data=person_details_for_test) def get_drugs(self, age_of_person): """ @@ -1286,13 +1286,13 @@ def get_drugs(self, age_of_person): # Formulation for young children drugs_available = self.get_consumables( item_codes=self.module.item_codes_for_consumables_required[ - "malaria_uncomplicated_young_children" + 'malaria_uncomplicated_young_children' ], optional_item_codes=[ self.module.item_codes_for_consumables_required[ - "paracetamol_syrup" + 'paracetamol_syrup' ], - self.module.item_codes_for_consumables_required["malaria_rdt"], + self.module.item_codes_for_consumables_required['malaria_rdt'], ], ) @@ -1300,13 +1300,13 @@ def get_drugs(self, age_of_person): # Formulation for older children drugs_available = self.get_consumables( item_codes=self.module.item_codes_for_consumables_required[ - "malaria_uncomplicated_older_children" + 'malaria_uncomplicated_older_children' ], optional_item_codes=[ self.module.item_codes_for_consumables_required[ - "paracetamol_syrup" + 'paracetamol_syrup' ], - self.module.item_codes_for_consumables_required["malaria_rdt"], + self.module.item_codes_for_consumables_required['malaria_rdt'], ], ) @@ -1314,18 +1314,18 @@ def get_drugs(self, age_of_person): # Formulation for adults drugs_available = self.get_consumables( item_codes=self.module.item_codes_for_consumables_required[ - "malaria_uncomplicated_adult" + 'malaria_uncomplicated_adult' ], optional_item_codes=[ - self.module.item_codes_for_consumables_required["paracetamol"], - self.module.item_codes_for_consumables_required["malaria_rdt"], + self.module.item_codes_for_consumables_required['paracetamol'], + self.module.item_codes_for_consumables_required['malaria_rdt'], ], ) return drugs_available def did_not_run(self): - logger.debug(key="message", data="HSI_Malaria_Treatment: did not run") + logger.debug(key='message', data='HSI_Malaria_Treatment: did not run') pass @@ -1338,68 +1338,68 @@ def __init__(self, module, person_id): super().__init__(module, person_id=person_id) assert isinstance(module, Malaria) - self.TREATMENT_ID = "Malaria_Treatment_Complicated" + self.TREATMENT_ID = 'Malaria_Treatment_Complicated' self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint( { ( - "Under5OPD" - if self.sim.population.props.at[person_id, "age_years"] < 5 - else "Over5OPD" + 'Under5OPD' + if self.sim.population.props.at[person_id, 'age_years'] < 5 + else 'Over5OPD' ): 1 } ) - self.ACCEPTED_FACILITY_LEVEL = "1b" - self.BEDDAYS_FOOTPRINT = self.make_beddays_footprint({"general_bed": 5}) + self.ACCEPTED_FACILITY_LEVEL = '1b' + self.BEDDAYS_FOOTPRINT = self.make_beddays_footprint({'general_bed': 5}) def apply(self, person_id, squeeze_factor): df = self.sim.population.props # if person is not on treatment and still alive - if (df.at[person_id, "ma_tx"] == "none") and df.at[person_id, "is_alive"]: + if (df.at[person_id, 'ma_tx'] == 'none') and df.at[person_id, 'is_alive']: logger.debug( - key="message", - data=f"HSI_Malaria_Treatment_Complicated: requesting complicated malaria treatment for " - f" {person_id}", + key='message', + data=f'HSI_Malaria_Treatment_Complicated: requesting complicated malaria treatment for ' + f' {person_id}', ) if self.get_consumables( item_codes=self.module.item_codes_for_consumables_required[ - "malaria_complicated" + 'malaria_complicated' ], optional_item_codes=self.module.item_codes_for_consumables_required[ - "malaria_complicated_optional_items" + 'malaria_complicated_optional_items' ], ): logger.debug( - key="message", - data=f"HSI_Malaria_Treatment_Complicated: giving complicated malaria treatment for " - f" {person_id}", + key='message', + data=f'HSI_Malaria_Treatment_Complicated: giving complicated malaria treatment for ' + f' {person_id}', ) - df.at[person_id, "ma_tx"] = "complicated" - df.at[person_id, "ma_date_tx"] = self.sim.date - df.at[person_id, "ma_tx_counter"] += 1 + df.at[person_id, 'ma_tx'] = 'complicated' + df.at[person_id, 'ma_date_tx'] = self.sim.date + df.at[person_id, 'ma_tx_counter'] += 1 # rdt is offered as part of the treatment package # Log the test: line-list of summary information about each test - fever_present = "fever" in self.sim.modules["SymptomManager"].has_what( + fever_present = 'fever' in self.sim.modules['SymptomManager'].has_what( person_id ) person_details_for_test = { - "person_id": person_id, - "age": df.at[person_id, "age_years"], - "fever_present": fever_present, - "rdt_result": True, - "facility_level": self.ACCEPTED_FACILITY_LEVEL, - "called_by": self.TREATMENT_ID, + 'person_id': person_id, + 'age': df.at[person_id, 'age_years'], + 'fever_present': fever_present, + 'rdt_result': True, + 'facility_level': self.ACCEPTED_FACILITY_LEVEL, + 'called_by': self.TREATMENT_ID, } - logger.info(key="rdt_log", data=person_details_for_test) + logger.info(key='rdt_log', data=person_details_for_test) def did_not_run(self): logger.debug( - key="message", data="HSI_Malaria_Treatment_Complicated: did not run" + key='message', data='HSI_Malaria_Treatment_Complicated: did not run' ) @@ -1412,44 +1412,44 @@ def __init__(self, module, person_id): super().__init__(module, person_id=person_id) assert isinstance(module, Malaria) - self.TREATMENT_ID = "Malaria_Prevention_Iptp" - self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({"Over5OPD": 1}) - self.ACCEPTED_FACILITY_LEVEL = "1a" + self.TREATMENT_ID = 'Malaria_Prevention_Iptp' + self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({'Over5OPD': 1}) + self.ACCEPTED_FACILITY_LEVEL = '1a' def apply(self, person_id, squeeze_factor): df = self.sim.population.props p = self.module.parameters - if not df.at[person_id, "is_alive"] or (df.at[person_id, "ma_tx"] != "none"): + if not df.at[person_id, 'is_alive'] or (df.at[person_id, 'ma_tx'] != 'none'): return # IPTp contra-indicated if currently on cotrimoxazole - if "Hiv" in self.sim.modules and df.at[person_id, "hv_on_cotrimoxazole"]: + if 'Hiv' in self.sim.modules and df.at[person_id, 'hv_on_cotrimoxazole']: return logger.debug( - key="message", - data=f"HSI_MalariaIPTp: requesting IPTp for person {person_id}", + key='message', + data=f'HSI_MalariaIPTp: requesting IPTp for person {person_id}', ) # request the treatment if self.get_consumables( - self.module.item_codes_for_consumables_required["malaria_iptp"] + self.module.item_codes_for_consumables_required['malaria_iptp'] ): logger.debug( - key="message", - data=f"HSI_MalariaIPTp: giving IPTp for person {person_id}", + key='message', + data=f'HSI_MalariaIPTp: giving IPTp for person {person_id}', ) - df.at[person_id, "ma_iptp"] = True + df.at[person_id, 'ma_iptp'] = True # if currently infected, IPTp will clear the infection - df.at[person_id, "ma_is_infected"] = False - df.at[person_id, "ma_inf_type"] = "none" + df.at[person_id, 'ma_is_infected'] = False + df.at[person_id, 'ma_inf_type'] = 'none' # clear any symptoms - self.sim.modules["SymptomManager"].clear_symptoms( + self.sim.modules['SymptomManager'].clear_symptoms( person_id=person_id, disease_module=self.module ) @@ -1457,12 +1457,12 @@ def apply(self, person_id, squeeze_factor): self.sim.schedule_event( MalariaEndIPTpProtection(person_id=person_id, module=self.module), self.sim.date - + pd.DateOffset(days=7 * p["duration_iptp_protection_weeks"]), + + pd.DateOffset(days=7 * p['duration_iptp_protection_weeks']), ) def did_not_run(self): - logger.debug(key="message", data="HSI_MalariaIPTp: did not run") + logger.debug(key='message', data='HSI_MalariaIPTp: did not run') pass @@ -1483,7 +1483,7 @@ def apply(self, population): * clears parasites if treated """ - logger.debug(key="message", data="MalariaUpdateEvent") + logger.debug(key='message', data='MalariaUpdateEvent') df = self.sim.population.props p = self.module.parameters @@ -1492,45 +1492,45 @@ def apply(self, population): # assign symptoms # find those with schedule date of symptoms = today new_symptomatic_clinical = df.loc[ - df.is_alive & (df.ma_inf_type == "clinical") & (df.ma_date_symptoms == now) + df.is_alive & (df.ma_inf_type == 'clinical') & (df.ma_date_symptoms == now) ].index new_symptomatic_severe = df.loc[ - df.is_alive & (df.ma_inf_type == "severe") & (df.ma_date_symptoms == now) + df.is_alive & (df.ma_inf_type == 'severe') & (df.ma_date_symptoms == now) ].index new_symptomatic_pregnant = df.loc[ df.is_alive - & ((df.ma_inf_type == "clinical") | (df.ma_inf_type == "severe")) + & ((df.ma_inf_type == 'clinical') | (df.ma_inf_type == 'severe')) & df.is_pregnant & (df.ma_date_symptoms == now) ].index # assign clinical symptoms - self.sim.modules["SymptomManager"].change_symptom( + self.sim.modules['SymptomManager'].change_symptom( person_id=new_symptomatic_clinical, - symptom_string=["fever", "headache", "vomiting", "stomachache"], - add_or_remove="+", + symptom_string=['fever', 'headache', 'vomiting', 'stomachache'], + add_or_remove='+', disease_module=self.module, date_of_onset=now, duration_in_days=None, # remove duration as symptoms cleared by MalariaCureEvent ) # assign symptoms if pregnant - self.sim.modules["SymptomManager"].change_symptom( + self.sim.modules['SymptomManager'].change_symptom( person_id=new_symptomatic_pregnant, - symptom_string="severe_anaemia", - add_or_remove="+", + symptom_string='severe_anaemia', + add_or_remove='+', disease_module=self.module, date_of_onset=now, duration_in_days=None, # remove duration as symptoms cleared by MalariaCureEvent ) # assign severe symptom - self.sim.modules["SymptomManager"].change_symptom( + self.sim.modules['SymptomManager'].change_symptom( person_id=new_symptomatic_severe, - symptom_string="severe_malaria", - add_or_remove="+", + symptom_string='severe_malaria', + add_or_remove='+', disease_module=self.module, date_of_onset=now, duration_in_days=None, # remove duration as symptoms cleared by MalariaCureEvent @@ -1542,19 +1542,19 @@ def apply(self, population): ) # clinical counter - df.loc[all_new_infections, "ma_clinical_counter"] += 1 - df.loc[all_new_infections, "ma_is_infected"] = True + df.loc[all_new_infections, 'ma_clinical_counter'] += 1 + df.loc[all_new_infections, 'ma_is_infected'] = True # sample those scheduled for rdt eligible_for_rdt = df.loc[df.is_alive & (df.ma_date_symptoms == now)].index selected_for_rdt = ( self.module.rng.random_sample(size=len(eligible_for_rdt)) - < p["prob_malaria_case_tests"] + < p['prob_malaria_case_tests'] ) for idx in eligible_for_rdt[selected_for_rdt]: - self.sim.modules["HealthSystem"].schedule_hsi_event( - HSI_Malaria_rdt(self.module, person_id=idx, facility_level="1a"), + self.sim.modules['HealthSystem'].schedule_hsi_event( + HSI_Malaria_rdt(self.module, person_id=idx, facility_level='1a'), priority=1, topen=random_date( now + DateOffset(days=1), now + DateOffset(days=4), self.module.rng @@ -1571,16 +1571,16 @@ def apply(self, population): clinical_and_treated = df.index[ df.is_alive & (df.ma_date_tx < (self.sim.date - DateOffset(days=5))) - & (df.ma_inf_type == "clinical") - & (random_draw < p["prob_of_treatment_success"]) + & (df.ma_inf_type == 'clinical') + & (random_draw < p['prob_of_treatment_success']) ] # select people with severe malaria and treatment for at least 7 days severe_and_treated = df.index[ df.is_alive & (df.ma_date_tx < (self.sim.date - DateOffset(days=7))) - & (df.ma_inf_type == "severe") - & (random_draw < p["prob_of_treatment_success"]) + & (df.ma_inf_type == 'severe') + & (random_draw < p['prob_of_treatment_success']) ] # create list of all cases to be resolved through treatment @@ -1588,30 +1588,30 @@ def apply(self, population): set(clinical_and_treated).union(severe_and_treated) ) - self.sim.modules["SymptomManager"].clear_symptoms( + self.sim.modules['SymptomManager'].clear_symptoms( person_id=infections_to_clear, disease_module=self.module ) # change properties - df.loc[infections_to_clear, "ma_tx"] = "none" - df.loc[infections_to_clear, "ma_is_infected"] = False - df.loc[infections_to_clear, "ma_inf_type"] = "none" + df.loc[infections_to_clear, 'ma_tx'] = 'none' + df.loc[infections_to_clear, 'ma_is_infected'] = False + df.loc[infections_to_clear, 'ma_inf_type'] = 'none' # UNTREATED or TREATMENT FAILURE # if not treated or treatment failed, self-cure occurs after 6 days of symptoms # but parasites remain in blood clinical_not_treated = df.index[ df.is_alive - & (df.ma_inf_type == "clinical") + & (df.ma_inf_type == 'clinical') & (df.ma_date_symptoms < (self.sim.date - DateOffset(days=6))) ] - self.sim.modules["SymptomManager"].clear_symptoms( + self.sim.modules['SymptomManager'].clear_symptoms( person_id=clinical_not_treated, disease_module=self.module ) # change properties - df.loc[clinical_not_treated, "ma_inf_type"] = "asym" + df.loc[clinical_not_treated, 'ma_inf_type'] = 'asym' class MalariaParasiteClearanceEvent(RegularEvent, PopulationScopeEventMixin): @@ -1620,8 +1620,8 @@ def __init__(self, module): def apply(self, population): logger.debug( - key="message", - data="MalariaParasiteClearanceEvent: parasite clearance for malaria cases", + key='message', + data='MalariaParasiteClearanceEvent: parasite clearance for malaria cases', ) df = self.sim.population.props @@ -1630,12 +1630,12 @@ def apply(self, population): # select people infected at least a period ago equal to the duration of asymptomatic infection asym_inf = df.index[ df.is_alive - & (df.ma_inf_type == "asym") - & (df.ma_date_infected < (self.sim.date - DateOffset(days=p["dur_asym"]))) + & (df.ma_inf_type == 'asym') + & (df.ma_date_infected < (self.sim.date - DateOffset(days=p['dur_asym']))) ] - df.loc[asym_inf, "ma_inf_type"] = "none" - df.loc[asym_inf, "ma_is_infected"] = False + df.loc[asym_inf, 'ma_inf_type'] = 'none' + df.loc[asym_inf, 'ma_is_infected'] = False # --------------------------------------------------------------------------------- @@ -1681,48 +1681,48 @@ def apply(self, population): # using clinical counter # sum all the counters for previous year clin_episodes = df[ - "ma_clinical_counter" + 'ma_clinical_counter' ].sum() # clinical episodes (inc severe) inc_counter_1000py = (clin_episodes / pop) * 1000 clin_preg_episodes = df[ - "ma_clinical_preg_counter" + 'ma_clinical_preg_counter' ].sum() # clinical episodes in pregnant women (inc severe) summary = { - "number_new_cases": tmp, - "population": pop, - "inc_1000py": inc_1000py, - "inc_1000py_hiv": inc_1000py_hiv, - "new_cases_2_10": tmp2, - "population2_10": pop2_10, - "inc_1000py_2_10": inc_1000py_2_10, - "inc_clin_counter": inc_counter_1000py, - "clinical_preg_counter": clin_preg_episodes, + 'number_new_cases': tmp, + 'population': pop, + 'inc_1000py': inc_1000py, + 'inc_1000py_hiv': inc_1000py_hiv, + 'new_cases_2_10': tmp2, + 'population2_10': pop2_10, + 'inc_1000py_2_10': inc_1000py_2_10, + 'inc_clin_counter': inc_counter_1000py, + 'clinical_preg_counter': clin_preg_episodes, } logger.info( - key="incidence", + key='incidence', data=summary, - description="Summary of incident malaria cases", + description='Summary of incident malaria cases', ) # ------------------------------------ RUNNING COUNTS ------------------------------------ - counts = {"none": 0, "asym": 0, "clinical": 0, "severe": 0} - counts.update(df.loc[df.is_alive, "ma_inf_type"].value_counts().to_dict()) + counts = {'none': 0, 'asym': 0, 'clinical': 0, 'severe': 0} + counts.update(df.loc[df.is_alive, 'ma_inf_type'].value_counts().to_dict()) logger.info( - key="status_counts", + key='status_counts', data=counts, - description="Running counts of incident malaria cases", + description='Running counts of incident malaria cases', ) # ------------------------------------ PARASITE PREVALENCE BY AGE ------------------------------------ # includes all parasite positive cases: some may have low parasitaemia (undetectable) child2_10_inf = len( - df[df.is_alive & (df.ma_inf_type != "none") & (df.age_years.between(2, 10))] + df[df.is_alive & (df.ma_inf_type != 'none') & (df.age_years.between(2, 10))] ) # population size - children @@ -1735,25 +1735,25 @@ def apply(self, population): total_clin = len( df[ df.is_alive - & ((df.ma_inf_type == "clinical") | (df.ma_inf_type == "severe")) + & ((df.ma_inf_type == 'clinical') | (df.ma_inf_type == 'severe')) ] ) pop2 = len(df[df.is_alive]) prev_clin = total_clin / pop2 prev = { - "child2_10_prev": child_prev, - "clinical_prev": prev_clin, + 'child2_10_prev': child_prev, + 'clinical_prev': prev_clin, } - logger.info(key="prevalence", data=prev, description="Prevalence malaria cases") + logger.info(key='prevalence', data=prev, description='Prevalence malaria cases') # ------------------------------------ CO-INFECTION PREVALENCE ------------------------------------ - if "Hiv" in self.sim.modules: + if 'Hiv' in self.sim.modules: # number of people with both HIV and clinical/severe malaria # output is malaria prevalence in HIV pop coinfection_num = len( - df[df.is_alive & (df.ma_inf_type != "none") & df.hv_inf] + df[df.is_alive & (df.ma_inf_type != 'none') & df.hv_inf] ) # hiv population @@ -1763,20 +1763,20 @@ def apply(self, population): prev_malaria_in_hiv_population = coinfection_num / hiv_infected # proportion of malaria cases with concurrent HIV infection - malaria_infected = len(df[df.is_alive & (df.ma_inf_type != "none")]) + malaria_infected = len(df[df.is_alive & (df.ma_inf_type != 'none')]) prop_malaria_cases_with_hiv = coinfection_num / malaria_infected coinfection_prevalence = { - "coinfection_num": coinfection_num, - "prev_malaria_in_hiv_population": prev_malaria_in_hiv_population, - "prop_malaria_cases_with_hiv": prop_malaria_cases_with_hiv, + 'coinfection_num': coinfection_num, + 'prev_malaria_in_hiv_population': prev_malaria_in_hiv_population, + 'prop_malaria_cases_with_hiv': prop_malaria_cases_with_hiv, } logger.info( - key="coinfection_prevalence", + key='coinfection_prevalence', data=coinfection_prevalence, - description="Co-infection prevalence", + description='Co-infection prevalence', ) @@ -1793,34 +1793,34 @@ def apply(self, population): # prop clinical episodes which had treatment, all ages # sum all the counters for previous year - dx = df["ma_dx_counter"].sum() # treatment (inc severe) - tx = df["ma_tx_counter"].sum() # treatment (inc severe) - clin = df["ma_clinical_counter"].sum() # clinical episodes (inc severe) + dx = df['ma_dx_counter'].sum() # treatment (inc severe) + tx = df['ma_tx_counter'].sum() # treatment (inc severe) + clin = df['ma_clinical_counter'].sum() # clinical episodes (inc severe) dx_coverage = dx / clin if clin else 0 tx_coverage = tx / clin if clin else 0 treatment = { - "number_diagnosed": dx, - "number_treated": tx, - "number_clinical episodes": clin, - "proportion_diagnosed": dx_coverage, - "treatment_coverage": tx_coverage, + 'number_diagnosed': dx, + 'number_treated': tx, + 'number_clinical episodes': clin, + 'proportion_diagnosed': dx_coverage, + 'treatment_coverage': tx_coverage, } logger.info( - key="tx_coverage", data=treatment, description="Treatment of malaria cases" + key='tx_coverage', data=treatment, description='Treatment of malaria cases' ) # reset all counters logger.debug( - key="message", data=f"Resetting the malaria counter {self.sim.date}" + key='message', data=f'Resetting the malaria counter {self.sim.date}' ) - df["ma_clinical_counter"] = 0 - df["ma_tx_counter"] = 0 - df["ma_dx_counter"] = 0 - df["ma_clinical_preg_counter"] = 0 + df['ma_clinical_counter'] = 0 + df['ma_tx_counter'] = 0 + df['ma_dx_counter'] = 0 + df['ma_clinical_preg_counter'] = 0 class MalariaPrevDistrictLoggingEvent(RegularEvent, PopulationScopeEventMixin): @@ -1835,23 +1835,23 @@ def apply(self, population): # ------------------------------------ PREVALENCE OF INFECTION ------------------------------------ infected = ( df[df.is_alive & df.ma_is_infected] - .groupby("district_num_of_residence") + .groupby('district_num_of_residence') .size() ) - pop = df[df.is_alive].groupby("district_num_of_residence").size() + pop = df[df.is_alive].groupby('district_num_of_residence').size() prev = infected / pop prev_ed = prev.fillna(0) assert prev_ed.all() >= 0 # checks assert prev_ed.all() <= 1 logger.info( - key="prev_district", + key='prev_district', data=prev_ed.to_dict(), - description="District estimates of malaria prevalence", + description='District estimates of malaria prevalence', ) logger.info( - key="pop_district", + key='pop_district', data=pop.to_dict(), - description="District population sizes", + description='District population sizes', ) From 4bcb25da41de0cdc8c472e8620db72377d3f0f1b Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 28 May 2024 16:34:35 +0100 Subject: [PATCH 079/131] remove unneeded resource files --- resources/ResourceFile_HIV.xlsx | 4 ++-- resources/~$ResourceFile_HIV.xlsx | 3 --- resources/~$ResourceFile_TB.xlsx | 3 --- 3 files changed, 2 insertions(+), 8 deletions(-) delete mode 100644 resources/~$ResourceFile_HIV.xlsx delete mode 100644 resources/~$ResourceFile_TB.xlsx diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index 841d0f10ff..fee747159e 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2e350ab8e94335c48220455cd573386b67064675ec4ebe126a345234aeb70d5 -size 161944 +oid sha256:184d652d8b03fa61ede128af2b62b8f83fc7beca420fae61338ea3b8bc4db5a8 +size 161946 diff --git a/resources/~$ResourceFile_HIV.xlsx b/resources/~$ResourceFile_HIV.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/~$ResourceFile_HIV.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 diff --git a/resources/~$ResourceFile_TB.xlsx b/resources/~$ResourceFile_TB.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/~$ResourceFile_TB.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 From f67fa42dfa0de25a0ded5042d650295884038d9b Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 28 May 2024 16:35:00 +0100 Subject: [PATCH 080/131] remove unneeded resource files --- resources/malaria/~$ResourceFile_malaria.xlsx | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 resources/malaria/~$ResourceFile_malaria.xlsx diff --git a/resources/malaria/~$ResourceFile_malaria.xlsx b/resources/malaria/~$ResourceFile_malaria.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/malaria/~$ResourceFile_malaria.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 From 7082b51ef33779e65925156d84615cf2c1ce41c3 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 29 May 2024 09:29:28 +0100 Subject: [PATCH 081/131] edit filepaths --- tests/test_HTMscaleup.py | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/tests/test_HTMscaleup.py b/tests/test_HTMscaleup.py index 322b99006b..e93ff34053 100644 --- a/tests/test_HTMscaleup.py +++ b/tests/test_HTMscaleup.py @@ -68,10 +68,7 @@ def get_sim(seed, scaleup_hiv=False, scaleup_tb=False, scaleup_malaria=False, sc def check_initial_params(sim): - original_params = pd.read_excel( - os.path.join(resourcefilepath, "ResourceFile_HIV.xlsx"), - sheet_name="parameters", - ) + original_params = pd.read_excel(Path(resourcefilepath) / "ResourceFile_HIV.xlsx", sheet_name='parameters') # todo do we need to be exhaustive and check every parameter here? # check initial parameters @@ -91,10 +88,7 @@ def test_hiv_scale_up(seed): """ test hiv program scale-up changes parameters correctly and on correct date """ - workbook = pd.read_excel( - os.path.join(resourcefilepath, "ResourceFile_HIV.xlsx"), - sheet_name=None, - ) + workbook = pd.read_excel(Path(resourcefilepath) / "ResourceFile_HIV.xlsx", sheet_name=None) # Load data on HIV prevalence original_params = workbook["parameters"] @@ -124,10 +118,8 @@ def test_hiv_scale_up(seed): new_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] # check malaria parameters unchanged - mal_workbook = pd.read_excel( - os.path.join(resourcefilepath, "malaria/ResourceFile_Malaria.xlsx"), - sheet_name=None, - ) + mal_workbook = pd.read_excel(Path(resourcefilepath) / "malaria" / "ResourceFile_Malaria.xlsx", sheet_name=None) + mal_original_params = mal_workbook["parameters"] mal_rdt_testing = mal_workbook["WHO_TestData2023"] @@ -143,10 +135,8 @@ def test_hiv_scale_up(seed): mal_original_params.parameter_name == "itn", "value"].values[0] # check tb parameters unchanged - tb_workbook = pd.read_excel( - os.path.join(resourcefilepath, "ResourceFile_TB.xlsx"), - sheet_name=None, - ) + tb_workbook = pd.read_excel(Path(resourcefilepath) / "ResourceFile_TB.xlsx", sheet_name=None) + tb_original_params = tb_workbook["parameters"] tb_testing = tb_workbook["NTP2019"] @@ -168,10 +158,7 @@ def test_htm_scale_up(seed): """ test hiv/tb/malaria program scale-up changes parameters correctly and on correct date """ - workbook = pd.read_excel( - os.path.join(resourcefilepath, "ResourceFile_HIV.xlsx"), - sheet_name=None, - ) + workbook = pd.read_excel(Path(resourcefilepath) / "ResourceFile_HIV.xlsx", sheet_name=None) # Load data on HIV prevalence original_params = workbook["parameters"] From 721fe0b3d6072eb981e0d5e79d85388407aa2912 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 29 May 2024 09:35:33 +0100 Subject: [PATCH 082/131] isort for imports --- src/tlo/methods/hiv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index ee73fa9224..abfce7c795 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -30,7 +30,7 @@ import numpy as np import pandas as pd -from tlo import DAYS_IN_YEAR, DateOffset, Module, Parameter, Property, Types, logging, Date +from tlo import DAYS_IN_YEAR, Date, DateOffset, Module, Parameter, Property, Types, logging from tlo.core import IndividualPropertyUpdates from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent from tlo.lm import LinearModel, LinearModelType, Predictor From 7782350953998f42bd377f2edd62d5da29274a38 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 29 May 2024 10:58:07 +0100 Subject: [PATCH 083/131] edit ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx to remove 2024 missing value in malaria Rate_rdt_testing --- ...sourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx index ff88584e3f..955002f197 100644 --- a/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx +++ b/resources/ResourceFile_Improved_Healthsystem_And_Healthcare_Seeking.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da900375dda86f999e744bfb6d6dec7347d5b13f176046ba182740421a43d256 +oid sha256:a0bd18fa6cc69b09b780f11b610f7a1c87f0ff7030d266e3f6d8eb938211da60 size 48274 From e2c81e21a376e444882e6305e2e49ff56abb2771 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 29 May 2024 13:25:15 +0100 Subject: [PATCH 084/131] fix filename for test_HTMscaleup.py --- tests/test_HTMscaleup.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/test_HTMscaleup.py b/tests/test_HTMscaleup.py index e93ff34053..504240f293 100644 --- a/tests/test_HTMscaleup.py +++ b/tests/test_HTMscaleup.py @@ -68,7 +68,8 @@ def get_sim(seed, scaleup_hiv=False, scaleup_tb=False, scaleup_malaria=False, sc def check_initial_params(sim): - original_params = pd.read_excel(Path(resourcefilepath) / "ResourceFile_HIV.xlsx", sheet_name='parameters') + original_params = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_HIV.xlsx', + sheet_name='parameters') # todo do we need to be exhaustive and check every parameter here? # check initial parameters @@ -88,7 +89,8 @@ def test_hiv_scale_up(seed): """ test hiv program scale-up changes parameters correctly and on correct date """ - workbook = pd.read_excel(Path(resourcefilepath) / "ResourceFile_HIV.xlsx", sheet_name=None) + # workbook = pd.read_excel(Path(resourcefilepath) / "ResourceFile_HIV.xlsx", sheet_name=None) + workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_HIV.xlsx', sheet_name=None) # Load data on HIV prevalence original_params = workbook["parameters"] @@ -107,7 +109,8 @@ def test_hiv_scale_up(seed): sim.simulate(end_date=scaleup_start_date + pd.DateOffset(days=1)) # check HIV parameters changed - assert sim.modules["Hiv"].parameters["beta"] < original_params.loc[original_params.parameter_name == "beta", "value"].values[0] + assert sim.modules["Hiv"].parameters["beta"] < original_params.loc[ + original_params.parameter_name == "beta", "value"].values[0] assert sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] == new_params.loc[ new_params.parameter == "prob_prep_for_fsw_after_hiv_test", "scaleup_value"].values[0] assert sim.modules["Hiv"].parameters["prob_prep_for_agyw"] == new_params.loc[ @@ -118,7 +121,8 @@ def test_hiv_scale_up(seed): new_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] # check malaria parameters unchanged - mal_workbook = pd.read_excel(Path(resourcefilepath) / "malaria" / "ResourceFile_Malaria.xlsx", sheet_name=None) + mal_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_Malaria.xlsx', + sheet_name=None) mal_original_params = mal_workbook["parameters"] mal_rdt_testing = mal_workbook["WHO_TestData2023"] @@ -135,7 +139,8 @@ def test_hiv_scale_up(seed): mal_original_params.parameter_name == "itn", "value"].values[0] # check tb parameters unchanged - tb_workbook = pd.read_excel(Path(resourcefilepath) / "ResourceFile_TB.xlsx", sheet_name=None) + tb_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_TB.xlsx', + sheet_name=None) tb_original_params = tb_workbook["parameters"] tb_testing = tb_workbook["NTP2019"] @@ -158,7 +163,7 @@ def test_htm_scale_up(seed): """ test hiv/tb/malaria program scale-up changes parameters correctly and on correct date """ - workbook = pd.read_excel(Path(resourcefilepath) / "ResourceFile_HIV.xlsx", sheet_name=None) + workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_HIV.xlsx', sheet_name=None) # Load data on HIV prevalence original_params = workbook["parameters"] @@ -178,7 +183,8 @@ def test_htm_scale_up(seed): sim.simulate(end_date=scaleup_start_date + pd.DateOffset(days=1)) # check HIV parameters changed - assert sim.modules["Hiv"].parameters["beta"] < original_params.loc[original_params.parameter_name == "beta", "value"].values[0] + assert sim.modules["Hiv"].parameters["beta"] < original_params.loc[ + original_params.parameter_name == "beta", "value"].values[0] assert sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] == new_params.loc[ new_params.parameter == "prob_prep_for_fsw_after_hiv_test", "scaleup_value"].values[0] assert sim.modules["Hiv"].parameters["prob_prep_for_agyw"] == new_params.loc[ From c21fbc36ef6696b529b498d10ca559e6c334f82e Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 10 Jun 2024 10:42:10 +0100 Subject: [PATCH 085/131] set scaleup parameters separately in each module use parameters to select scaleup instead of module arguments update resourcefiles --- resources/ResourceFile_HIV.xlsx | 4 +- resources/ResourceFile_TB.xlsx | 4 +- resources/malaria/ResourceFile_malaria.xlsx | 4 +- resources/malaria/~$ResourceFile_malaria.xlsx | 3 + resources/~$ResourceFile_HIV.xlsx | 3 + resources/~$ResourceFile_TB.xlsx | 3 + src/scripts/malaria/analysis_malaria.py | 13 +- src/tlo/methods/hiv.py | 142 ++++-------------- src/tlo/methods/malaria.py | 96 +++++++++++- src/tlo/methods/tb.py | 75 ++++++++- 10 files changed, 223 insertions(+), 124 deletions(-) create mode 100644 resources/malaria/~$ResourceFile_malaria.xlsx create mode 100644 resources/~$ResourceFile_HIV.xlsx create mode 100644 resources/~$ResourceFile_TB.xlsx diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index fee747159e..510133468a 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:184d652d8b03fa61ede128af2b62b8f83fc7beca420fae61338ea3b8bc4db5a8 -size 161946 +oid sha256:17708c2eac0a3e9547dc33c22d17547871d15301f00a54e2c48a3fd719a0be8a +size 161505 diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index 21bf5ac66c..96cb0de2ab 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ccb3e12476fd218e7c460de4ec9ea289dae447ace28b7dd1b0803347091f7690 -size 55619 +oid sha256:a63bf90a29a5b99498e50fed0c670cb81ddc2a93ce3223c94a7e900ed9aa3050 +size 55601 diff --git a/resources/malaria/ResourceFile_malaria.xlsx b/resources/malaria/ResourceFile_malaria.xlsx index b7fb10682f..3f533f2e7f 100644 --- a/resources/malaria/ResourceFile_malaria.xlsx +++ b/resources/malaria/ResourceFile_malaria.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d120e4a196a1907c995b712f77c0f5bb16e8dd6ea24ecb7d27157e4b6afb0bc5 -size 68549 +oid sha256:f5f5eeedf9c7b31a48ed68a887daeb0c3d77170471a143236c46d952320f8477 +size 69634 diff --git a/resources/malaria/~$ResourceFile_malaria.xlsx b/resources/malaria/~$ResourceFile_malaria.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/malaria/~$ResourceFile_malaria.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/resources/~$ResourceFile_HIV.xlsx b/resources/~$ResourceFile_HIV.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/~$ResourceFile_HIV.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/resources/~$ResourceFile_TB.xlsx b/resources/~$ResourceFile_TB.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/~$ResourceFile_TB.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/src/scripts/malaria/analysis_malaria.py b/src/scripts/malaria/analysis_malaria.py index d59182e3af..4cd00bfbdb 100644 --- a/src/scripts/malaria/analysis_malaria.py +++ b/src/scripts/malaria/analysis_malaria.py @@ -78,14 +78,21 @@ ), hiv.Hiv( resourcefilepath=resourcefilepath, - scaleup_hiv=True, scaleup_tb=True, scaleup_malaria=True, - scaleup_start_date=Date(2010, 2, 1) ), epi.Epi( resourcefilepath=resourcefilepath, ) ) +# update parameters +sim.modules["Hiv"].parameters["do_scaleup"] = True +sim.modules["Tb"].parameters["do_scaleup"] = True +sim.modules["Malaria"].parameters["do_scaleup"] = True +sim.modules["Hiv"].parameters["scaleup_start_date"] = Date(2011, 1, 1) +sim.modules["Tb"].parameters["scaleup_start_date"] = Date(2011, 1, 1) +sim.modules["Malaria"].parameters["scaleup_start_date"] = Date(2011, 1, 1) + + # Run the simulation and flush the logger sim.make_initial_population(n=popsize) sim.simulate(end_date=end_date) @@ -99,5 +106,5 @@ pickle.dump(dict(output), f, pickle.HIGHEST_PROTOCOL) # load the results -with open(outputpath / "default_run.pickle", "rb") as f: +with open(outputpath / "malaria_run.pickle", "rb") as f: output = pickle.load(f) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index abfce7c795..3527360411 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -50,9 +50,7 @@ class Hiv(Module): The HIV Disease Module """ - def __init__(self, name=None, resourcefilepath=None, run_with_checks=False, - scaleup_hiv=False, scaleup_tb=False, scaleup_malaria=False, - scaleup_start_date=pd.NaT): + def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): super().__init__(name) self.resourcefilepath = resourcefilepath @@ -60,12 +58,6 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False, assert isinstance(run_with_checks, bool) self.run_with_checks = run_with_checks - # determine whether to scale-up programs on a specified date - self.scaleup_hiv = scaleup_hiv - self.scaleup_tb = scaleup_tb - self.scaleup_malaria = scaleup_malaria - self.scaleup_start_date = scaleup_start_date - self.stored_test_numbers = [] # create empty list for storing hiv test numbers # hiv outputs needed for calibration @@ -399,9 +391,15 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False, "length of prescription for ARVs in months, same for all PLHIV", ), # ------------------ scale-up parameters for scenario analysis ------------------ # + "do_scaleup": Parameter( + Types.BOOL, + "argument to determine whether scale-up of program will be implemented"), + "scaleup_start_date": Parameter( + Types.DATE, + "date at which program scale-up will occur"), "scaleup_parameters": Parameter( Types.DATA_FRAME, - "list of parameters and values changed in scenario analysis", + "list of parameters and values changed in scenario analysis" ), } @@ -913,13 +911,15 @@ def initialise_simulation(self, sim): sim.schedule_event(HivLoggingEvent(self), sim.date + DateOffset(years=1)) # Optional: Schedule the scale-up of programs - if self.scaleup_start_date is not pd.NaT: - assert isinstance(self.scaleup_start_date, Date), "Value is not a Date object" + if self.parameters["do_scaleup"]: + scaleup_start_date = self.parameters["scaleup_start_date"] + + assert isinstance(scaleup_start_date, Date), "Value is not a Date object" # Check if scale-up start date is on or after sim start date - assert self.scaleup_start_date >= Date(2010, 1, 1), \ - f"Date {self.scaleup_start_date} is before January 1, 2010" + assert scaleup_start_date >= Date(2010, 1, 1), \ + f"Date {scaleup_start_date} is before January 1, 2010" - sim.schedule_event(ScaleUpSetupEvent(self), self.scaleup_start_date) + sim.schedule_event(ScaleUpSetupEvent(self), self.parameters["scaleup_start_date"]) # 3) Determine who has AIDS and impose the Symptoms 'aids_symptoms' @@ -1126,142 +1126,58 @@ def initialise_simulation(self, sim): def update_parameters(self): - p_hiv = self.parameters - scaled_params = p_hiv["scaleup_parameters"] - - p_tb = self.sim.modules['Tb'].parameters - p_mal = self.sim.modules['Malaria'].parameters - - scaleup_hiv = self.scaleup_hiv - scaleup_tb = self.scaleup_tb - scaleup_malaria = self.scaleup_malaria + p = self.parameters + scaled_params = p["scaleup_parameters"] - if scaleup_hiv: + if p["do_scaleup"]: # scale-up HIV program # reduce risk of HIV - applies to whole adult population - p_hiv["beta"] = p_hiv["beta"] * scaled_params.loc[( + p["beta"] = p["beta"] * scaled_params.loc[( scaled_params.parameter == "reduction_in_hiv_beta"), "scaleup_value"].values[0] # increase PrEP coverage for FSW after HIV test - p_hiv["prob_prep_for_fsw_after_hiv_test"] = scaled_params.loc[ + p["prob_prep_for_fsw_after_hiv_test"] = scaled_params.loc[ scaled_params.parameter == "prob_prep_for_fsw_after_hiv_test", "scaleup_value"].values[0] # prep poll for AGYW - target to the highest risk # increase retention to 75% for FSW and AGYW - p_hiv["prob_prep_for_agyw"] = scaled_params.loc[ + p["prob_prep_for_agyw"] = scaled_params.loc[ scaled_params.parameter == "prob_prep_for_agyw", "scaleup_value"].values[0] - p_hiv["probability_of_being_retained_on_prep_every_3_months"] = scaled_params.loc[ + p["probability_of_being_retained_on_prep_every_3_months"] = scaled_params.loc[ scaled_params.parameter == "probability_of_being_retained_on_prep_every_3_months", "scaleup_value"].values[ 0] # increase probability of VMMC after hiv test - p_hiv["prob_circ_after_hiv_test"] = scaled_params.loc[ + p["prob_circ_after_hiv_test"] = scaled_params.loc[ scaled_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] # increase testing/diagnosis rates, default 2020 0.03/0.25 -> 93% dx - p_hiv["hiv_testing_rates"]["annual_testing_rate_children"] = \ + p["hiv_testing_rates"]["annual_testing_rate_children"] = \ scaled_params.loc[ scaled_params.parameter == "annual_testing_rate_children", "scaleup_value"].values[0] - p_hiv["hiv_testing_rates"]["annual_testing_rate_adults"] = \ + p["hiv_testing_rates"]["annual_testing_rate_adults"] = \ scaled_params.loc[ scaled_params.parameter == "annual_testing_rate_adults", "scaleup_value"].values[0] # ANC testing - value for mothers and infants testing - p_hiv["prob_hiv_test_at_anc_or_delivery"] = scaled_params.loc[ + p["prob_hiv_test_at_anc_or_delivery"] = scaled_params.loc[ scaled_params.parameter == "prob_hiv_test_at_anc_or_delivery", "scaleup_value"].values[0] - p_hiv["prob_hiv_test_for_newborn_infant"] = scaled_params.loc[ + p["prob_hiv_test_for_newborn_infant"] = scaled_params.loc[ scaled_params.parameter == "prob_hiv_test_for_newborn_infant", "scaleup_value"].values[0] # prob ART start if dx, this is already 95% at 2020 - p_hiv["prob_start_art_after_hiv_test"] = scaled_params.loc[ + p["prob_start_art_after_hiv_test"] = scaled_params.loc[ scaled_params.parameter == "prob_start_art_after_hiv_test", "scaleup_value"].values[0] # viral suppression rates # adults already at 95% by 2020 # change all column values - p_hiv["prob_start_art_or_vs"]["virally_suppressed_on_art"] = \ + p["prob_start_art_or_vs"]["virally_suppressed_on_art"] = \ scaled_params.loc[ scaled_params.parameter == "virally_suppressed_on_art", "scaleup_value"].values[0] - if scaleup_tb: - # scale-up TB program - # use NTP treatment rates - p_tb["rate_testing_active_tb"]["treatment_coverage"] = scaled_params.loc[ - scaled_params.parameter == "tb_treatment_coverage", "scaleup_value"].values[0] - - # increase tb treatment success rates - p_tb["prob_tx_success_ds"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_ds", "scaleup_value"].values[0] - p_tb["prob_tx_success_mdr"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_mdr", "scaleup_value"].values[0] - p_tb["prob_tx_success_0_4"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_0_4", "scaleup_value"].values[0] - p_tb["prob_tx_success_5_14"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_5_14", "scaleup_value"].values[0] - - # change first-line testing for TB to xpert - p_tb["first_line_test"] = scaled_params.loc[ - scaled_params.parameter == "first_line_test", "scaleup_value"].values[0] - p_tb["second_line_test"] = scaled_params.loc[ - scaled_params.parameter == "second_line_test", "scaleup_value"].values[0] - - # increase coverage of IPT - p_tb["ipt_coverage"]["coverage_plhiv"] = scaled_params.loc[ - scaled_params.parameter == "ipt_coverage_plhiv", "scaleup_value"].values[0] - p_tb["ipt_coverage"]["coverage_paediatric"] = scaled_params.loc[ - scaled_params.parameter == "ipt_coverage_paediatric", "scaleup_value"].values[0] - - if scaleup_malaria: - # scale-up malaria program - # increase testing - # prob_malaria_case_tests=0.4 default - p_mal["prob_malaria_case_tests"] = scaled_params.loc[ - scaled_params.parameter == "prob_malaria_case_tests", "scaleup_value"].values[0] - - # gen pop testing rates - # annual Rate_rdt_testing=0.64 at 2023 - p_mal["rdt_testing_rates"]["Rate_rdt_testing"] = \ - scaled_params.loc[ - scaled_params.parameter == "rdt_testing_rates", "scaleup_value"].values[0] - - # treatment reaches XX - # no default between testing and treatment, governed by tx availability - - # coverage IPTp reaches XX - # given during ANC visits and MalariaIPTp Event which selects ALL eligible women - - # treatment success reaches 1 - default is currently 1 also - self.sim.modules["Malaria"].parameters["prob_of_treatment_success"] = scaled_params.loc[ - scaled_params.parameter == "prob_of_treatment_success", "scaleup_value"].values[0] - - # bednet and ITN coverage - # set IRS for 4 high-risk districts - # lookup table created in malaria read_parameters - # produces self.itn_irs called by malaria poll to draw incidence - # need to overwrite this - highrisk_distr_num = self.sim.modules["Malaria"].parameters["highrisk_districts"]["district_num"] - - # Find indices where District_Num is in highrisk_distr_num - mask = self.sim.modules["Malaria"].itn_irs['irs_rate'].index.get_level_values('District_Num').isin( - highrisk_distr_num) - - # IRS values can be 0 or 0.8 - no other value in lookup table - self.sim.modules["Malaria"].itn_irs['irs_rate'].loc[mask] = scaled_params.loc[ - scaled_params.parameter == "irs_district", "scaleup_value"].values[0] - - # set ITN for all districts - # Set these values to 0.7 - this is the max value possible in lookup table - # equivalent to 0.7 of all pop sleeping under bednet - # household coverage could be 100%, but not everyone in household sleeping under bednet - self.sim.modules["Malaria"].itn_irs['itn_rate'] = scaled_params.loc[ - scaled_params.parameter == "itn_district", "scaleup_value"].values[0] - - # itn rates for 2019 onwards - self.sim.modules["Malaria"].parameters["itn"] = scaled_params.loc[ - scaled_params.parameter == "itn", "scaleup_value"].values[0] - def on_birth(self, mother_id, child_id): """ * Initialise our properties for a newborn individual; diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 03327d9689..c370c6fe55 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -11,7 +11,7 @@ import pandas as pd -from tlo import DateOffset, Module, Parameter, Property, Types, logging +from tlo import DateOffset, Date, Module, Parameter, Property, Types, logging from tlo.core import DiagnosisFunction, IndividualPropertyUpdates from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent from tlo.lm import LinearModel, Predictor @@ -182,6 +182,16 @@ def __init__(self, name=None, resourcefilepath=None): ), 'highrisk_districts': Parameter(Types.LIST, 'list of four malaria high-risk districts' ), + # ------------------ scale-up parameters for scenario analysis ------------------ # + "do_scaleup": Parameter( + Types.BOOL, + "argument to determine whether scale-up of program will be implemented"), + "scaleup_start_date": Parameter( + Types.DATE, + "date at which program scale-up will occur"), + "scaleup_parameters": Parameter( + Types.DATA_FRAME, + "list of parameters and values changed in scenario analysis") } PROPERTIES = { @@ -242,6 +252,9 @@ def read_parameters(self, data_folder): p['clin_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_ClinInc_expanded.csv') p['sev_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_SevInc_expanded.csv') + # load parameters for scale-up projections + p["scaleup_parameters"] = workbook["scaleup_parameters"] + # check itn projected values are <=0.7 and rounded to 1dp for matching to incidence tables p['itn'] = round(p['itn'], 1) assert p['itn'] <= 0.7 @@ -654,6 +667,17 @@ def initialise_simulation(self, sim): MalariaPrevDistrictLoggingEvent(self), sim.date + DateOffset(months=1) ) + # Optional: Schedule the scale-up of programs + if self.parameters["do_scaleup"]: + scaleup_start_date = self.parameters["scaleup_start_date"] + + assert isinstance(scaleup_start_date, Date), "Value is not a Date object" + # Check if scale-up start date is on or after sim start date + assert scaleup_start_date >= Date(2010, 1, 1), \ + f"Date {scaleup_start_date} is before January 1, 2010" + + sim.schedule_event(ScaleUpSetupEvent(self), self.parameters["scaleup_start_date"]) + # 2) ----------------------------------- DIAGNOSTIC TESTS ----------------------------------- # Create the diagnostic test representing the use of RDT for malaria diagnosis # and registers it with the Diagnostic Test Manager @@ -719,6 +743,61 @@ def initialise_simulation(self, sim): 'Sulfamethoxazole + trimethropin, tablet 400 mg + 80 mg' ) + def update_parameters(self): + + p = self.parameters + scaled_params = p["scaleup_parameters"] + + if p["do_scaleup"]: + + # scale-up malaria program + # increase testing + # prob_malaria_case_tests=0.4 default + p["prob_malaria_case_tests"] = scaled_params.loc[ + scaled_params.parameter == "prob_malaria_case_tests", "scaleup_value"].values[0] + + # gen pop testing rates + # annual Rate_rdt_testing=0.64 at 2023 + p["rdt_testing_rates"]["Rate_rdt_testing"] = \ + scaled_params.loc[ + scaled_params.parameter == "rdt_testing_rates", "scaleup_value"].values[0] + + # treatment reaches XX + # no default between testing and treatment, governed by tx availability + + # coverage IPTp reaches XX + # given during ANC visits and MalariaIPTp Event which selects ALL eligible women + + # treatment success reaches 1 - default is currently 1 also + p["prob_of_treatment_success"] = scaled_params.loc[ + scaled_params.parameter == "prob_of_treatment_success", "scaleup_value"].values[0] + + # bednet and ITN coverage + # set IRS for 4 high-risk districts + # lookup table created in malaria read_parameters + # produces self.itn_irs called by malaria poll to draw incidence + # need to overwrite this + highrisk_distr_num = p["highrisk_districts"]["district_num"] + + # Find indices where District_Num is in highrisk_distr_num + mask = self.itn_irs['irs_rate'].index.get_level_values('District_Num').isin( + highrisk_distr_num) + + # IRS values can be 0 or 0.8 - no other value in lookup table + self.itn_irs['irs_rate'].loc[mask] = scaled_params.loc[ + scaled_params.parameter == "irs_district", "scaleup_value"].values[0] + + # set ITN for all districts + # Set these values to 0.7 - this is the max value possible in lookup table + # equivalent to 0.7 of all pop sleeping under bednet + # household coverage could be 100%, but not everyone in household sleeping under bednet + self.itn_irs['itn_rate'] = scaled_params.loc[ + scaled_params.parameter == "itn_district", "scaleup_value"].values[0] + + # itn rates for 2019 onwards + p["itn"] = scaled_params.loc[ + scaled_params.parameter == "itn", "scaleup_value"].values[0] + def on_birth(self, mother_id, child_id): df = self.sim.population.props df.at[child_id, 'ma_is_infected'] = False @@ -917,6 +996,21 @@ def apply(self, population): self.module.general_population_rdt_scheduler(population) +class ScaleUpSetupEvent(RegularEvent, PopulationScopeEventMixin): + """ This event exists to change parameters or functions + depending on the scenario for projections which has been set + It only occurs once on date: scaleup_start_date, + called by initialise_simulation + """ + + def __init__(self, module): + super().__init__(module, frequency=DateOffset(years=100)) + + def apply(self, population): + + self.module.update_parameters() + + class MalariaIPTp(RegularEvent, PopulationScopeEventMixin): """ malaria prophylaxis for pregnant women diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 37b55d20cc..2ba3c80571 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -9,7 +9,7 @@ import pandas as pd -from tlo import DateOffset, Module, Parameter, Property, Types, logging +from tlo import DateOffset, Date, Module, Parameter, Property, Types, logging from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent from tlo.lm import LinearModel, LinearModelType, Predictor from tlo.methods import Metadata, hiv @@ -372,6 +372,16 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): Types.INT, "last year for which data are available", ), + # ------------------ scale-up parameters for scenario analysis ------------------ # + "do_scaleup": Parameter( + Types.BOOL, + "argument to determine whether scale-up of program will be implemented"), + "scaleup_start_date": Parameter( + Types.DATE, + "date at which program scale-up will occur"), + "scaleup_parameters": Parameter( + Types.DATA_FRAME, + "list of parameters and values changed in scenario analysis") } def read_parameters(self, data_folder): @@ -409,6 +419,9 @@ def read_parameters(self, data_folder): .tolist() ) + # load parameters for scale-up projections + p["scaleup_parameters"] = workbook["scaleup_parameters"] + # 2) Get the DALY weights if "HealthBurden" in self.sim.modules.keys(): # HIV-negative @@ -839,6 +852,17 @@ def initialise_simulation(self, sim): sim.schedule_event(TbSelfCureEvent(self), sim.date) sim.schedule_event(TbActiveCasePoll(self), sim.date + DateOffset(years=1)) + # Optional: Schedule the scale-up of programs + if self.parameters["do_scaleup"]: + scaleup_start_date = self.parameters["scaleup_start_date"] + + assert isinstance(scaleup_start_date, Date), "Value is not a Date object" + # Check if scale-up start date is on or after sim start date + assert scaleup_start_date >= Date(2010, 1, 1), \ + f"Date {scaleup_start_date} is before January 1, 2010" + + sim.schedule_event(ScaleUpSetupEvent(self), self.parameters["scaleup_start_date"]) + # 2) log at the end of the year sim.schedule_event(TbLoggingEvent(self), sim.date + DateOffset(years=1)) @@ -851,6 +875,40 @@ def initialise_simulation(self, sim): TbCheckPropertiesEvent(self), sim.date + pd.DateOffset(months=1) ) + def update_parameters(self): + + p = self.parameters + scaled_params = p["scaleup_parameters"] + + if p["do_scaleup"]: + + # scale-up TB program + # use NTP treatment rates + p["rate_testing_active_tb"]["treatment_coverage"] = scaled_params.loc[ + scaled_params.parameter == "tb_treatment_coverage", "scaleup_value"].values[0] + + # increase tb treatment success rates + p["prob_tx_success_ds"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_ds", "scaleup_value"].values[0] + p["prob_tx_success_mdr"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_mdr", "scaleup_value"].values[0] + p["prob_tx_success_0_4"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_0_4", "scaleup_value"].values[0] + p["prob_tx_success_5_14"] = scaled_params.loc[ + scaled_params.parameter == "tb_prob_tx_success_5_14", "scaleup_value"].values[0] + + # change first-line testing for TB to xpert + p["first_line_test"] = scaled_params.loc[ + scaled_params.parameter == "first_line_test", "scaleup_value"].values[0] + p["second_line_test"] = scaled_params.loc[ + scaled_params.parameter == "second_line_test", "scaleup_value"].values[0] + + # increase coverage of IPT + p["ipt_coverage"]["coverage_plhiv"] = scaled_params.loc[ + scaled_params.parameter == "ipt_coverage_plhiv", "scaleup_value"].values[0] + p["ipt_coverage"]["coverage_paediatric"] = scaled_params.loc[ + scaled_params.parameter == "ipt_coverage_paediatric", "scaleup_value"].values[0] + def on_birth(self, mother_id, child_id): """Initialise properties for a newborn individual allocate IPT for child if mother diagnosed with TB @@ -1357,6 +1415,21 @@ def apply(self, population): self.module.relapse_event(population) +class ScaleUpSetupEvent(RegularEvent, PopulationScopeEventMixin): + """ This event exists to change parameters or functions + depending on the scenario for projections which has been set + It only occurs once on date: scaleup_start_date, + called by initialise_simulation + """ + + def __init__(self, module): + super().__init__(module, frequency=DateOffset(years=100)) + + def apply(self, population): + + self.module.update_parameters() + + class TbActiveEvent(RegularEvent, PopulationScopeEventMixin): """ * check for those with dates of active tb onset within last time-period From c2a459002b3bbc8f58c02a2b7a86e0ae0c24db30 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 10 Jun 2024 10:47:04 +0100 Subject: [PATCH 086/131] set scaleup parameters separately in each module use parameters to select scaleup instead of module arguments update resourcefiles --- resources/ResourceFile_HIV.xlsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index 510133468a..1a5ea64362 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17708c2eac0a3e9547dc33c22d17547871d15301f00a54e2c48a3fd719a0be8a -size 161505 +oid sha256:331e47a1bd57457c0ee8b2051ed3b106ae5f17cf1637a06cd34ab57805a4709c +size 161507 From f161de39bc37add57b2011dcdd04e05ea20000c9 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 10 Jun 2024 11:34:39 +0100 Subject: [PATCH 087/131] update resourcefiles --- resources/ResourceFile_HIV.xlsx | 4 ++-- resources/ResourceFile_TB.xlsx | 4 ++-- resources/malaria/ResourceFile_malaria.xlsx | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index 64ef25c261..cc6647c0c8 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2345032931c1360046dc7394681cc39669687888f7f8f3e42469d8add067438 -size 160376 +oid sha256:f6301a0e9fd7e1de138daf6f423c86f5d62c37ceb9394f695e28894d05207583 +size 161572 diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index d40eb40490..2b3c85fea0 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3fc295cc70b8e86c75e1725a92ceb95bc86a26d3fbe1f680db379726bcab3ab3 -size 55662 +oid sha256:2de05344eb8fed04102fd478a59e4b282eb0b374fba0131c2e0f961ff81bc6c5 +size 55669 diff --git a/resources/malaria/ResourceFile_malaria.xlsx b/resources/malaria/ResourceFile_malaria.xlsx index 70902b7480..eb28caadaf 100644 --- a/resources/malaria/ResourceFile_malaria.xlsx +++ b/resources/malaria/ResourceFile_malaria.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6ba5849e265103ee799d1982325b6fed1ef4d3df559ffce9d6790395c201fcaf -size 67562 +oid sha256:0da6c8d3485dc99dccb926d97ee70376ea9a8399e6bf02550383c19fbbc3303e +size 68714 From af5d6a9b90953e7029dafef2cfd97be376d56c7c Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 10 Jun 2024 11:43:43 +0100 Subject: [PATCH 088/131] update resourcefiles --- resources/malaria/ResourceFile_malaria.xlsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/malaria/ResourceFile_malaria.xlsx b/resources/malaria/ResourceFile_malaria.xlsx index eb28caadaf..763c1ad46a 100644 --- a/resources/malaria/ResourceFile_malaria.xlsx +++ b/resources/malaria/ResourceFile_malaria.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0da6c8d3485dc99dccb926d97ee70376ea9a8399e6bf02550383c19fbbc3303e -size 68714 +oid sha256:ff08e2532e48d94817ae98512ad77c213bcd62758f89240fc7c6d1e57cada6da +size 69599 From 7fa127f2db2722b70732c6e57d5d56a4726e1489 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 10 Jun 2024 11:50:26 +0100 Subject: [PATCH 089/131] set up script to test scenarios - scale-up of HTM programs --- .../scenario_analyses/analysis_htm_scaleup.py | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 src/scripts/scenario_analyses/analysis_htm_scaleup.py diff --git a/src/scripts/scenario_analyses/analysis_htm_scaleup.py b/src/scripts/scenario_analyses/analysis_htm_scaleup.py new file mode 100644 index 0000000000..a0025f0311 --- /dev/null +++ b/src/scripts/scenario_analyses/analysis_htm_scaleup.py @@ -0,0 +1,79 @@ + +""" +This scenario file sets up the scenarios for simulating the effects of scaling up programs + +The scenarios are: +*0 baseline mode 1 +*1 scale-up HIV program +*2 scale-up TB program +*3 scale-up malaria program + +For all scenarios, keep all default health system settings + +check the batch configuration gets generated without error: +tlo scenario-run --draw-only src/scripts/scenario_analyses/analysis_htm_scaleup.py + +Run on the batch system using: +tlo batch-submit src/scripts/scenario_analyses/analysis_htm_scaleup.py + +or locally using: +tlo scenario-run src/scripts/scenario_analyses/analysis_htm_scaleup.py + +or execute a single run: +tlo scenario-run src/scripts/scenario_analyses/analysis_htm_scaleup.py --draw 1 0 + +""" + +from pathlib import Path +from tlo import Date, logging +from tlo.methods.fullmodel import fullmodel +from tlo.scenario import BaseScenario + + +class EffectOfProgrammes(BaseScenario): + def __init__(self): + super().__init__() + self.seed = 0 + self.start_date = Date(2010, 1, 1) + self.end_date = Date(2035, 1, 1) + self.pop_size = 1_000 + self.number_of_draws = 4 + self.runs_per_draw = 1 + + def log_configuration(self): + return { + 'filename': 'scaleup_tests', + 'directory': Path('./outputs'), # <- (specified only for local running) + 'custom_levels': { + '*': logging.WARNING, + 'tlo.methods.hiv': logging.INFO, + 'tlo.methods.tb': logging.INFO, + 'tlo.methods.malaria': logging.INFO, + 'tlo.methods.demography': logging.INFO, + 'tlo.methods.healthsystem.summary': logging.INFO, + 'tlo.methods.healthburden': logging.INFO + } + } + + def modules(self): + return fullmodel(resourcefilepath=self.resources) + + def draw_parameters(self, draw_number, rng): + + return { + 'Hiv': { + 'do_scaleup': [False, True, False, False][draw_number], + }, + 'Tb': { + 'do_scaleup': [False, False, True, False][draw_number], + }, + 'Malaria': { + 'do_scaleup': [False, False, False, True][draw_number], + }, + } + + +if __name__ == '__main__': + from tlo.cli import scenario_run + + scenario_run([__file__]) From 4e8d6c0f3ac57c694f201c28f4975f3451cb9db2 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 10 Jun 2024 11:55:50 +0100 Subject: [PATCH 090/131] test runs --- src/tlo/methods/tb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 08a2cdeca4..1ba3b7b448 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -538,7 +538,7 @@ def pre_initialise_population(self): ), ] conditional_predictors = [ - Predictor("nc_diabetes").when(True, p['rr_tb_diabetes1']), + Predictor("nc_diabetes").when(True, p['rr_tb_diabetes']), ] if "CardioMetabolicDisorders" in self.sim.modules else [] self.lm["active_tb"] = LinearModel.multiplicative( From 34585250d544d23410af8003fcef6429cf7f9860 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 10 Jun 2024 12:02:12 +0100 Subject: [PATCH 091/131] add scenario switch to malaria.py --- src/scripts/scenario_analyses/analysis_htm_scaleup.py | 2 ++ src/tlo/methods/malaria.py | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/scripts/scenario_analyses/analysis_htm_scaleup.py b/src/scripts/scenario_analyses/analysis_htm_scaleup.py index a0025f0311..9e9f52ffd7 100644 --- a/src/scripts/scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/scenario_analyses/analysis_htm_scaleup.py @@ -8,6 +8,8 @@ *2 scale-up TB program *3 scale-up malaria program +scale-up occurs on the default scale-up start date (01/01/2025: in parameters list of resourcefiles) + For all scenarios, keep all default health system settings check the batch configuration gets generated without error: diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index e5a142029c..1b68dc3c57 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -594,6 +594,17 @@ def initialise_simulation(self, sim): sim.schedule_event(MalariaTxLoggingEvent(self), sim.date + DateOffset(years=1)) sim.schedule_event(MalariaPrevDistrictLoggingEvent(self), sim.date + DateOffset(months=1)) + # Optional: Schedule the scale-up of programs + if self.parameters["do_scaleup"]: + scaleup_start_date = self.parameters["scaleup_start_date"] + + assert isinstance(scaleup_start_date, Date), "Value is not a Date object" + # Check if scale-up start date is on or after sim start date + assert scaleup_start_date >= Date(2010, 1, 1), \ + f"Date {scaleup_start_date} is before January 1, 2010" + + sim.schedule_event(ScaleUpSetupEvent(self), self.parameters["scaleup_start_date"]) + # 2) ----------------------------------- DIAGNOSTIC TESTS ----------------------------------- # Create the diagnostic test representing the use of RDT for malaria diagnosis # and registers it with the Diagnostic Test Manager From bc1a8641920e1ca00b5e61c78a85bee88cc84655 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 10 Jun 2024 13:22:46 +0100 Subject: [PATCH 092/131] cherry-pick inadvertently updated files and revert --- .../ResourceFile_Oesophageal_Cancer.xlsx | 4 +-- .../bladder_cancer_analyses_single_run.py | 31 +++++++++++++------ ...se_of_death_and_disability_calibrations.py | 6 ---- src/tlo/methods/cardio_metabolic_disorders.py | 7 ++--- src/tlo/methods/enhanced_lifestyle.py | 2 +- src/tlo/methods/oesophagealcancer.py | 2 +- 6 files changed, 29 insertions(+), 23 deletions(-) diff --git a/resources/ResourceFile_Oesophageal_Cancer.xlsx b/resources/ResourceFile_Oesophageal_Cancer.xlsx index fc68b62c99..3ce8a6432e 100644 --- a/resources/ResourceFile_Oesophageal_Cancer.xlsx +++ b/resources/ResourceFile_Oesophageal_Cancer.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34ebbfadba9c02a040c453b8ee98d15a2764a5811cf47a77ff558aa125049dfa -size 10881 +oid sha256:2453935db927550987c9b47cbcb6bbe1816a2cffed31a38d5e350231f6436651 +size 10889 diff --git a/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py b/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py index 308d455f6e..ce83ced543 100644 --- a/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py +++ b/src/scripts/bladder_cancer_analyses/bladder_cancer_analyses_single_run.py @@ -9,13 +9,17 @@ from tlo.analysis.utils import parse_log_file from tlo.methods import ( bladder_cancer, + care_of_women_during_pregnancy, + contraception, demography, enhanced_lifestyle, healthburden, healthseekingbehaviour, healthsystem, - schisto, - simplified_births, + labour, + newborn_outcomes, + postnatal_supervisor, + pregnancy_supervisor, symptommanager, ) @@ -34,30 +38,39 @@ start_date = Date(2010, 1, 1) end_date = Date(2013, 1, 1) -popsize = 5000 +popsize = 19000 # Establish the simulation object log_config = { 'filename': 'LogFile', 'directory': outputpath, 'custom_levels': { - "*": logging.WARNING, - 'tlo.methods.bladder_cancer': logging.INFO, - 'tlo.methods.schisto': logging.INFO, + 'tlo.methods.demography': logging.CRITICAL, + 'tlo.methods.contraception': logging.CRITICAL, + 'tlo.methods.healthsystem': logging.CRITICAL, + 'tlo.methods.labour': logging.CRITICAL, + 'tlo.methods.healthburden': logging.CRITICAL, + 'tlo.methods.symptommanager': logging.CRITICAL, + 'tlo.methods.healthseekingbehaviour': logging.CRITICAL, + 'tlo.methods.pregnancy_supervisor': logging.CRITICAL } } sim = Simulation(start_date=start_date, seed=4, log_config=log_config) # Register the appropriate modules sim.register(demography.Demography(resourcefilepath=resourcefilepath), + care_of_women_during_pregnancy.CareOfWomenDuringPregnancy(resourcefilepath=resourcefilepath), + contraception.Contraception(resourcefilepath=resourcefilepath), enhanced_lifestyle.Lifestyle(resourcefilepath=resourcefilepath), healthsystem.HealthSystem(resourcefilepath=resourcefilepath), symptommanager.SymptomManager(resourcefilepath=resourcefilepath), healthseekingbehaviour.HealthSeekingBehaviour(resourcefilepath=resourcefilepath), healthburden.HealthBurden(resourcefilepath=resourcefilepath), - simplified_births.SimplifiedBirths(resourcefilepath=resourcefilepath), - bladder_cancer.BladderCancer(resourcefilepath=resourcefilepath), - schisto.Schisto(resourcefilepath=resourcefilepath) + labour.Labour(resourcefilepath=resourcefilepath), + newborn_outcomes.NewbornOutcomes(resourcefilepath=resourcefilepath), + pregnancy_supervisor.PregnancySupervisor(resourcefilepath=resourcefilepath), + postnatal_supervisor.PostnatalSupervisor(resourcefilepath=resourcefilepath), + bladder_cancer.BladderCancer(resourcefilepath=resourcefilepath) ) # Run the simulation and flush the logger diff --git a/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py b/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py index eaba50b485..e6fa66603d 100644 --- a/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py +++ b/src/scripts/calibration_analyses/analysis_scripts/analysis_cause_of_death_and_disability_calibrations.py @@ -474,9 +474,3 @@ def get_total_num_dalys_by_wealth_and_label(_df): output_folder=args.results_folder, resourcefilepath=Path('./resources') ) - - # apply( - # results_folder=Path("outputs/long_run_all_diseases-2024-03-05T114732Z"), - # output_folder=Path("outputs/long_run_all_diseases-2024-03-05T114732Z"), - # resourcefilepath=Path('./resources') - # ) diff --git a/src/tlo/methods/cardio_metabolic_disorders.py b/src/tlo/methods/cardio_metabolic_disorders.py index d2d1d13d93..1d5f47ecb9 100644 --- a/src/tlo/methods/cardio_metabolic_disorders.py +++ b/src/tlo/methods/cardio_metabolic_disorders.py @@ -517,7 +517,7 @@ def initialise_simulation(self, sim): # Hypertension is the only condition for which we assume some community-based testing occurs; build LM based on # age / sex self.lms_testing['hypertension'] = self.build_linear_model('hypertension', self.parameters[ - 'interval_between_polls'], lm_type='testing') + 'interval_between_polls'], lm_type='testing') for event in self.events: self.lms_event_onset[event] = self.build_linear_model(event, self.parameters['interval_between_polls'], @@ -865,7 +865,6 @@ def do_at_generic_first_appt( # Schedule follow-up HSI *if* there are any conditions to investigate: if conditions_to_investigate: - event = HSI_CardioMetabolicDisorders_Investigations( module=self, person_id=patient_id, @@ -1636,7 +1635,7 @@ def apply(self, person_id, squeeze_factor): # NB. With a probability of 1.0, this will keep occurring, and the person will never give up coming back to # pick up medication. if (m.rng.random_sample() < - m.parameters[f'{self.condition}_hsi'].get('pr_seeking_further_appt_if_drug_not_available')): + m.parameters[f'{self.condition}_hsi'].get('pr_seeking_further_appt_if_drug_not_available')): self.sim.modules['HealthSystem'].schedule_hsi_event( hsi_event=self, topen=self.sim.date + pd.DateOffset(days=1), @@ -1712,7 +1711,7 @@ def apply(self, person_id, squeeze_factor): # NB. With a probability of 1.0, this will keep occurring, and the person will never give-up coming back to # pick-up medication. if (m.rng.random_sample() < - m.parameters[f'{self.condition}_hsi'].get('pr_seeking_further_appt_if_drug_not_available')): + m.parameters[f'{self.condition}_hsi'].get('pr_seeking_further_appt_if_drug_not_available')): self.sim.modules['HealthSystem'].schedule_hsi_event( hsi_event=self, topen=self.sim.date + pd.DateOffset(days=1), diff --git a/src/tlo/methods/enhanced_lifestyle.py b/src/tlo/methods/enhanced_lifestyle.py index ffe1e5bac7..008424ec2b 100644 --- a/src/tlo/methods/enhanced_lifestyle.py +++ b/src/tlo/methods/enhanced_lifestyle.py @@ -660,7 +660,7 @@ def update_all_properties(self, df): :param df: The population dataframe """ # get months since last poll now = self.module.sim.date - months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "m")) + months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "M")) # loop through linear models dictionary and initialise each property in the population dataframe for _property_name, _model in self._models.items(): if _model['update'] is not None: diff --git a/src/tlo/methods/oesophagealcancer.py b/src/tlo/methods/oesophagealcancer.py index 32a113609c..b3a302bcd9 100644 --- a/src/tlo/methods/oesophagealcancer.py +++ b/src/tlo/methods/oesophagealcancer.py @@ -84,7 +84,7 @@ def __init__(self, name=None, resourcefilepath=None): ), "r_low_grade_dysplasia_none": Parameter( Types.REAL, - "probability per 3 months of incident low grade oesophageal dysplasia, amongst people with no " + "probabilty per 3 months of incident low grade oesophageal dysplasia, amongst people with no " "oesophageal dysplasia (men, age20, no excess alcohol, no tobacco)", ), "rr_low_grade_dysplasia_none_female": Parameter( From b57379fea8ae27bb59b96cc0731055cdfbd2d7cf Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 10 Jun 2024 13:26:30 +0100 Subject: [PATCH 093/131] isort fixes --- src/scripts/scenario_analyses/analysis_htm_scaleup.py | 1 + src/tlo/methods/malaria.py | 2 +- src/tlo/methods/tb.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/scripts/scenario_analyses/analysis_htm_scaleup.py b/src/scripts/scenario_analyses/analysis_htm_scaleup.py index 9e9f52ffd7..5ad7f0c9c4 100644 --- a/src/scripts/scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/scenario_analyses/analysis_htm_scaleup.py @@ -27,6 +27,7 @@ """ from pathlib import Path + from tlo import Date, logging from tlo.methods.fullmodel import fullmodel from tlo.scenario import BaseScenario diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 1b68dc3c57..07f171a2ea 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -11,7 +11,7 @@ import pandas as pd -from tlo import DateOffset, Date, Module, Parameter, Property, Types, logging +from tlo import Date, DateOffset, Module, Parameter, Property, Types, logging from tlo.core import DiagnosisFunction, IndividualPropertyUpdates from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent from tlo.lm import LinearModel, Predictor diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 1ba3b7b448..292ac5dca0 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -9,7 +9,7 @@ import pandas as pd -from tlo import DateOffset, Date, Module, Parameter, Property, Types, logging +from tlo import Date, DateOffset, Module, Parameter, Property, Types, logging from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent from tlo.lm import LinearModel, LinearModelType, Predictor from tlo.methods import Metadata, hiv From 8d13b172e691e870af7ec0035eaaeb2259a577e9 Mon Sep 17 00:00:00 2001 From: Tara <37845078+tdm32@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:34:48 +0100 Subject: [PATCH 094/131] Delete resources/~$ResourceFile_HIV.xlsx --- resources/~$ResourceFile_HIV.xlsx | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 resources/~$ResourceFile_HIV.xlsx diff --git a/resources/~$ResourceFile_HIV.xlsx b/resources/~$ResourceFile_HIV.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/~$ResourceFile_HIV.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 From e6c7dcf1247b9e48da5c4174cc8dcb1394d01d09 Mon Sep 17 00:00:00 2001 From: Tara <37845078+tdm32@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:34:59 +0100 Subject: [PATCH 095/131] Delete resources/~$ResourceFile_TB.xlsx --- resources/~$ResourceFile_TB.xlsx | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 resources/~$ResourceFile_TB.xlsx diff --git a/resources/~$ResourceFile_TB.xlsx b/resources/~$ResourceFile_TB.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/~$ResourceFile_TB.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 From d72487e03e439f5f48a0bcb64ba4165ef5b8f4a0 Mon Sep 17 00:00:00 2001 From: Tara <37845078+tdm32@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:35:10 +0100 Subject: [PATCH 096/131] Delete resources/malaria/~$ResourceFile_malaria.xlsx --- resources/malaria/~$ResourceFile_malaria.xlsx | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 resources/malaria/~$ResourceFile_malaria.xlsx diff --git a/resources/malaria/~$ResourceFile_malaria.xlsx b/resources/malaria/~$ResourceFile_malaria.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/malaria/~$ResourceFile_malaria.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 From b8163d4ab442d1ebe6f53174bba55e60323c63af Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 10 Jun 2024 14:00:47 +0100 Subject: [PATCH 097/131] fix failing tests --- src/tlo/methods/enhanced_lifestyle.py | 2 +- tests/test_HTMscaleup.py | 113 ++++++++++++++------------ 2 files changed, 64 insertions(+), 51 deletions(-) diff --git a/src/tlo/methods/enhanced_lifestyle.py b/src/tlo/methods/enhanced_lifestyle.py index 008424ec2b..ffe1e5bac7 100644 --- a/src/tlo/methods/enhanced_lifestyle.py +++ b/src/tlo/methods/enhanced_lifestyle.py @@ -660,7 +660,7 @@ def update_all_properties(self, df): :param df: The population dataframe """ # get months since last poll now = self.module.sim.date - months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "M")) + months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "m")) # loop through linear models dictionary and initialise each property in the population dataframe for _property_name, _model in self._models.items(): if _model['update'] is not None: diff --git a/tests/test_HTMscaleup.py b/tests/test_HTMscaleup.py index 504240f293..24f1b4254e 100644 --- a/tests/test_HTMscaleup.py +++ b/tests/test_HTMscaleup.py @@ -27,7 +27,11 @@ resourcefilepath = "resources" -def get_sim(seed, scaleup_hiv=False, scaleup_tb=False, scaleup_malaria=False, scaleup_start_date=pd.NaT): +scaleup_start_date = Date(2012, 1, 1) +end_date = scaleup_start_date + pd.DateOffset(years=1) + + +def get_sim(seed): """ register all necessary modules for the tests to run """ @@ -54,11 +58,7 @@ def get_sim(seed, scaleup_hiv=False, scaleup_tb=False, scaleup_malaria=False, sc healthseekingbehaviour.HealthSeekingBehaviour(resourcefilepath=resourcefilepath), healthburden.HealthBurden(resourcefilepath=resourcefilepath), epi.Epi(resourcefilepath=resourcefilepath), - hiv.Hiv(resourcefilepath=resourcefilepath, - scaleup_hiv=scaleup_hiv, - scaleup_tb=scaleup_tb, - scaleup_malaria=scaleup_malaria, - scaleup_start_date=scaleup_start_date), + hiv.Hiv(resourcefilepath=resourcefilepath), tb.Tb(resourcefilepath=resourcefilepath), malaria.Malaria(resourcefilepath=resourcefilepath), ) @@ -89,24 +89,25 @@ def test_hiv_scale_up(seed): """ test hiv program scale-up changes parameters correctly and on correct date """ - # workbook = pd.read_excel(Path(resourcefilepath) / "ResourceFile_HIV.xlsx", sheet_name=None) - workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_HIV.xlsx', sheet_name=None) + hiv_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_HIV.xlsx', sheet_name=None) - # Load data on HIV prevalence - original_params = workbook["parameters"] - new_params = workbook["scaleup_parameters"] - scaleup_start_date = Date(2011, 1, 1) + original_params = hiv_workbook["parameters"] + new_params = hiv_workbook["scaleup_parameters"] popsize = 100 - sim = get_sim(seed=seed, scaleup_hiv=True, scaleup_start_date=scaleup_start_date) + sim = get_sim(seed=seed) # check initial parameters check_initial_params(sim) + # update parameters + sim.modules["Hiv"].parameters["do_scaleup"] = True + sim.modules["Hiv"].parameters["scaleup_start_date"] = scaleup_start_date + # Make the population sim.make_initial_population(n=popsize) - sim.simulate(end_date=scaleup_start_date + pd.DateOffset(days=1)) + sim.simulate(end_date=end_date) # check HIV parameters changed assert sim.modules["Hiv"].parameters["beta"] < original_params.loc[ @@ -121,7 +122,7 @@ def test_hiv_scale_up(seed): new_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] # check malaria parameters unchanged - mal_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_Malaria.xlsx', + mal_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'malaria' / 'ResourceFile_Malaria.xlsx', sheet_name=None) mal_original_params = mal_workbook["parameters"] @@ -163,60 +164,72 @@ def test_htm_scale_up(seed): """ test hiv/tb/malaria program scale-up changes parameters correctly and on correct date """ - workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_HIV.xlsx', sheet_name=None) + hiv_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_HIV.xlsx', sheet_name=None) # Load data on HIV prevalence - original_params = workbook["parameters"] - new_params = workbook["scaleup_parameters"] - scaleup_start_date = Date(2011, 1, 1) + original_hiv_params = hiv_workbook["parameters"] + new_hiv_params = hiv_workbook["scaleup_parameters"] popsize = 100 - sim = get_sim(seed=seed, scaleup_hiv=True, scaleup_tb=True, scaleup_malaria=True, - scaleup_start_date=scaleup_start_date) + sim = get_sim(seed=seed) # check initial parameters check_initial_params(sim) + # update parameters + sim.modules["Hiv"].parameters["do_scaleup"] = True + sim.modules["Hiv"].parameters["scaleup_start_date"] = scaleup_start_date + sim.modules["Tb"].parameters["do_scaleup"] = True + sim.modules["Tb"].parameters["scaleup_start_date"] = scaleup_start_date + sim.modules["Malaria"].parameters["do_scaleup"] = True + sim.modules["Malaria"].parameters["scaleup_start_date"] = scaleup_start_date + # Make the population sim.make_initial_population(n=popsize) - sim.simulate(end_date=scaleup_start_date + pd.DateOffset(days=1)) + sim.simulate(end_date=end_date) # check HIV parameters changed - assert sim.modules["Hiv"].parameters["beta"] < original_params.loc[ - original_params.parameter_name == "beta", "value"].values[0] - assert sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] == new_params.loc[ - new_params.parameter == "prob_prep_for_fsw_after_hiv_test", "scaleup_value"].values[0] - assert sim.modules["Hiv"].parameters["prob_prep_for_agyw"] == new_params.loc[ - new_params.parameter == "prob_prep_for_agyw", "scaleup_value"].values[0] - assert sim.modules["Hiv"].parameters["probability_of_being_retained_on_prep_every_3_months"] == new_params.loc[ - new_params.parameter == "probability_of_being_retained_on_prep_every_3_months", "scaleup_value"].values[0] - assert sim.modules["Hiv"].parameters["prob_circ_after_hiv_test"] == new_params.loc[ - new_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] + assert sim.modules["Hiv"].parameters["beta"] < original_hiv_params.loc[ + original_hiv_params.parameter_name == "beta", "value"].values[0] + assert sim.modules["Hiv"].parameters["prob_prep_for_fsw_after_hiv_test"] == new_hiv_params.loc[ + new_hiv_params.parameter == "prob_prep_for_fsw_after_hiv_test", "scaleup_value"].values[0] + assert sim.modules["Hiv"].parameters["prob_prep_for_agyw"] == new_hiv_params.loc[ + new_hiv_params.parameter == "prob_prep_for_agyw", "scaleup_value"].values[0] + assert sim.modules["Hiv"].parameters["probability_of_being_retained_on_prep_every_3_months"] == new_hiv_params.loc[ + new_hiv_params.parameter == "probability_of_being_retained_on_prep_every_3_months", "scaleup_value"].values[0] + assert sim.modules["Hiv"].parameters["prob_circ_after_hiv_test"] == new_hiv_params.loc[ + new_hiv_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] # check malaria parameters changed - assert sim.modules["Malaria"].parameters["prob_malaria_case_tests"] == new_params.loc[ - new_params.parameter == "prob_malaria_case_tests", "scaleup_value"].values[0] - assert sim.modules["Malaria"].parameters["rdt_testing_rates"]["Rate_rdt_testing"].eq(new_params.loc[ - new_params.parameter == "rdt_testing_rates", "scaleup_value"].values[0]).all() + mal_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'malaria' / 'ResourceFile_malaria.xlsx', sheet_name=None) + new_mal_params = mal_workbook["scaleup_parameters"] + + assert sim.modules["Malaria"].parameters["prob_malaria_case_tests"] == new_mal_params.loc[ + new_mal_params.parameter == "prob_malaria_case_tests", "scaleup_value"].values[0] + assert sim.modules["Malaria"].parameters["rdt_testing_rates"]["Rate_rdt_testing"].eq(new_mal_params.loc[ + new_mal_params.parameter == "rdt_testing_rates", "scaleup_value"].values[0]).all() # some irs coverage levels should now = 1.0 assert sim.modules["Malaria"].itn_irs['irs_rate'].any() == 1.0 # itn rates for 2019 onwards - assert sim.modules["Malaria"].parameters["itn"] == new_params.loc[ - new_params.parameter == "itn", "scaleup_value"].values[0] + assert sim.modules["Malaria"].parameters["itn"] == new_mal_params.loc[ + new_mal_params.parameter == "itn", "scaleup_value"].values[0] # check tb parameters changed - assert sim.modules["Tb"].parameters["rate_testing_active_tb"]["treatment_coverage"].eq(new_params.loc[ - new_params.parameter == "tb_treatment_coverage", "scaleup_value"].values[0]).all() - assert sim.modules["Tb"].parameters["prob_tx_success_ds"] == new_params.loc[ - new_params.parameter == "tb_prob_tx_success_ds", "scaleup_value"].values[0] - assert sim.modules["Tb"].parameters["prob_tx_success_mdr"] == new_params.loc[ - new_params.parameter == "tb_prob_tx_success_mdr", "scaleup_value"].values[0] - assert sim.modules["Tb"].parameters["prob_tx_success_0_4"] == new_params.loc[ - new_params.parameter == "tb_prob_tx_success_0_4", "scaleup_value"].values[0] - assert sim.modules["Tb"].parameters["prob_tx_success_5_14"] == new_params.loc[ - new_params.parameter == "tb_prob_tx_success_5_14", "scaleup_value"].values[0] - assert sim.modules["Tb"].parameters["first_line_test"] == new_params.loc[ - new_params.parameter == "first_line_test", "scaleup_value"].values[0] + tb_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_TB.xlsx', sheet_name=None) + new_tb_params = tb_workbook["scaleup_parameters"] + + assert sim.modules["Tb"].parameters["rate_testing_active_tb"]["treatment_coverage"].eq(new_tb_params.loc[ + new_tb_params.parameter == "tb_treatment_coverage", "scaleup_value"].values[0]).all() + assert sim.modules["Tb"].parameters["prob_tx_success_ds"] == new_tb_params.loc[ + new_tb_params.parameter == "tb_prob_tx_success_ds", "scaleup_value"].values[0] + assert sim.modules["Tb"].parameters["prob_tx_success_mdr"] == new_tb_params.loc[ + new_tb_params.parameter == "tb_prob_tx_success_mdr", "scaleup_value"].values[0] + assert sim.modules["Tb"].parameters["prob_tx_success_0_4"] == new_tb_params.loc[ + new_tb_params.parameter == "tb_prob_tx_success_0_4", "scaleup_value"].values[0] + assert sim.modules["Tb"].parameters["prob_tx_success_5_14"] == new_tb_params.loc[ + new_tb_params.parameter == "tb_prob_tx_success_5_14", "scaleup_value"].values[0] + assert sim.modules["Tb"].parameters["first_line_test"] == new_tb_params.loc[ + new_tb_params.parameter == "first_line_test", "scaleup_value"].values[0] From 6304c76968d7298951ab6f40ddc19aec92e89f35 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Mon, 10 Jun 2024 14:02:22 +0100 Subject: [PATCH 098/131] rollback changes to calibration_analyses/scenarios/long_run_all_diseases.py --- .../calibration_analyses/scenarios/long_run_all_diseases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/calibration_analyses/scenarios/long_run_all_diseases.py b/src/scripts/calibration_analyses/scenarios/long_run_all_diseases.py index 7aba3499a2..4a354e026c 100644 --- a/src/scripts/calibration_analyses/scenarios/long_run_all_diseases.py +++ b/src/scripts/calibration_analyses/scenarios/long_run_all_diseases.py @@ -24,7 +24,7 @@ def __init__(self): self.end_date = Date(2031, 1, 1) # The simulation will stop before reaching this date. self.pop_size = 20_000 self.number_of_draws = 1 - self.runs_per_draw = 1 + self.runs_per_draw = 10 def log_configuration(self): return { From 63a7dc6335025204c7a1c8faba126ca243a375a3 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 11 Jun 2024 13:25:24 +0100 Subject: [PATCH 099/131] fix error in filename --- tests/test_HTMscaleup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_HTMscaleup.py b/tests/test_HTMscaleup.py index 24f1b4254e..6089311ac2 100644 --- a/tests/test_HTMscaleup.py +++ b/tests/test_HTMscaleup.py @@ -122,7 +122,7 @@ def test_hiv_scale_up(seed): new_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] # check malaria parameters unchanged - mal_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'malaria' / 'ResourceFile_Malaria.xlsx', + mal_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'malaria' / 'ResourceFile_malaria.xlsx', sheet_name=None) mal_original_params = mal_workbook["parameters"] From d06cb847d1ec2929eb3686191549eaa4d0a56a12 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 11 Jun 2024 13:26:34 +0100 Subject: [PATCH 100/131] revert timedelta format to 'M' --- src/tlo/methods/enhanced_lifestyle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tlo/methods/enhanced_lifestyle.py b/src/tlo/methods/enhanced_lifestyle.py index ffe1e5bac7..008424ec2b 100644 --- a/src/tlo/methods/enhanced_lifestyle.py +++ b/src/tlo/methods/enhanced_lifestyle.py @@ -660,7 +660,7 @@ def update_all_properties(self, df): :param df: The population dataframe """ # get months since last poll now = self.module.sim.date - months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "m")) + months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "M")) # loop through linear models dictionary and initialise each property in the population dataframe for _property_name, _model in self._models.items(): if _model['update'] is not None: From 34436032f87c5399b5c62e0c9a9887d80ebd598a Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 25 Jun 2024 14:11:58 +0100 Subject: [PATCH 101/131] merge in master --- resources/ResourceFile_HIV.xlsx | 4 ++-- resources/ResourceFile_TB.xlsx | 4 ++-- src/tlo/methods/enhanced_lifestyle.py | 2 +- src/tlo/methods/hiv.py | 2 -- src/tlo/methods/malaria.py | 1 - 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index 1cdb865eb1..2d0457a4f9 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58978c108515c3762addd18824129b2654f241d94bcc778ab17b27d0d8250593 -size 160402 +oid sha256:7ba43fe239bae65079b14afe1ea4d0f478da57131ab7cf0188a15ddcf51cbdc1 +size 161585 diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index e6c1bf80db..4dd240cfe2 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3cb13e128d4bcb3b694def108c3bd61b16508b48e389c3e5cdf8155717aab9e9 -size 55662 +oid sha256:9ed333dd0e1702faddb72acd202847acab08f515bc3357f8e06729ea1f38e072 +size 55653 diff --git a/src/tlo/methods/enhanced_lifestyle.py b/src/tlo/methods/enhanced_lifestyle.py index 008424ec2b..ffe1e5bac7 100644 --- a/src/tlo/methods/enhanced_lifestyle.py +++ b/src/tlo/methods/enhanced_lifestyle.py @@ -660,7 +660,7 @@ def update_all_properties(self, df): :param df: The population dataframe """ # get months since last poll now = self.module.sim.date - months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "M")) + months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "m")) # loop through linear models dictionary and initialise each property in the population dataframe for _property_name, _model in self._models.items(): if _model['update'] is not None: diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index d52e9abd3b..6eeebb4eb1 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -31,9 +31,7 @@ import numpy as np import pandas as pd -from tlo import DAYS_IN_YEAR, DateOffset, Module, Parameter, Property, Types, logging from tlo import DAYS_IN_YEAR, Date, DateOffset, Module, Parameter, Property, Types, logging -from tlo.core import IndividualPropertyUpdates from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent from tlo.lm import LinearModel, LinearModelType, Predictor from tlo.methods import Metadata, demography, tb diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 93c59fecb5..d408be40e1 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -13,7 +13,6 @@ from tlo import DateOffset, Module, Parameter, Property, Types, logging from tlo import Date, DateOffset, Module, Parameter, Property, Types, logging -from tlo.core import DiagnosisFunction, IndividualPropertyUpdates from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent from tlo.lm import LinearModel, Predictor from tlo.methods import Metadata From 9ba266f4c8c135a60c0ae33b3368559d73124275 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 25 Jun 2024 16:07:07 +0100 Subject: [PATCH 102/131] set up test runs for scale-up --- src/scripts/scenario_analyses/analysis_htm_scaleup.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/scripts/scenario_analyses/analysis_htm_scaleup.py b/src/scripts/scenario_analyses/analysis_htm_scaleup.py index 5ad7f0c9c4..95dcce0d99 100644 --- a/src/scripts/scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/scenario_analyses/analysis_htm_scaleup.py @@ -38,8 +38,8 @@ def __init__(self): super().__init__() self.seed = 0 self.start_date = Date(2010, 1, 1) - self.end_date = Date(2035, 1, 1) - self.pop_size = 1_000 + self.end_date = Date(2020, 1, 1) + self.pop_size = 10_000 self.number_of_draws = 4 self.runs_per_draw = 1 @@ -66,12 +66,15 @@ def draw_parameters(self, draw_number, rng): return { 'Hiv': { 'do_scaleup': [False, True, False, False][draw_number], + 'scaleup_start_date': Date(2015, 1, 1) }, 'Tb': { 'do_scaleup': [False, False, True, False][draw_number], + 'scaleup_start_date': Date(2015, 1, 1) }, 'Malaria': { 'do_scaleup': [False, False, False, True][draw_number], + 'scaleup_start_date': Date(2015, 1, 1) }, } From 975509cab5dcb28d06e57f1be2f9a1ee3a3cdc3a Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 25 Jun 2024 16:09:13 +0100 Subject: [PATCH 103/131] edit scenario start date --- src/scripts/scenario_analyses/analysis_htm_scaleup.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/scripts/scenario_analyses/analysis_htm_scaleup.py b/src/scripts/scenario_analyses/analysis_htm_scaleup.py index 95dcce0d99..5a49d2b94e 100644 --- a/src/scripts/scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/scenario_analyses/analysis_htm_scaleup.py @@ -62,19 +62,20 @@ def modules(self): return fullmodel(resourcefilepath=self.resources) def draw_parameters(self, draw_number, rng): + scaleup_date = Date(2015, 1, 1) return { 'Hiv': { 'do_scaleup': [False, True, False, False][draw_number], - 'scaleup_start_date': Date(2015, 1, 1) + 'scaleup_start_date': scaleup_date }, 'Tb': { 'do_scaleup': [False, False, True, False][draw_number], - 'scaleup_start_date': Date(2015, 1, 1) + 'scaleup_start_date': scaleup_date }, 'Malaria': { 'do_scaleup': [False, False, False, True][draw_number], - 'scaleup_start_date': Date(2015, 1, 1) + 'scaleup_start_date': scaleup_date }, } From 304c4238f6a5d99148b7d4264129f68cc9688e37 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 25 Jun 2024 16:19:28 +0100 Subject: [PATCH 104/131] change parameter scaleup_start_date to an integer value used in DateOffset, as timestamp is not json serializable for analysis runs --- resources/ResourceFile_HIV.xlsx | 4 ++-- resources/ResourceFile_TB.xlsx | 4 ++-- resources/malaria/ResourceFile_malaria.xlsx | 4 ++-- resources/~$ResourceFile_HIV.xlsx | 3 +++ resources/~$ResourceFile_TB.xlsx | 3 +++ src/scripts/scenario_analyses/analysis_htm_scaleup.py | 10 ++++++---- src/tlo/methods/hiv.py | 4 ++-- src/tlo/methods/malaria.py | 4 ++-- src/tlo/methods/tb.py | 4 ++-- 9 files changed, 24 insertions(+), 16 deletions(-) create mode 100644 resources/~$ResourceFile_HIV.xlsx create mode 100644 resources/~$ResourceFile_TB.xlsx diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index 2d0457a4f9..c65b0a31b1 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ba43fe239bae65079b14afe1ea4d0f478da57131ab7cf0188a15ddcf51cbdc1 -size 161585 +oid sha256:e4bd3cced4a5643a1a11b79b97c2c5556f179567a708306eb0406bf847640914 +size 161606 diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index 4dd240cfe2..8f2dbdd939 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ed333dd0e1702faddb72acd202847acab08f515bc3357f8e06729ea1f38e072 -size 55653 +oid sha256:28bb046aebc874b26c803be5d04d7ea1132657bcf0711cd37fd0d8da402d299c +size 55649 diff --git a/resources/malaria/ResourceFile_malaria.xlsx b/resources/malaria/ResourceFile_malaria.xlsx index 763c1ad46a..66d808e141 100644 --- a/resources/malaria/ResourceFile_malaria.xlsx +++ b/resources/malaria/ResourceFile_malaria.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff08e2532e48d94817ae98512ad77c213bcd62758f89240fc7c6d1e57cada6da -size 69599 +oid sha256:99cf607f72fab7ca0639e8987290ba0dc6e11d69ff8be6dc9cbcb87144caac1c +size 69588 diff --git a/resources/~$ResourceFile_HIV.xlsx b/resources/~$ResourceFile_HIV.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/~$ResourceFile_HIV.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/resources/~$ResourceFile_TB.xlsx b/resources/~$ResourceFile_TB.xlsx new file mode 100644 index 0000000000..d5d6d83ccc --- /dev/null +++ b/resources/~$ResourceFile_TB.xlsx @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e +size 165 diff --git a/src/scripts/scenario_analyses/analysis_htm_scaleup.py b/src/scripts/scenario_analyses/analysis_htm_scaleup.py index 5a49d2b94e..4c757850d2 100644 --- a/src/scripts/scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/scenario_analyses/analysis_htm_scaleup.py @@ -28,6 +28,8 @@ from pathlib import Path +import pandas as pd + from tlo import Date, logging from tlo.methods.fullmodel import fullmodel from tlo.scenario import BaseScenario @@ -62,20 +64,20 @@ def modules(self): return fullmodel(resourcefilepath=self.resources) def draw_parameters(self, draw_number, rng): - scaleup_date = Date(2015, 1, 1) + scaleup_start = 5 return { 'Hiv': { 'do_scaleup': [False, True, False, False][draw_number], - 'scaleup_start_date': scaleup_date + 'scaleup_start': scaleup_start }, 'Tb': { 'do_scaleup': [False, False, True, False][draw_number], - 'scaleup_start_date': scaleup_date + 'scaleup_start': scaleup_start }, 'Malaria': { 'do_scaleup': [False, False, False, True][draw_number], - 'scaleup_start_date': scaleup_date + 'scaleup_start': scaleup_start }, } diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 6eeebb4eb1..2951ab27f8 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -401,9 +401,9 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "do_scaleup": Parameter( Types.BOOL, "argument to determine whether scale-up of program will be implemented"), - "scaleup_start_date": Parameter( + "scaleup_start": Parameter( Types.DATE, - "date at which program scale-up will occur"), + "number of years after state date at which program scale-up will occur"), "scaleup_parameters": Parameter( Types.DATA_FRAME, "list of parameters and values changed in scenario analysis" diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index d408be40e1..9eabb1aa94 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -194,9 +194,9 @@ def __init__(self, name=None, resourcefilepath=None): "do_scaleup": Parameter( Types.BOOL, "argument to determine whether scale-up of program will be implemented"), - "scaleup_start_date": Parameter( + "scaleup_start": Parameter( Types.DATE, - "date at which program scale-up will occur"), + "number of years after state date at which program scale-up will occur"), "scaleup_parameters": Parameter( Types.DATA_FRAME, "list of parameters and values changed in scenario analysis") diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index fbd3b21b0a..69731e26c0 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -374,9 +374,9 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "do_scaleup": Parameter( Types.BOOL, "argument to determine whether scale-up of program will be implemented"), - "scaleup_start_date": Parameter( + "scaleup_start": Parameter( Types.DATE, - "date at which program scale-up will occur"), + "number of years after state date at which program scale-up will occur"), "scaleup_parameters": Parameter( Types.DATA_FRAME, "list of parameters and values changed in scenario analysis") From 01745defae3c078bab7cb0ac971e134d168d7573 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Tue, 25 Jun 2024 16:21:35 +0100 Subject: [PATCH 105/131] change scale-up event scheduling to use new DateOffset parameter --- src/tlo/methods/hiv.py | 4 ++-- src/tlo/methods/malaria.py | 4 ++-- src/tlo/methods/tb.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 2951ab27f8..5b82e188f4 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -910,14 +910,14 @@ def initialise_simulation(self, sim): # Optional: Schedule the scale-up of programs if self.parameters["do_scaleup"]: - scaleup_start_date = self.parameters["scaleup_start_date"] + scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start_date"]) assert isinstance(scaleup_start_date, Date), "Value is not a Date object" # Check if scale-up start date is on or after sim start date assert scaleup_start_date >= Date(2010, 1, 1), \ f"Date {scaleup_start_date} is before January 1, 2010" - sim.schedule_event(ScaleUpSetupEvent(self), self.parameters["scaleup_start_date"]) + sim.schedule_event(ScaleUpSetupEvent(self), scaleup_start_date) # 3) Determine who has AIDS and impose the Symptoms 'aids_symptoms' diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 9eabb1aa94..148686b7d5 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -598,14 +598,14 @@ def initialise_simulation(self, sim): # Optional: Schedule the scale-up of programs if self.parameters["do_scaleup"]: - scaleup_start_date = self.parameters["scaleup_start_date"] + scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start_date"]) assert isinstance(scaleup_start_date, Date), "Value is not a Date object" # Check if scale-up start date is on or after sim start date assert scaleup_start_date >= Date(2010, 1, 1), \ f"Date {scaleup_start_date} is before January 1, 2010" - sim.schedule_event(ScaleUpSetupEvent(self), self.parameters["scaleup_start_date"]) + sim.schedule_event(ScaleUpSetupEvent(self), scaleup_start_date) # 2) ----------------------------------- DIAGNOSTIC TESTS ----------------------------------- # Create the diagnostic test representing the use of RDT for malaria diagnosis diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 69731e26c0..cec2f1670a 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -859,14 +859,14 @@ def initialise_simulation(self, sim): # 2) log at the end of the year # Optional: Schedule the scale-up of programs if self.parameters["do_scaleup"]: - scaleup_start_date = self.parameters["scaleup_start_date"] + scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start_date"]) assert isinstance(scaleup_start_date, Date), "Value is not a Date object" # Check if scale-up start date is on or after sim start date assert scaleup_start_date >= Date(2010, 1, 1), \ f"Date {scaleup_start_date} is before January 1, 2010" - sim.schedule_event(ScaleUpSetupEvent(self), self.parameters["scaleup_start_date"]) + sim.schedule_event(ScaleUpSetupEvent(self), scaleup_start_date) # 2) log at the end of the year sim.schedule_event(TbLoggingEvent(self), sim.date + DateOffset(years=1)) From 0d8d8e980e76152dadc43a1b6a0a59743a6c26e4 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 26 Jun 2024 10:18:43 +0100 Subject: [PATCH 106/131] test runs --- resources/ResourceFile_HIV.xlsx | 4 ++-- resources/~$ResourceFile_HIV.xlsx | 3 --- resources/~$ResourceFile_TB.xlsx | 3 --- src/scripts/malaria/analysis_malaria.py | 6 +++--- src/scripts/scenario_analyses/analysis_htm_scaleup.py | 2 -- src/tlo/methods/hiv.py | 4 ++-- src/tlo/methods/malaria.py | 4 ++-- src/tlo/methods/tb.py | 4 ++-- 8 files changed, 11 insertions(+), 19 deletions(-) delete mode 100644 resources/~$ResourceFile_HIV.xlsx delete mode 100644 resources/~$ResourceFile_TB.xlsx diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index c65b0a31b1..d904eb06d5 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4bd3cced4a5643a1a11b79b97c2c5556f179567a708306eb0406bf847640914 -size 161606 +oid sha256:a38984e94b158cf3e6fb7fad4856b8515615854a8a151c769e08585423708cac +size 161633 diff --git a/resources/~$ResourceFile_HIV.xlsx b/resources/~$ResourceFile_HIV.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/~$ResourceFile_HIV.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 diff --git a/resources/~$ResourceFile_TB.xlsx b/resources/~$ResourceFile_TB.xlsx deleted file mode 100644 index d5d6d83ccc..0000000000 --- a/resources/~$ResourceFile_TB.xlsx +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:019349b15c524cfef4b39db4dd792de3376f4bc3da9b6b298a1fee07c4eb219e -size 165 diff --git a/src/scripts/malaria/analysis_malaria.py b/src/scripts/malaria/analysis_malaria.py index 4cd00bfbdb..b2b4217dc6 100644 --- a/src/scripts/malaria/analysis_malaria.py +++ b/src/scripts/malaria/analysis_malaria.py @@ -88,9 +88,9 @@ sim.modules["Hiv"].parameters["do_scaleup"] = True sim.modules["Tb"].parameters["do_scaleup"] = True sim.modules["Malaria"].parameters["do_scaleup"] = True -sim.modules["Hiv"].parameters["scaleup_start_date"] = Date(2011, 1, 1) -sim.modules["Tb"].parameters["scaleup_start_date"] = Date(2011, 1, 1) -sim.modules["Malaria"].parameters["scaleup_start_date"] = Date(2011, 1, 1) +sim.modules["Hiv"].parameters["scaleup_start"] = 2 +sim.modules["Tb"].parameters["scaleup_start"] = 2 +sim.modules["Malaria"].parameters["scaleup_start"] = 2 # Run the simulation and flush the logger diff --git a/src/scripts/scenario_analyses/analysis_htm_scaleup.py b/src/scripts/scenario_analyses/analysis_htm_scaleup.py index 4c757850d2..e666e32492 100644 --- a/src/scripts/scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/scenario_analyses/analysis_htm_scaleup.py @@ -28,8 +28,6 @@ from pathlib import Path -import pandas as pd - from tlo import Date, logging from tlo.methods.fullmodel import fullmodel from tlo.scenario import BaseScenario diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 5b82e188f4..5801fc25bb 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -402,7 +402,7 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): Types.BOOL, "argument to determine whether scale-up of program will be implemented"), "scaleup_start": Parameter( - Types.DATE, + Types.INT, "number of years after state date at which program scale-up will occur"), "scaleup_parameters": Parameter( Types.DATA_FRAME, @@ -910,7 +910,7 @@ def initialise_simulation(self, sim): # Optional: Schedule the scale-up of programs if self.parameters["do_scaleup"]: - scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start_date"]) + scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start"]) assert isinstance(scaleup_start_date, Date), "Value is not a Date object" # Check if scale-up start date is on or after sim start date diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 148686b7d5..f379e860c5 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -195,7 +195,7 @@ def __init__(self, name=None, resourcefilepath=None): Types.BOOL, "argument to determine whether scale-up of program will be implemented"), "scaleup_start": Parameter( - Types.DATE, + Types.INT, "number of years after state date at which program scale-up will occur"), "scaleup_parameters": Parameter( Types.DATA_FRAME, @@ -598,7 +598,7 @@ def initialise_simulation(self, sim): # Optional: Schedule the scale-up of programs if self.parameters["do_scaleup"]: - scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start_date"]) + scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start"]) assert isinstance(scaleup_start_date, Date), "Value is not a Date object" # Check if scale-up start date is on or after sim start date diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index cec2f1670a..764d3c2724 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -375,7 +375,7 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): Types.BOOL, "argument to determine whether scale-up of program will be implemented"), "scaleup_start": Parameter( - Types.DATE, + Types.INT, "number of years after state date at which program scale-up will occur"), "scaleup_parameters": Parameter( Types.DATA_FRAME, @@ -859,7 +859,7 @@ def initialise_simulation(self, sim): # 2) log at the end of the year # Optional: Schedule the scale-up of programs if self.parameters["do_scaleup"]: - scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start_date"]) + scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start"]) assert isinstance(scaleup_start_date, Date), "Value is not a Date object" # Check if scale-up start date is on or after sim start date From acae16e07842505c2dfde3d7a55ab886faa398d4 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 26 Jun 2024 19:06:04 +0100 Subject: [PATCH 107/131] set up test runs --- .../scenario_analyses/analysis_htm_scaleup.py | 39 ++++- .../scenario_analyses/scenario_plots.py | 149 ++++++++++++++++++ 2 files changed, 182 insertions(+), 6 deletions(-) create mode 100644 src/scripts/scenario_analyses/scenario_plots.py diff --git a/src/scripts/scenario_analyses/analysis_htm_scaleup.py b/src/scripts/scenario_analyses/analysis_htm_scaleup.py index e666e32492..6be810410f 100644 --- a/src/scripts/scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/scenario_analyses/analysis_htm_scaleup.py @@ -29,7 +29,20 @@ from pathlib import Path from tlo import Date, logging -from tlo.methods.fullmodel import fullmodel +# from tlo.methods.fullmodel import fullmodel +from tlo.methods import ( + demography, + enhanced_lifestyle, + epi, + healthburden, + healthseekingbehaviour, + healthsystem, + hiv, + malaria, + simplified_births, + symptommanager, + tb, +) from tlo.scenario import BaseScenario @@ -39,7 +52,7 @@ def __init__(self): self.seed = 0 self.start_date = Date(2010, 1, 1) self.end_date = Date(2020, 1, 1) - self.pop_size = 10_000 + self.pop_size = 75_000 self.number_of_draws = 4 self.runs_per_draw = 1 @@ -53,16 +66,28 @@ def log_configuration(self): 'tlo.methods.tb': logging.INFO, 'tlo.methods.malaria': logging.INFO, 'tlo.methods.demography': logging.INFO, - 'tlo.methods.healthsystem.summary': logging.INFO, - 'tlo.methods.healthburden': logging.INFO } } def modules(self): - return fullmodel(resourcefilepath=self.resources) + # return fullmodel(resourcefilepath=self.resources) + + return [ + demography.Demography(resourcefilepath=self.resources), + simplified_births.SimplifiedBirths(resourcefilepath=self.resources), + enhanced_lifestyle.Lifestyle(resourcefilepath=self.resources), + healthsystem.HealthSystem(resourcefilepath=self.resources), + symptommanager.SymptomManager(resourcefilepath=self.resources), + healthseekingbehaviour.HealthSeekingBehaviour(resourcefilepath=self.resources), + healthburden.HealthBurden(resourcefilepath=self.resources), + epi.Epi(resourcefilepath=self.resources), + hiv.Hiv(resourcefilepath=self.resources), + tb.Tb(resourcefilepath=self.resources), + malaria.Malaria(resourcefilepath=self.resources), + ] def draw_parameters(self, draw_number, rng): - scaleup_start = 5 + scaleup_start = 2 return { 'Hiv': { @@ -84,3 +109,5 @@ def draw_parameters(self, draw_number, rng): from tlo.cli import scenario_run scenario_run([__file__]) + + diff --git a/src/scripts/scenario_analyses/scenario_plots.py b/src/scripts/scenario_analyses/scenario_plots.py new file mode 100644 index 0000000000..bf4b6b8e21 --- /dev/null +++ b/src/scripts/scenario_analyses/scenario_plots.py @@ -0,0 +1,149 @@ +""" this reads in the outputs generates through analysis_htm_scaleup.py +and produces plots for HIV, TB and malaria incidence +""" + + +import datetime +from pathlib import Path + +import matplotlib.lines as mlines +import matplotlib.patches as mpatches +import matplotlib.pyplot as plt +import pandas as pd +import seaborn as sns + +from tlo import Date +from tlo.analysis.utils import ( + compare_number_of_deaths, + extract_params, + extract_results, + get_scenario_info, + get_scenario_outputs, + load_pickled_dataframes, + summarize, +) + +resourcefilepath = Path("./resources") +datestamp = datetime.date.today().strftime("__%Y_%m_%d") + +outputspath = Path("./outputs") + + +# 0) Find results_folder associated with a given batch_file (and get most recent [-1]) +results_folder = get_scenario_outputs("scaleup_tests.py", outputspath)[-1] + +# Declare path for output graphs from this script +make_graph_file_name = lambda stub: results_folder / f"{stub}.png" # noqa: E731 + +# look at one log (so can decide what to extract) +log = load_pickled_dataframes(results_folder) + +# get basic information about the results +info = get_scenario_info(results_folder) + +# 1) Extract the parameters that have varied over the set of simulations +params = extract_params(results_folder) + + +# DEATHS + + +def get_num_deaths_by_cause_label(_df): + """Return total number of Deaths by label within the TARGET_PERIOD + values are summed for all ages + df returned: rows=COD, columns=draw + """ + return _df \ + .loc[pd.to_datetime(_df.date).between(*TARGET_PERIOD)] \ + .groupby(_df['label']) \ + .size() + + +TARGET_PERIOD = (Date(2015, 1, 1), Date(2020, 1, 1)) + +num_deaths_by_cause_label = extract_results( + results_folder, + module='tlo.methods.demography', + key='death', + custom_generate_series=get_num_deaths_by_cause_label, + do_scaling=True + ) + + +def summarise_deaths_for_one_cause(results_folder, label): + """ returns mean deaths for each year of the simulation + values are aggregated across the runs of each draw + for the specified cause + """ + + results_deaths = extract_results( + results_folder, + module="tlo.methods.demography", + key="death", + custom_generate_series=( + lambda df: df.assign(year=df["date"].dt.year).groupby( + ["year", "label"])["person_id"].count() + ), + do_scaling=True, + ) + # removes multi-index + results_deaths = results_deaths.reset_index() + + # select only cause specified + tmp = results_deaths.loc[ + (results_deaths.label == label) + ] + + # group deaths by year + tmp = pd.DataFrame(tmp.groupby(["year"]).sum()) + + # get mean for each draw + mean_deaths = pd.concat({'mean': tmp.iloc[:, 1:].groupby(level=0, axis=1).mean()}, axis=1).swaplevel(axis=1) + + return mean_deaths + + +aids_deaths = summarise_deaths_for_one_cause(results_folder, 'AIDS') +tb_deaths = summarise_deaths_for_one_cause(results_folder, 'TB (non-AIDS)') +malaria_deaths = summarise_deaths_for_one_cause(results_folder, 'Malaria') + +draw_labels = ['No scale-up', 'HIV, scale-up', 'TB scale-up', 'Malaria scale-up'] + + +# Define colors for the lines +colors = sns.color_palette("Set1", 4) # Blue, Orange, Green, Red + + +# Create subplots +fig, axs = plt.subplots(3, 1, figsize=(6, 10)) + +# Plot for df1 +for i, col in enumerate(aids_deaths.columns): + axs[0].plot(aids_deaths.index, aids_deaths[col], label=draw_labels[i], color=colors[i]) +axs[0].set_title('HIV/AIDS') +axs[0].legend() +axs[0].axvline(x=2015, color='gray', linestyle='--') + +# Plot for df2 +for i, col in enumerate(tb_deaths.columns): + axs[1].plot(tb_deaths.index, tb_deaths[col], color=colors[i]) +axs[1].set_title('TB') +axs[1].axvline(x=2015, color='gray', linestyle='--') + +# Plot for df3 +for i, col in enumerate(malaria_deaths.columns): + axs[2].plot(malaria_deaths.index, malaria_deaths[col], color=colors[i]) +axs[2].set_title('Malaria') +axs[2].axvline(x=2015, color='gray', linestyle='--') + +# Set common labels +for ax in axs: + ax.set_xlabel('Years') + ax.set_ylabel('Number deaths') + +# Adjust layout +plt.tight_layout() + +# Show plot +plt.show() + From 67f2fa4d5356ce9ca0fb7439748c62f96de97c26 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 26 Jun 2024 19:07:13 +0100 Subject: [PATCH 108/131] set up test runs --- resources/ResourceFile_HIV.xlsx | 4 ++-- src/scripts/scenario_analyses/analysis_htm_scaleup.py | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index d904eb06d5..c7b8282a48 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a38984e94b158cf3e6fb7fad4856b8515615854a8a151c769e08585423708cac -size 161633 +oid sha256:1cf8bd8bd6e40988dae55c04b3fbd6904199b5fa67684902f6de6e59c51fc353 +size 161617 diff --git a/src/scripts/scenario_analyses/analysis_htm_scaleup.py b/src/scripts/scenario_analyses/analysis_htm_scaleup.py index 6be810410f..aa5a5ff21a 100644 --- a/src/scripts/scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/scenario_analyses/analysis_htm_scaleup.py @@ -29,7 +29,6 @@ from pathlib import Path from tlo import Date, logging -# from tlo.methods.fullmodel import fullmodel from tlo.methods import ( demography, enhanced_lifestyle, From 7d668de515e0447d8cc029793912fb5636f6d2d1 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 26 Jun 2024 20:43:17 +0100 Subject: [PATCH 109/131] fix failing tests --- src/tlo/methods/malaria.py | 1 - tests/test_HTMscaleup.py | 53 +++++++++++++++----------------------- 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index f379e860c5..6fedcd8c96 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -11,7 +11,6 @@ import pandas as pd -from tlo import DateOffset, Module, Parameter, Property, Types, logging from tlo import Date, DateOffset, Module, Parameter, Property, Types, logging from tlo.events import Event, IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent from tlo.lm import LinearModel, Predictor diff --git a/tests/test_HTMscaleup.py b/tests/test_HTMscaleup.py index 6089311ac2..d6570cb64b 100644 --- a/tests/test_HTMscaleup.py +++ b/tests/test_HTMscaleup.py @@ -26,9 +26,9 @@ # running interactively resourcefilepath = "resources" - -scaleup_start_date = Date(2012, 1, 1) -end_date = scaleup_start_date + pd.DateOffset(years=1) +start_date = Date(2010, 1, 1) +years_until_scale_up = 2 +end_date = start_date + pd.DateOffset(years=years_until_scale_up+1) def get_sim(seed): @@ -36,7 +36,6 @@ def get_sim(seed): register all necessary modules for the tests to run """ - start_date = Date(2010, 1, 1) sim = Simulation(start_date=start_date, seed=seed) # Register the appropriate modules @@ -68,8 +67,7 @@ def get_sim(seed): def check_initial_params(sim): - original_params = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_HIV.xlsx', - sheet_name='parameters') + original_params = pd.read_excel(resourcefilepath / 'ResourceFile_HIV.xlsx', sheet_name='parameters') # todo do we need to be exhaustive and check every parameter here? # check initial parameters @@ -89,10 +87,8 @@ def test_hiv_scale_up(seed): """ test hiv program scale-up changes parameters correctly and on correct date """ - hiv_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_HIV.xlsx', sheet_name=None) - - original_params = hiv_workbook["parameters"] - new_params = hiv_workbook["scaleup_parameters"] + original_params = pd.read_excel(resourcefilepath / 'ResourceFile_HIV.xlsx', sheet_name="parameters") + new_params = pd.read_excel(resourcefilepath / 'ResourceFile_HIV.xlsx', sheet_name="scaleup_parameters") popsize = 100 @@ -103,7 +99,7 @@ def test_hiv_scale_up(seed): # update parameters sim.modules["Hiv"].parameters["do_scaleup"] = True - sim.modules["Hiv"].parameters["scaleup_start_date"] = scaleup_start_date + sim.modules["Hiv"].parameters["scaleup_start"] = years_until_scale_up # Make the population sim.make_initial_population(n=popsize) @@ -122,11 +118,10 @@ def test_hiv_scale_up(seed): new_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] # check malaria parameters unchanged - mal_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'malaria' / 'ResourceFile_malaria.xlsx', - sheet_name=None) - - mal_original_params = mal_workbook["parameters"] - mal_rdt_testing = mal_workbook["WHO_TestData2023"] + mal_original_params = pd.read_excel(resourcefilepath / 'malaria' / 'ResourceFile_malaria.xlsx', + sheet_name="parameters") + mal_rdt_testing = pd.read_excel(resourcefilepath / 'malaria' / 'ResourceFile_malaria.xlsx', + sheet_name="WHO_TestData2023") assert sim.modules["Malaria"].parameters["prob_malaria_case_tests"] == mal_original_params.loc[ mal_original_params.parameter_name == "prob_malaria_case_tests", "value"].values[0] @@ -140,11 +135,8 @@ def test_hiv_scale_up(seed): mal_original_params.parameter_name == "itn", "value"].values[0] # check tb parameters unchanged - tb_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_TB.xlsx', - sheet_name=None) - - tb_original_params = tb_workbook["parameters"] - tb_testing = tb_workbook["NTP2019"] + tb_original_params = pd.read_excel(resourcefilepath / 'ResourceFile_TB.xlsx', sheet_name="parameters") + tb_testing = pd.read_excel(resourcefilepath / 'ResourceFile_TB.xlsx', sheet_name="NTP2019") pd.testing.assert_series_equal(sim.modules["Tb"].parameters["rate_testing_active_tb"]["treatment_coverage"], tb_testing["treatment_coverage"]) @@ -164,11 +156,9 @@ def test_htm_scale_up(seed): """ test hiv/tb/malaria program scale-up changes parameters correctly and on correct date """ - hiv_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_HIV.xlsx', sheet_name=None) - # Load data on HIV prevalence - original_hiv_params = hiv_workbook["parameters"] - new_hiv_params = hiv_workbook["scaleup_parameters"] + original_hiv_params = pd.read_excel(resourcefilepath / 'ResourceFile_HIV.xlsx', sheet_name="parameters") + new_hiv_params = pd.read_excel(resourcefilepath / 'ResourceFile_HIV.xlsx', sheet_name="scaleup_parameters") popsize = 100 @@ -179,11 +169,11 @@ def test_htm_scale_up(seed): # update parameters sim.modules["Hiv"].parameters["do_scaleup"] = True - sim.modules["Hiv"].parameters["scaleup_start_date"] = scaleup_start_date + sim.modules["Hiv"].parameters["scaleup_start"] = years_until_scale_up sim.modules["Tb"].parameters["do_scaleup"] = True - sim.modules["Tb"].parameters["scaleup_start_date"] = scaleup_start_date + sim.modules["Tb"].parameters["scaleup_start"] = years_until_scale_up sim.modules["Malaria"].parameters["do_scaleup"] = True - sim.modules["Malaria"].parameters["scaleup_start_date"] = scaleup_start_date + sim.modules["Malaria"].parameters["scaleup_start"] = years_until_scale_up # Make the population sim.make_initial_population(n=popsize) @@ -202,8 +192,8 @@ def test_htm_scale_up(seed): new_hiv_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] # check malaria parameters changed - mal_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'malaria' / 'ResourceFile_malaria.xlsx', sheet_name=None) - new_mal_params = mal_workbook["scaleup_parameters"] + new_mal_params = pd.read_excel(resourcefilepath / 'malaria' / 'ResourceFile_malaria.xlsx', + sheet_name="scaleup_parameters") assert sim.modules["Malaria"].parameters["prob_malaria_case_tests"] == new_mal_params.loc[ new_mal_params.parameter == "prob_malaria_case_tests", "scaleup_value"].values[0] @@ -217,8 +207,7 @@ def test_htm_scale_up(seed): new_mal_params.parameter == "itn", "scaleup_value"].values[0] # check tb parameters changed - tb_workbook = pd.read_excel(Path(__file__).parent.parent / 'resources' / 'ResourceFile_TB.xlsx', sheet_name=None) - new_tb_params = tb_workbook["scaleup_parameters"] + new_tb_params = pd.read_excel(resourcefilepath / 'ResourceFile_TB.xlsx', sheet_name="scaleup_parameters") assert sim.modules["Tb"].parameters["rate_testing_active_tb"]["treatment_coverage"].eq(new_tb_params.loc[ new_tb_params.parameter == "tb_treatment_coverage", "scaleup_value"].values[0]).all() From 0b3e938e6e3293c9daacdf6927cbc713f7e2b654 Mon Sep 17 00:00:00 2001 From: Tara <37845078+tdm32@users.noreply.github.com> Date: Wed, 26 Jun 2024 20:43:47 +0100 Subject: [PATCH 110/131] Update tests/test_HTMscaleup.py Co-authored-by: Tim Hallett <39991060+tbhallett@users.noreply.github.com> --- tests/test_HTMscaleup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_HTMscaleup.py b/tests/test_HTMscaleup.py index d6570cb64b..a2cd445822 100644 --- a/tests/test_HTMscaleup.py +++ b/tests/test_HTMscaleup.py @@ -20,7 +20,7 @@ tb, ) -try: +resourcefilepath = Path(os.path.dirname(__file__)) / "../resources" resourcefilepath = Path(os.path.dirname(__file__)) / "../resources" except NameError: # running interactively From fcea78b52875db65a60cdbe9686e807dc446ec09 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 26 Jun 2024 20:52:43 +0100 Subject: [PATCH 111/131] address comments on PR review --- src/tlo/methods/hiv.py | 8 ++------ src/tlo/methods/malaria.py | 4 ++-- src/tlo/methods/tb.py | 4 ++-- tests/test_HTMscaleup.py | 4 ---- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 5801fc25bb..6bb719cc8f 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -2293,7 +2293,7 @@ def apply(self, person_id): ) -class ScaleUpSetupEvent(RegularEvent, PopulationScopeEventMixin): +class ScaleUpSetupEvent(Event, PopulationScopeEventMixin): """ This event exists to change parameters or functions depending on the scenario for projections which has been set It only occurs once on date: scaleup_start_date, @@ -2301,7 +2301,7 @@ class ScaleUpSetupEvent(RegularEvent, PopulationScopeEventMixin): """ def __init__(self, module): - super().__init__(module, frequency=DateOffset(years=100)) + super().__init__(module) def apply(self, population): @@ -2476,10 +2476,6 @@ def apply(self, person_id, squeeze_factor): tclose=None, priority=0, ) - # set cap for number of repeat tests - self.counter_for_test_not_available += ( - 1 # The current appointment is included in the count. - ) if ( self.counter_for_test_not_available diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 6fedcd8c96..5a8795e81e 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -903,7 +903,7 @@ def apply(self, population): self.module.general_population_rdt_scheduler(population) -class ScaleUpSetupEvent(RegularEvent, PopulationScopeEventMixin): +class ScaleUpSetupEvent(Event, PopulationScopeEventMixin): """ This event exists to change parameters or functions depending on the scenario for projections which has been set It only occurs once on date: scaleup_start_date, @@ -911,7 +911,7 @@ class ScaleUpSetupEvent(RegularEvent, PopulationScopeEventMixin): """ def __init__(self, module): - super().__init__(module, frequency=DateOffset(years=100)) + super().__init__(module) def apply(self, population): diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 764d3c2724..05e8da5ee8 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -1420,7 +1420,7 @@ def apply(self, population): self.module.relapse_event(population) -class ScaleUpSetupEvent(RegularEvent, PopulationScopeEventMixin): +class ScaleUpSetupEvent(Event, PopulationScopeEventMixin): """ This event exists to change parameters or functions depending on the scenario for projections which has been set It only occurs once on date: scaleup_start_date, @@ -1428,7 +1428,7 @@ class ScaleUpSetupEvent(RegularEvent, PopulationScopeEventMixin): """ def __init__(self, module): - super().__init__(module, frequency=DateOffset(years=100)) + super().__init__(module) def apply(self, population): diff --git a/tests/test_HTMscaleup.py b/tests/test_HTMscaleup.py index a2cd445822..91ef0cd5f7 100644 --- a/tests/test_HTMscaleup.py +++ b/tests/test_HTMscaleup.py @@ -21,10 +21,6 @@ ) resourcefilepath = Path(os.path.dirname(__file__)) / "../resources" - resourcefilepath = Path(os.path.dirname(__file__)) / "../resources" -except NameError: - # running interactively - resourcefilepath = "resources" start_date = Date(2010, 1, 1) years_until_scale_up = 2 From 1a86e9cc807016c6e747e1128518f2f3ca174a3c Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 26 Jun 2024 20:56:37 +0100 Subject: [PATCH 112/131] isort fixes --- src/scripts/scenario_analyses/analysis_htm_scaleup.py | 8 ++++---- src/scripts/scenario_analyses/scenario_plots.py | 11 ----------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/src/scripts/scenario_analyses/analysis_htm_scaleup.py b/src/scripts/scenario_analyses/analysis_htm_scaleup.py index aa5a5ff21a..0ffd6a88c8 100644 --- a/src/scripts/scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/scenario_analyses/analysis_htm_scaleup.py @@ -52,7 +52,7 @@ def __init__(self): self.start_date = Date(2010, 1, 1) self.end_date = Date(2020, 1, 1) self.pop_size = 75_000 - self.number_of_draws = 4 + self.number_of_draws = 5 self.runs_per_draw = 1 def log_configuration(self): @@ -90,15 +90,15 @@ def draw_parameters(self, draw_number, rng): return { 'Hiv': { - 'do_scaleup': [False, True, False, False][draw_number], + 'do_scaleup': [False, True, False, False, True][draw_number], 'scaleup_start': scaleup_start }, 'Tb': { - 'do_scaleup': [False, False, True, False][draw_number], + 'do_scaleup': [False, False, True, False, True][draw_number], 'scaleup_start': scaleup_start }, 'Malaria': { - 'do_scaleup': [False, False, False, True][draw_number], + 'do_scaleup': [False, False, False, True, True][draw_number], 'scaleup_start': scaleup_start }, } diff --git a/src/scripts/scenario_analyses/scenario_plots.py b/src/scripts/scenario_analyses/scenario_plots.py index bf4b6b8e21..6f62ef542e 100644 --- a/src/scripts/scenario_analyses/scenario_plots.py +++ b/src/scripts/scenario_analyses/scenario_plots.py @@ -5,22 +5,17 @@ import datetime from pathlib import Path - -import matplotlib.lines as mlines -import matplotlib.patches as mpatches import matplotlib.pyplot as plt import pandas as pd import seaborn as sns from tlo import Date from tlo.analysis.utils import ( - compare_number_of_deaths, extract_params, extract_results, get_scenario_info, get_scenario_outputs, load_pickled_dataframes, - summarize, ) resourcefilepath = Path("./resources") @@ -109,8 +104,6 @@ def summarise_deaths_for_one_cause(results_folder, label): draw_labels = ['No scale-up', 'HIV, scale-up', 'TB scale-up', 'Malaria scale-up'] - -# Define colors for the lines colors = sns.color_palette("Set1", 4) # Blue, Orange, Green, Red @@ -136,14 +129,10 @@ def summarise_deaths_for_one_cause(results_folder, label): axs[2].set_title('Malaria') axs[2].axvline(x=2015, color='gray', linestyle='--') -# Set common labels for ax in axs: ax.set_xlabel('Years') ax.set_ylabel('Number deaths') -# Adjust layout plt.tight_layout() - -# Show plot plt.show() From a0be80c6b4112d7bc9f01cd79d38c5528204d848 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 26 Jun 2024 21:00:42 +0100 Subject: [PATCH 113/131] isort fixes --- src/scripts/scenario_analyses/scenario_plots.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scripts/scenario_analyses/scenario_plots.py b/src/scripts/scenario_analyses/scenario_plots.py index 6f62ef542e..de0ece9393 100644 --- a/src/scripts/scenario_analyses/scenario_plots.py +++ b/src/scripts/scenario_analyses/scenario_plots.py @@ -5,6 +5,7 @@ import datetime from pathlib import Path + import matplotlib.pyplot as plt import pandas as pd import seaborn as sns From 802939a3674506f5faedf200c9714bbe6b1b15c8 Mon Sep 17 00:00:00 2001 From: tdm32 Date: Wed, 26 Jun 2024 21:15:43 +0100 Subject: [PATCH 114/131] change np.timedelta in enhanced_lifestyle.py back to original --- src/tlo/methods/enhanced_lifestyle.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tlo/methods/enhanced_lifestyle.py b/src/tlo/methods/enhanced_lifestyle.py index ffe1e5bac7..008424ec2b 100644 --- a/src/tlo/methods/enhanced_lifestyle.py +++ b/src/tlo/methods/enhanced_lifestyle.py @@ -660,7 +660,7 @@ def update_all_properties(self, df): :param df: The population dataframe """ # get months since last poll now = self.module.sim.date - months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "m")) + months_since_last_poll = round((now - self.date_last_run) / np.timedelta64(1, "M")) # loop through linear models dictionary and initialise each property in the population dataframe for _property_name, _model in self._models.items(): if _model['update'] is not None: From 7ff54e61346d4dc02d8659ae71db922d2ee3c720 Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:28:12 +0100 Subject: [PATCH 115/131] remove json file --- .../analysis_scenarios_draws.json | 245 ------------------ 1 file changed, 245 deletions(-) delete mode 100644 src/scripts/malaria/impact_analysis/analysis_scenarios_draws.json diff --git a/src/scripts/malaria/impact_analysis/analysis_scenarios_draws.json b/src/scripts/malaria/impact_analysis/analysis_scenarios_draws.json deleted file mode 100644 index 146169bf82..0000000000 --- a/src/scripts/malaria/impact_analysis/analysis_scenarios_draws.json +++ /dev/null @@ -1,245 +0,0 @@ -{ - "scenario_script_path": "src/scripts/malaria/impact_analysis/analysis_scenarios.py", - "scenario_seed": 0, - "runs_per_draw": 5, - "draws": [ - { - "draw_number": 0, - "parameters": { - "HealthSystem": { - "Service_Availability": [ - "*" - ], - "use_funded_or_actual_staffing": "funded", - "mode_appt_constraints": 1, - "policy_name": "Naive" - }, - "Hiv": { - "scenario": 0 - } - } - }, - { - "draw_number": 1, - "parameters": { - "HealthSystem": { - "Service_Availability": [ - "*" - ], - "use_funded_or_actual_staffing": "funded", - "mode_appt_constraints": 1, - "policy_name": "Naive" - }, - "Hiv": { - "scenario": 1 - } - } - }, - { - "draw_number": 2, - "parameters": { - "HealthSystem": { - "Service_Availability": [ - "*" - ], - "use_funded_or_actual_staffing": "funded", - "mode_appt_constraints": 1, - "policy_name": "Naive" - }, - "Hiv": { - "scenario": 2 - } - } - }, - { - "draw_number": 3, - "parameters": { - "HealthSystem": { - "Service_Availability": [ - "*" - ], - "use_funded_or_actual_staffing": "funded", - "mode_appt_constraints": 1, - "policy_name": "Naive" - }, - "Hiv": { - "scenario": 3 - } - } - }, - { - "draw_number": 4, - "parameters": { - "HealthSystem": { - "Service_Availability": [ - "*" - ], - "use_funded_or_actual_staffing": "funded", - "mode_appt_constraints": 1, - "policy_name": "Naive" - }, - "Hiv": { - "scenario": 5 - } - } - }, - { - "draw_number": 5, - "parameters": { - "HealthSystem": { - "Service_Availability": [ - "Alri_*", - "AntenatalCare_*", - "BladderCancer_*", - "BreastCancer_*", - "CardioMetabolicDisorders_*", - "Contraception_*", - "Copd_*", - "DeliveryCare_*", - "Depression_*", - "Diarrhoea_*", - "Epi_*", - "Epilepsy_*", - "FirstAttendance_*", - "Malaria_*", - "Measles_*", - "OesophagealCancer_*", - "OtherAdultCancer_*", - "PostnatalCare_*", - "ProstateCancer_*", - "Rti_*", - "Schisto_*", - "Tb_*", - "Undernutrition_*", - "Hiv_PalliativeCare" - ], - "use_funded_or_actual_staffing": "funded", - "mode_appt_constraints": 1, - "policy_name": "Naive" - }, - "Hiv": { - "scenario": 0 - } - } - }, - { - "draw_number": 6, - "parameters": { - "HealthSystem": { - "Service_Availability": [ - "Alri_*", - "AntenatalCare_*", - "BladderCancer_*", - "BreastCancer_*", - "CardioMetabolicDisorders_*", - "Contraception_*", - "Copd_*", - "DeliveryCare_*", - "Depression_*", - "Diarrhoea_*", - "Epi_*", - "Epilepsy_*", - "FirstAttendance_*", - "Hiv_*", - "Malaria_*", - "Measles_*", - "OesophagealCancer_*", - "OtherAdultCancer_*", - "PostnatalCare_*", - "ProstateCancer_*", - "Rti_*", - "Schisto_*", - "Undernutrition_*", - "Tb_PalliativeCare" - ], - "use_funded_or_actual_staffing": "funded", - "mode_appt_constraints": 1, - "policy_name": "Naive" - }, - "Hiv": { - "scenario": 0 - } - } - }, - { - "draw_number": 7, - "parameters": { - "HealthSystem": { - "Service_Availability": [ - "Alri_*", - "AntenatalCare_*", - "BladderCancer_*", - "BreastCancer_*", - "CardioMetabolicDisorders_*", - "Contraception_*", - "Copd_*", - "DeliveryCare_*", - "Depression_*", - "Diarrhoea_*", - "Epi_*", - "Epilepsy_*", - "FirstAttendance_*", - "Hiv_*", - "Measles_*", - "OesophagealCancer_*", - "OtherAdultCancer_*", - "PostnatalCare_*", - "ProstateCancer_*", - "Rti_*", - "Schisto_*", - "Tb_*", - "Undernutrition_*", - "Malaria_Treatment_Complicated" - ], - "use_funded_or_actual_staffing": "funded", - "mode_appt_constraints": 1, - "policy_name": "Naive" - }, - "Hiv": { - "scenario": 3 - } - } - }, - { - "draw_number": 8, - "parameters": { - "HealthSystem": { - "Service_Availability": [ - "Alri_*", - "AntenatalCare_*", - "BladderCancer_*", - "BreastCancer_*", - "CardioMetabolicDisorders_*", - "Contraception_*", - "Copd_*", - "DeliveryCare_*", - "Depression_*", - "Diarrhoea_*", - "Epi_*", - "Epilepsy_*", - "FirstAttendance_*", - "Measles_*", - "OesophagealCancer_*", - "OtherAdultCancer_*", - "PostnatalCare_*", - "ProstateCancer_*", - "Rti_*", - "Schisto_*", - "Undernutrition_*", - "Hiv_PalliativeCare", - "Tb_PalliativeCare", - "Malaria_Treatment_Complicated" - ], - "use_funded_or_actual_staffing": "funded", - "mode_appt_constraints": 1, - "policy_name": "Naive" - }, - "Hiv": { - "scenario": 3 - } - } - } - ], - "commit": "3ddf42452012af61b613ef76082fff028e6992d6", - "github": "https://github.com/UCL/TLOmodel/tree/3ddf42452012af61b613ef76082fff028e6992d6" -} \ No newline at end of file From 8e19f4fb70ff59a52ae6c818b49e99806cf087fe Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:30:02 +0100 Subject: [PATCH 116/131] call it 'htm_scenario_analysis' rather than just 'scenario_analysis' --- .../analysis_htm_scaleup.py | 8 ++++---- .../scenario_plots.py | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename src/scripts/{scenario_analyses => htm_scenario_analyses}/analysis_htm_scaleup.py (90%) rename src/scripts/{scenario_analyses => htm_scenario_analyses}/scenario_plots.py (100%) diff --git a/src/scripts/scenario_analyses/analysis_htm_scaleup.py b/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py similarity index 90% rename from src/scripts/scenario_analyses/analysis_htm_scaleup.py rename to src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py index 0ffd6a88c8..f1b83192c5 100644 --- a/src/scripts/scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py @@ -13,16 +13,16 @@ For all scenarios, keep all default health system settings check the batch configuration gets generated without error: -tlo scenario-run --draw-only src/scripts/scenario_analyses/analysis_htm_scaleup.py +tlo scenario-run --draw-only src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py Run on the batch system using: -tlo batch-submit src/scripts/scenario_analyses/analysis_htm_scaleup.py +tlo batch-submit src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py or locally using: -tlo scenario-run src/scripts/scenario_analyses/analysis_htm_scaleup.py +tlo scenario-run src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py or execute a single run: -tlo scenario-run src/scripts/scenario_analyses/analysis_htm_scaleup.py --draw 1 0 +tlo scenario-run src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py --draw 1 0 """ diff --git a/src/scripts/scenario_analyses/scenario_plots.py b/src/scripts/htm_scenario_analyses/scenario_plots.py similarity index 100% rename from src/scripts/scenario_analyses/scenario_plots.py rename to src/scripts/htm_scenario_analyses/scenario_plots.py From 7ef49145ccc1ad31351dec763c51adb038f55e3a Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:31:11 +0100 Subject: [PATCH 117/131] update comment --- src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py b/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py index f1b83192c5..1fb14da6cf 100644 --- a/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py @@ -7,6 +7,7 @@ *1 scale-up HIV program *2 scale-up TB program *3 scale-up malaria program +*4 scale-up HIV and Tb and malaria programs scale-up occurs on the default scale-up start date (01/01/2025: in parameters list of resourcefiles) From d08e3463cbf772766813ca5f5223858ed7319381 Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:34:31 +0100 Subject: [PATCH 118/131] roll back change in test_tb.py --- tests/test_tb.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_tb.py b/tests/test_tb.py index 8ff363708d..0434c70069 100644 --- a/tests/test_tb.py +++ b/tests/test_tb.py @@ -421,7 +421,6 @@ def get_appt_footprints(_consumables_availability): # 1) If consumables available, the HSI will only be run once and the appt footprint should be TBNew: assert [{'TBNew': 1}] == get_appt_footprints(_consumables_availability='all') - # 2) If consumables not available, there should be multiple footprints where the first is TBNew # and the rest is PharmDispensing appt_list = get_appt_footprints(_consumables_availability='none') From 08e2421f80d54ce75089e404b1ca7c1ef852f6cc Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:34:45 +0100 Subject: [PATCH 119/131] remove .py extension for clarity --- src/scripts/htm_scenario_analyses/scenario_plots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/htm_scenario_analyses/scenario_plots.py b/src/scripts/htm_scenario_analyses/scenario_plots.py index de0ece9393..d14454ae13 100644 --- a/src/scripts/htm_scenario_analyses/scenario_plots.py +++ b/src/scripts/htm_scenario_analyses/scenario_plots.py @@ -26,7 +26,7 @@ # 0) Find results_folder associated with a given batch_file (and get most recent [-1]) -results_folder = get_scenario_outputs("scaleup_tests.py", outputspath)[-1] +results_folder = get_scenario_outputs("scaleup_tests", outputspath)[-1] # Declare path for output graphs from this script make_graph_file_name = lambda stub: results_folder / f"{stub}.png" # noqa: E731 From 52949f814cb2da68faaf3b0f9efddafa0860f699 Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:37:24 +0100 Subject: [PATCH 120/131] roll back incidental change --- src/tlo/methods/tb.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 05e8da5ee8..59c497c497 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -48,7 +48,13 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): # initialise empty dict with set keys self.tb_outputs = {k: [] for k in keys} - INIT_DEPENDENCIES = {"Demography", "HealthSystem", "Lifestyle", "SymptomManager", "Epi"} + INIT_DEPENDENCIES = { + "Demography", + "HealthSystem", + "Lifestyle", + "SymptomManager", + "Epi", + } OPTIONAL_INIT_DEPENDENCIES = {"HealthBurden", "Hiv"} From 3e91f8844650570fa3f440be97fd3db7666cd50d Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:37:40 +0100 Subject: [PATCH 121/131] linting and editing string for clarity --- src/tlo/methods/hiv.py | 8 +++++--- src/tlo/methods/malaria.py | 9 ++++++--- src/tlo/methods/tb.py | 9 ++++++--- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 6bb719cc8f..e8ca2ea116 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -400,13 +400,15 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): # ------------------ scale-up parameters for scenario analysis ------------------ # "do_scaleup": Parameter( Types.BOOL, - "argument to determine whether scale-up of program will be implemented"), + "argument to determine whether scale-up of program will be implemented" + ), "scaleup_start": Parameter( Types.INT, - "number of years after state date at which program scale-up will occur"), + "number of years after state date at which program scale-up will occur" + ), "scaleup_parameters": Parameter( Types.DATA_FRAME, - "list of parameters and values changed in scenario analysis" + "the parameters and values changed in scenario analysis" ), } diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 5a8795e81e..7e9d7b0836 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -192,13 +192,16 @@ def __init__(self, name=None, resourcefilepath=None): # ------------------ scale-up parameters for scenario analysis ------------------ # "do_scaleup": Parameter( Types.BOOL, - "argument to determine whether scale-up of program will be implemented"), + "argument to determine whether scale-up of program will be implemented" + ), "scaleup_start": Parameter( Types.INT, - "number of years after state date at which program scale-up will occur"), + "number of years after state date at which program scale-up will occur" + ), "scaleup_parameters": Parameter( Types.DATA_FRAME, - "list of parameters and values changed in scenario analysis") + "the parameters and values changed in scenario analysis" + ) } PROPERTIES = { diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 59c497c497..582eb62607 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -379,13 +379,16 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): # ------------------ scale-up parameters for scenario analysis ------------------ # "do_scaleup": Parameter( Types.BOOL, - "argument to determine whether scale-up of program will be implemented"), + "argument to determine whether scale-up of program will be implemented" + ), "scaleup_start": Parameter( Types.INT, - "number of years after state date at which program scale-up will occur"), + "number of years after state date at which program scale-up will occur" + ), "scaleup_parameters": Parameter( Types.DATA_FRAME, - "list of parameters and values changed in scenario analysis") + "the parameters and values changed in scenario analysis" + ) } def read_parameters(self, data_folder): From ea0eebe8f542d079811b9b0dc5bb82e5fd833638 Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:42:05 +0100 Subject: [PATCH 122/131] roll back incidental changes --- src/tlo/methods/hiv.py | 4 ++-- src/tlo/methods/malaria.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index e8ca2ea116..3a8945a700 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -1232,7 +1232,7 @@ def on_birth(self, mother_id, child_id): # usually performed by care_of_women_during_pregnancy module if not mother.hv_diagnosed and \ mother.is_alive and ( - self.rng.random_sample() < p["prob_hiv_test_at_anc_or_delivery"]): + self.rng.random_sample() < p["prob_hiv_test_at_anc_or_delivery"]): self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_TestAndRefer( person_id=abs(mother_id), # Pass mother's id, whether from true or direct birth @@ -1257,7 +1257,7 @@ def on_birth(self, mother_id, child_id): ) if "newborn_outcomes" not in self.sim.modules and ( - self.rng.random_sample() < p['prob_hiv_test_for_newborn_infant']): + self.rng.random_sample() < p['prob_hiv_test_for_newborn_infant']): self.sim.modules["HealthSystem"].schedule_hsi_event( hsi_event=HSI_Hiv_TestAndRefer( person_id=child_id, diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 7e9d7b0836..6c3ab1ea27 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -265,7 +265,7 @@ def read_parameters(self, data_folder): # check itn projected values are <=0.7 and rounded to 1dp for matching to incidence tables p['itn'] = round(p['itn'], 1) - assert p['itn'] <= 0.7 + assert (p['itn'] <= 0.7) # =============================================================================== # single dataframe for itn and irs district/year data; set index for fast lookup From b5ccd4d664c195f706114855b4e09f9d656ac203 Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 13:47:57 +0100 Subject: [PATCH 123/131] defaults for healthsystem ok -- no need to step through each option --- tests/test_HTMscaleup.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tests/test_HTMscaleup.py b/tests/test_HTMscaleup.py index 91ef0cd5f7..363fafc691 100644 --- a/tests/test_HTMscaleup.py +++ b/tests/test_HTMscaleup.py @@ -39,16 +39,7 @@ def get_sim(seed): demography.Demography(resourcefilepath=resourcefilepath), simplified_births.SimplifiedBirths(resourcefilepath=resourcefilepath), enhanced_lifestyle.Lifestyle(resourcefilepath=resourcefilepath), - healthsystem.HealthSystem( - resourcefilepath=resourcefilepath, - service_availability=["*"], # all treatment allowed - mode_appt_constraints=1, # mode of constraints to do with officer numbers and time - cons_availability="default", # mode for consumable constraints (if ignored, all consumables available) - ignore_priority=True, # do not use the priority information in HSI event to schedule - capabilities_coefficient=1.0, # multiplier for the capabilities of health officers - disable=False, # disables the healthsystem (no constraints and no logging) and every HSI runs - disable_and_reject_all=False, # disable healthsystem and no HSI runs - ), + healthsystem.HealthSystem(resourcefilepath=resourcefilepath), symptommanager.SymptomManager(resourcefilepath=resourcefilepath), healthseekingbehaviour.HealthSeekingBehaviour(resourcefilepath=resourcefilepath), healthburden.HealthBurden(resourcefilepath=resourcefilepath), From 9ae96152f9e9800325a7368bd3d7f652a3026edf Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:03:47 +0100 Subject: [PATCH 124/131] remove inadvertent duplication in code --- src/tlo/methods/hiv.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 3a8945a700..133164ba60 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -2467,17 +2467,6 @@ def apply(self, person_id, squeeze_factor): # set cap for number of repeat tests self.counter_for_test_not_available += 1 # The current appointment is included in the count. - if ( - self.counter_for_test_not_available - <= self.module.parameters["hiv_healthseekingbehaviour_cap"] - ): - # repeat appt for HIV test - self.sim.modules["HealthSystem"].schedule_hsi_event( - self, - topen=self.sim.date + pd.DateOffset(days=7), - tclose=None, - priority=0, - ) if ( self.counter_for_test_not_available From 419d2b46a677befcde5021042897124d8ce8a1a4 Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:03:56 +0100 Subject: [PATCH 125/131] remove comment --- tests/test_HTMscaleup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_HTMscaleup.py b/tests/test_HTMscaleup.py index 363fafc691..f00fbb409c 100644 --- a/tests/test_HTMscaleup.py +++ b/tests/test_HTMscaleup.py @@ -56,7 +56,6 @@ def check_initial_params(sim): original_params = pd.read_excel(resourcefilepath / 'ResourceFile_HIV.xlsx', sheet_name='parameters') - # todo do we need to be exhaustive and check every parameter here? # check initial parameters assert sim.modules["Hiv"].parameters["beta"] == \ original_params.loc[original_params.parameter_name == "beta", "value"].values[0] @@ -84,7 +83,7 @@ def test_hiv_scale_up(seed): # check initial parameters check_initial_params(sim) - # update parameters + # update parameters to instruct there to be a scale-up sim.modules["Hiv"].parameters["do_scaleup"] = True sim.modules["Hiv"].parameters["scaleup_start"] = years_until_scale_up From 66eecb6e6e407ec97ced27f81cdff0b9b7891416 Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:34:28 +0100 Subject: [PATCH 126/131] use dict for ease of accessing --- src/tlo/methods/hiv.py | 42 ++++++++++++-------------------------- src/tlo/methods/malaria.py | 23 ++++++++------------- src/tlo/methods/tb.py | 31 ++++++++++------------------ 3 files changed, 32 insertions(+), 64 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 133164ba60..e14b53ce6f 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -407,7 +407,7 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "number of years after state date at which program scale-up will occur" ), "scaleup_parameters": Parameter( - Types.DATA_FRAME, + Types.DICT, "the parameters and values changed in scenario analysis" ), } @@ -448,7 +448,7 @@ def read_parameters(self, data_folder): p["treatment_cascade"] = workbook["spectrum_treatment_cascade"] # load parameters for scale-up projections - p["scaleup_parameters"] = workbook["scaleup_parameters"] + p["scaleup_parameters"] = workbook["scaleup_parameters"].set_index('parameter')['scaleup_value'].to_dict() # DALY weights # get the DALY weight that this module will use from the weight database (these codes are just random!) @@ -1112,50 +1112,34 @@ def update_parameters(self): # scale-up HIV program # reduce risk of HIV - applies to whole adult population - p["beta"] = p["beta"] * scaled_params.loc[( - scaled_params.parameter == "reduction_in_hiv_beta"), "scaleup_value"].values[0] + p["beta"] = p["beta"] * scaled_params["reduction_in_hiv_beta"] # increase PrEP coverage for FSW after HIV test - p["prob_prep_for_fsw_after_hiv_test"] = scaled_params.loc[ - scaled_params.parameter == "prob_prep_for_fsw_after_hiv_test", "scaleup_value"].values[0] + p["prob_prep_for_fsw_after_hiv_test"] = scaled_params["prob_prep_for_fsw_after_hiv_test"] # prep poll for AGYW - target to the highest risk # increase retention to 75% for FSW and AGYW - p["prob_prep_for_agyw"] = scaled_params.loc[ - scaled_params.parameter == "prob_prep_for_agyw", "scaleup_value"].values[0] - p["probability_of_being_retained_on_prep_every_3_months"] = scaled_params.loc[ - scaled_params.parameter == "probability_of_being_retained_on_prep_every_3_months", "scaleup_value"].values[ - 0] + p["prob_prep_for_agyw"] = scaled_params["prob_prep_for_agyw"] + p["probability_of_being_retained_on_prep_every_3_months"] = scaled_params["probability_of_being_retained_on_prep_every_3_months"] # increase probability of VMMC after hiv test - p["prob_circ_after_hiv_test"] = scaled_params.loc[ - scaled_params.parameter == "prob_circ_after_hiv_test", "scaleup_value"].values[0] + p["prob_circ_after_hiv_test"] = scaled_params["prob_circ_after_hiv_test"] # increase testing/diagnosis rates, default 2020 0.03/0.25 -> 93% dx - p["hiv_testing_rates"]["annual_testing_rate_children"] = \ - scaled_params.loc[ - scaled_params.parameter == "annual_testing_rate_children", "scaleup_value"].values[0] - p["hiv_testing_rates"]["annual_testing_rate_adults"] = \ - scaled_params.loc[ - scaled_params.parameter == "annual_testing_rate_adults", "scaleup_value"].values[0] + p["hiv_testing_rates"]["annual_testing_rate_children"] = scaled_params["annual_testing_rate_children"] + p["hiv_testing_rates"]["annual_testing_rate_adults"] = scaled_params["annual_testing_rate_adults"] # ANC testing - value for mothers and infants testing - p["prob_hiv_test_at_anc_or_delivery"] = scaled_params.loc[ - scaled_params.parameter == "prob_hiv_test_at_anc_or_delivery", "scaleup_value"].values[0] - p["prob_hiv_test_for_newborn_infant"] = scaled_params.loc[ - scaled_params.parameter == "prob_hiv_test_for_newborn_infant", "scaleup_value"].values[0] + p["prob_hiv_test_at_anc_or_delivery"] = scaled_params["prob_hiv_test_at_anc_or_delivery"] + p["prob_hiv_test_for_newborn_infant"] = scaled_params["prob_hiv_test_for_newborn_infant"] # prob ART start if dx, this is already 95% at 2020 - p["prob_start_art_after_hiv_test"] = scaled_params.loc[ - scaled_params.parameter == - "prob_start_art_after_hiv_test", "scaleup_value"].values[0] + p["prob_start_art_after_hiv_test"] = scaled_params["prob_start_art_after_hiv_test"] # viral suppression rates # adults already at 95% by 2020 # change all column values - p["prob_start_art_or_vs"]["virally_suppressed_on_art"] = \ - scaled_params.loc[ - scaled_params.parameter == "virally_suppressed_on_art", "scaleup_value"].values[0] + p["prob_start_art_or_vs"]["virally_suppressed_on_art"] = scaled_params["virally_suppressed_on_art"] def on_birth(self, mother_id, child_id): """ diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 6c3ab1ea27..d32301176e 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -199,7 +199,7 @@ def __init__(self, name=None, resourcefilepath=None): "number of years after state date at which program scale-up will occur" ), "scaleup_parameters": Parameter( - Types.DATA_FRAME, + Types.DICT, "the parameters and values changed in scenario analysis" ) } @@ -261,7 +261,7 @@ def read_parameters(self, data_folder): p['sev_inc'] = pd.read_csv(self.resourcefilepath / 'malaria' / 'ResourceFile_malaria_SevInc_expanded.csv') # load parameters for scale-up projections - p["scaleup_parameters"] = workbook["scaleup_parameters"] + p["scaleup_parameters"] = workbook["scaleup_parameters"].set_index('parameter')['scaleup_value'].to_dict() # check itn projected values are <=0.7 and rounded to 1dp for matching to incidence tables p['itn'] = round(p['itn'], 1) @@ -670,14 +670,11 @@ def update_parameters(self): # scale-up malaria program # increase testing # prob_malaria_case_tests=0.4 default - p["prob_malaria_case_tests"] = scaled_params.loc[ - scaled_params.parameter == "prob_malaria_case_tests", "scaleup_value"].values[0] + p["prob_malaria_case_tests"] = scaled_params["prob_malaria_case_tests"] # gen pop testing rates # annual Rate_rdt_testing=0.64 at 2023 - p["rdt_testing_rates"]["Rate_rdt_testing"] = \ - scaled_params.loc[ - scaled_params.parameter == "rdt_testing_rates", "scaleup_value"].values[0] + p["rdt_testing_rates"]["Rate_rdt_testing"] = scaled_params["rdt_testing_rates"] # treatment reaches XX # no default between testing and treatment, governed by tx availability @@ -686,8 +683,7 @@ def update_parameters(self): # given during ANC visits and MalariaIPTp Event which selects ALL eligible women # treatment success reaches 1 - default is currently 1 also - p["prob_of_treatment_success"] = scaled_params.loc[ - scaled_params.parameter == "prob_of_treatment_success", "scaleup_value"].values[0] + p["prob_of_treatment_success"] = scaled_params["prob_of_treatment_success"] # bednet and ITN coverage # set IRS for 4 high-risk districts @@ -701,19 +697,16 @@ def update_parameters(self): highrisk_distr_num) # IRS values can be 0 or 0.8 - no other value in lookup table - self.itn_irs['irs_rate'].loc[mask] = scaled_params.loc[ - scaled_params.parameter == "irs_district", "scaleup_value"].values[0] + self.itn_irs['irs_rate'].loc[mask] = scaled_params["irs_district"] # set ITN for all districts # Set these values to 0.7 - this is the max value possible in lookup table # equivalent to 0.7 of all pop sleeping under bednet # household coverage could be 100%, but not everyone in household sleeping under bednet - self.itn_irs['itn_rate'] = scaled_params.loc[ - scaled_params.parameter == "itn_district", "scaleup_value"].values[0] + self.itn_irs['itn_rate'] = scaled_params["itn_district"] # itn rates for 2019 onwards - p["itn"] = scaled_params.loc[ - scaled_params.parameter == "itn", "scaleup_value"].values[0] + p["itn"] = scaled_params["itn"] def on_birth(self, mother_id, child_id): df = self.sim.population.props diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 582eb62607..5b5d8bfabd 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -386,7 +386,7 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): "number of years after state date at which program scale-up will occur" ), "scaleup_parameters": Parameter( - Types.DATA_FRAME, + Types.DICT, "the parameters and values changed in scenario analysis" ) } @@ -427,7 +427,7 @@ def read_parameters(self, data_folder): ) # load parameters for scale-up projections - p["scaleup_parameters"] = workbook["scaleup_parameters"] + p["scaleup_parameters"] = workbook["scaleup_parameters"].set_index('parameter')['scaleup_value'].to_dict() # 2) Get the DALY weights if "HealthBurden" in self.sim.modules.keys(): @@ -898,30 +898,21 @@ def update_parameters(self): # scale-up TB program # use NTP treatment rates - p["rate_testing_active_tb"]["treatment_coverage"] = scaled_params.loc[ - scaled_params.parameter == "tb_treatment_coverage", "scaleup_value"].values[0] + p["rate_testing_active_tb"]["treatment_coverage"] = scaled_params["tb_treatment_coverage"] # increase tb treatment success rates - p["prob_tx_success_ds"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_ds", "scaleup_value"].values[0] - p["prob_tx_success_mdr"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_mdr", "scaleup_value"].values[0] - p["prob_tx_success_0_4"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_0_4", "scaleup_value"].values[0] - p["prob_tx_success_5_14"] = scaled_params.loc[ - scaled_params.parameter == "tb_prob_tx_success_5_14", "scaleup_value"].values[0] + p["prob_tx_success_ds"] = scaled_params["tb_prob_tx_success_ds"] + p["prob_tx_success_mdr"] = scaled_params["tb_prob_tx_success_mdr"] + p["prob_tx_success_0_4"] = scaled_params["tb_prob_tx_success_0_4"] + p["prob_tx_success_5_14"] = scaled_params["tb_prob_tx_success_5_14"] # change first-line testing for TB to xpert - p["first_line_test"] = scaled_params.loc[ - scaled_params.parameter == "first_line_test", "scaleup_value"].values[0] - p["second_line_test"] = scaled_params.loc[ - scaled_params.parameter == "second_line_test", "scaleup_value"].values[0] + p["first_line_test"] = scaled_params["first_line_test"] + p["second_line_test"] = scaled_params["second_line_test"] # increase coverage of IPT - p["ipt_coverage"]["coverage_plhiv"] = scaled_params.loc[ - scaled_params.parameter == "ipt_coverage_plhiv", "scaleup_value"].values[0] - p["ipt_coverage"]["coverage_paediatric"] = scaled_params.loc[ - scaled_params.parameter == "ipt_coverage_paediatric", "scaleup_value"].values[0] + p["ipt_coverage"]["coverage_plhiv"] = scaled_params["ipt_coverage_plhiv"] + p["ipt_coverage"]["coverage_paediatric"] = scaled_params["ipt_coverage_paediatric"] def on_birth(self, mother_id, child_id): """Initialise properties for a newborn individual From dc0dd8b049903a0bd2af07ce5d4b674a0e7c150d Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:50:42 +0100 Subject: [PATCH 127/131] parameter to be the YEAR (int) of the change to fit with the convention used in other modules (instead of years since the beginning of the simulation) --- resources/ResourceFile_HIV.xlsx | 4 ++-- resources/ResourceFile_TB.xlsx | 4 ++-- resources/malaria/ResourceFile_malaria.xlsx | 4 ++-- .../htm_scenario_analyses/analysis_htm_scaleup.py | 8 ++++---- src/tlo/methods/hiv.py | 13 ++++--------- src/tlo/methods/malaria.py | 13 ++++--------- src/tlo/methods/tb.py | 13 ++++--------- tests/{test_HTMscaleup.py => test_htm_scaleup.py} | 12 ++++++------ 8 files changed, 28 insertions(+), 43 deletions(-) rename tests/{test_HTMscaleup.py => test_htm_scaleup.py} (96%) diff --git a/resources/ResourceFile_HIV.xlsx b/resources/ResourceFile_HIV.xlsx index c7b8282a48..f76169e701 100644 --- a/resources/ResourceFile_HIV.xlsx +++ b/resources/ResourceFile_HIV.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1cf8bd8bd6e40988dae55c04b3fbd6904199b5fa67684902f6de6e59c51fc353 -size 161617 +oid sha256:913d736db7717519270d61824a8855cbfd4d6e61a73b7ce51e2c3b7915b011ff +size 161597 diff --git a/resources/ResourceFile_TB.xlsx b/resources/ResourceFile_TB.xlsx index 8f2dbdd939..3dfc69cd81 100644 --- a/resources/ResourceFile_TB.xlsx +++ b/resources/ResourceFile_TB.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:28bb046aebc874b26c803be5d04d7ea1132657bcf0711cd37fd0d8da402d299c -size 55649 +oid sha256:120d687122772909c267db41c933664ccc6247c8aef59d49532547c0c3791121 +size 55634 diff --git a/resources/malaria/ResourceFile_malaria.xlsx b/resources/malaria/ResourceFile_malaria.xlsx index 66d808e141..a6487e80ae 100644 --- a/resources/malaria/ResourceFile_malaria.xlsx +++ b/resources/malaria/ResourceFile_malaria.xlsx @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99cf607f72fab7ca0639e8987290ba0dc6e11d69ff8be6dc9cbcb87144caac1c -size 69588 +oid sha256:e8157368754dae9ce692fbd10fecf1e598f37fb258292085c93e1c881dd47aa9 +size 69590 diff --git a/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py b/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py index 1fb14da6cf..ccfc8e99cd 100644 --- a/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py @@ -87,20 +87,20 @@ def modules(self): ] def draw_parameters(self, draw_number, rng): - scaleup_start = 2 + scaleup_start = 2012 return { 'Hiv': { 'do_scaleup': [False, True, False, False, True][draw_number], - 'scaleup_start': scaleup_start + 'scaleup_start_year': scaleup_start_year }, 'Tb': { 'do_scaleup': [False, False, True, False, True][draw_number], - 'scaleup_start': scaleup_start + 'scaleup_start_year': scaleup_start_year }, 'Malaria': { 'do_scaleup': [False, False, False, True, True][draw_number], - 'scaleup_start': scaleup_start + 'scaleup_start_year': scaleup_start_year }, } diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index e14b53ce6f..cd6526d072 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -402,9 +402,9 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): Types.BOOL, "argument to determine whether scale-up of program will be implemented" ), - "scaleup_start": Parameter( + "scaleup_start_year": Parameter( Types.INT, - "number of years after state date at which program scale-up will occur" + "the year when the scale-up starts (it will occur on 1st January of that year)" ), "scaleup_parameters": Parameter( Types.DICT, @@ -912,13 +912,8 @@ def initialise_simulation(self, sim): # Optional: Schedule the scale-up of programs if self.parameters["do_scaleup"]: - scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start"]) - - assert isinstance(scaleup_start_date, Date), "Value is not a Date object" - # Check if scale-up start date is on or after sim start date - assert scaleup_start_date >= Date(2010, 1, 1), \ - f"Date {scaleup_start_date} is before January 1, 2010" - + scaleup_start_date = Date(self.parameters["scaleup_start_year"], 1, 1) + assert scaleup_start_date >= self.sim.start_date, f"Date {scaleup_start_date} is before simulation starts." sim.schedule_event(ScaleUpSetupEvent(self), scaleup_start_date) # 3) Determine who has AIDS and impose the Symptoms 'aids_symptoms' diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index d32301176e..c61b010819 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -194,9 +194,9 @@ def __init__(self, name=None, resourcefilepath=None): Types.BOOL, "argument to determine whether scale-up of program will be implemented" ), - "scaleup_start": Parameter( + "scaleup_start_year": Parameter( Types.INT, - "number of years after state date at which program scale-up will occur" + "the year when the scale-up starts (it will occur on 1st January of that year)" ), "scaleup_parameters": Parameter( Types.DICT, @@ -600,13 +600,8 @@ def initialise_simulation(self, sim): # Optional: Schedule the scale-up of programs if self.parameters["do_scaleup"]: - scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start"]) - - assert isinstance(scaleup_start_date, Date), "Value is not a Date object" - # Check if scale-up start date is on or after sim start date - assert scaleup_start_date >= Date(2010, 1, 1), \ - f"Date {scaleup_start_date} is before January 1, 2010" - + scaleup_start_date = Date(self.parameters["scaleup_start_year"], 1, 1) + assert scaleup_start_date >= self.sim.start_date, f"Date {scaleup_start_date} is before simulation starts." sim.schedule_event(ScaleUpSetupEvent(self), scaleup_start_date) # 2) ----------------------------------- DIAGNOSTIC TESTS ----------------------------------- diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 5b5d8bfabd..10c433761a 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -381,9 +381,9 @@ def __init__(self, name=None, resourcefilepath=None, run_with_checks=False): Types.BOOL, "argument to determine whether scale-up of program will be implemented" ), - "scaleup_start": Parameter( + "scaleup_start_year": Parameter( Types.INT, - "number of years after state date at which program scale-up will occur" + "the year when the scale-up starts (it will occur on 1st January of that year)" ), "scaleup_parameters": Parameter( Types.DICT, @@ -868,13 +868,8 @@ def initialise_simulation(self, sim): # 2) log at the end of the year # Optional: Schedule the scale-up of programs if self.parameters["do_scaleup"]: - scaleup_start_date = sim.date + DateOffset(years=self.parameters["scaleup_start"]) - - assert isinstance(scaleup_start_date, Date), "Value is not a Date object" - # Check if scale-up start date is on or after sim start date - assert scaleup_start_date >= Date(2010, 1, 1), \ - f"Date {scaleup_start_date} is before January 1, 2010" - + scaleup_start_date = Date(self.parameters["scaleup_start_year"], 1, 1) + assert scaleup_start_date >= self.sim.start_date, f"Date {scaleup_start_date} is before simulation starts." sim.schedule_event(ScaleUpSetupEvent(self), scaleup_start_date) # 2) log at the end of the year diff --git a/tests/test_HTMscaleup.py b/tests/test_htm_scaleup.py similarity index 96% rename from tests/test_HTMscaleup.py rename to tests/test_htm_scaleup.py index f00fbb409c..dbb2638c88 100644 --- a/tests/test_HTMscaleup.py +++ b/tests/test_htm_scaleup.py @@ -23,8 +23,8 @@ resourcefilepath = Path(os.path.dirname(__file__)) / "../resources" start_date = Date(2010, 1, 1) -years_until_scale_up = 2 -end_date = start_date + pd.DateOffset(years=years_until_scale_up+1) +scaleup_start_year = 2012 # <-- the scale-up will occur on 1st January of that year +end_date = Date(2013, 1, 1) def get_sim(seed): @@ -85,7 +85,7 @@ def test_hiv_scale_up(seed): # update parameters to instruct there to be a scale-up sim.modules["Hiv"].parameters["do_scaleup"] = True - sim.modules["Hiv"].parameters["scaleup_start"] = years_until_scale_up + sim.modules["Hiv"].parameters["scaleup_start_year"] = scaleup_start_year # Make the population sim.make_initial_population(n=popsize) @@ -155,11 +155,11 @@ def test_htm_scale_up(seed): # update parameters sim.modules["Hiv"].parameters["do_scaleup"] = True - sim.modules["Hiv"].parameters["scaleup_start"] = years_until_scale_up + sim.modules["Hiv"].parameters["scaleup_start_year"] = scaleup_start_year sim.modules["Tb"].parameters["do_scaleup"] = True - sim.modules["Tb"].parameters["scaleup_start"] = years_until_scale_up + sim.modules["Tb"].parameters["scaleup_start_year"] = scaleup_start_year sim.modules["Malaria"].parameters["do_scaleup"] = True - sim.modules["Malaria"].parameters["scaleup_start"] = years_until_scale_up + sim.modules["Malaria"].parameters["scaleup_start_year"] = scaleup_start_year # Make the population sim.make_initial_population(n=popsize) From 08dea6ab4c822f21af3018cc1e47f1afd5d57ea6 Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:50:52 +0100 Subject: [PATCH 128/131] remove comment --- src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py b/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py index ccfc8e99cd..fef598a501 100644 --- a/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py @@ -70,7 +70,6 @@ def log_configuration(self): } def modules(self): - # return fullmodel(resourcefilepath=self.resources) return [ demography.Demography(resourcefilepath=self.resources), From d5918907ba1708df78dd191f0feba955b91e8f28 Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:56:47 +0100 Subject: [PATCH 129/131] refactor module method for clarity --- src/tlo/methods/hiv.py | 5 ++--- src/tlo/methods/malaria.py | 4 ++-- src/tlo/methods/tb.py | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index cd6526d072..6a037c653a 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -1098,7 +1098,7 @@ def initialise_simulation(self, sim): ) ) - def update_parameters(self): + def update_parameters_for_program_scaleup(self): p = self.parameters scaled_params = p["scaleup_parameters"] @@ -2285,8 +2285,7 @@ def __init__(self, module): super().__init__(module) def apply(self, population): - - self.module.update_parameters() + self.module.update_parameters_for_program_scaleup() # --------------------------------------------------------------------------- diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index c61b010819..158e8d83a0 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -655,7 +655,7 @@ def initialise_simulation(self, sim): 'Sulfamethoxazole + trimethropin, tablet 400 mg + 80 mg' ) - def update_parameters(self): + def update_parameters_for_program_scaleup(self): p = self.parameters scaled_params = p["scaleup_parameters"] @@ -906,7 +906,7 @@ def __init__(self, module): def apply(self, population): - self.module.update_parameters() + self.module.update_parameters_for_program_scaleup() class MalariaIPTp(RegularEvent, PopulationScopeEventMixin): diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 10c433761a..474bab9c8d 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -884,7 +884,7 @@ def initialise_simulation(self, sim): TbCheckPropertiesEvent(self), sim.date + pd.DateOffset(months=1) ) - def update_parameters(self): + def update_parameters_for_program_scaleup(self): p = self.parameters scaled_params = p["scaleup_parameters"] @@ -1427,7 +1427,7 @@ def __init__(self, module): def apply(self, population): - self.module.update_parameters() + self.module.update_parameters_for_program_scaleup() class TbActiveEvent(RegularEvent, PopulationScopeEventMixin): From 467066d08b4ed86667821f6a0ba7888c83bebc38 Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:59:09 +0100 Subject: [PATCH 130/131] refactor to prevent same name being used for events specific to different modules --- src/tlo/methods/hiv.py | 4 ++-- src/tlo/methods/malaria.py | 4 ++-- src/tlo/methods/tb.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tlo/methods/hiv.py b/src/tlo/methods/hiv.py index 6a037c653a..1ddafe4c47 100644 --- a/src/tlo/methods/hiv.py +++ b/src/tlo/methods/hiv.py @@ -914,7 +914,7 @@ def initialise_simulation(self, sim): if self.parameters["do_scaleup"]: scaleup_start_date = Date(self.parameters["scaleup_start_year"], 1, 1) assert scaleup_start_date >= self.sim.start_date, f"Date {scaleup_start_date} is before simulation starts." - sim.schedule_event(ScaleUpSetupEvent(self), scaleup_start_date) + sim.schedule_event(HivScaleUpEvent(self), scaleup_start_date) # 3) Determine who has AIDS and impose the Symptoms 'aids_symptoms' @@ -2274,7 +2274,7 @@ def apply(self, person_id): ) -class ScaleUpSetupEvent(Event, PopulationScopeEventMixin): +class HivScaleUpEvent(Event, PopulationScopeEventMixin): """ This event exists to change parameters or functions depending on the scenario for projections which has been set It only occurs once on date: scaleup_start_date, diff --git a/src/tlo/methods/malaria.py b/src/tlo/methods/malaria.py index 158e8d83a0..f322783717 100644 --- a/src/tlo/methods/malaria.py +++ b/src/tlo/methods/malaria.py @@ -602,7 +602,7 @@ def initialise_simulation(self, sim): if self.parameters["do_scaleup"]: scaleup_start_date = Date(self.parameters["scaleup_start_year"], 1, 1) assert scaleup_start_date >= self.sim.start_date, f"Date {scaleup_start_date} is before simulation starts." - sim.schedule_event(ScaleUpSetupEvent(self), scaleup_start_date) + sim.schedule_event(MalariaScaleUpEvent(self), scaleup_start_date) # 2) ----------------------------------- DIAGNOSTIC TESTS ----------------------------------- # Create the diagnostic test representing the use of RDT for malaria diagnosis @@ -894,7 +894,7 @@ def apply(self, population): self.module.general_population_rdt_scheduler(population) -class ScaleUpSetupEvent(Event, PopulationScopeEventMixin): +class MalariaScaleUpEvent(Event, PopulationScopeEventMixin): """ This event exists to change parameters or functions depending on the scenario for projections which has been set It only occurs once on date: scaleup_start_date, diff --git a/src/tlo/methods/tb.py b/src/tlo/methods/tb.py index 474bab9c8d..aa62f3ea8a 100644 --- a/src/tlo/methods/tb.py +++ b/src/tlo/methods/tb.py @@ -870,7 +870,7 @@ def initialise_simulation(self, sim): if self.parameters["do_scaleup"]: scaleup_start_date = Date(self.parameters["scaleup_start_year"], 1, 1) assert scaleup_start_date >= self.sim.start_date, f"Date {scaleup_start_date} is before simulation starts." - sim.schedule_event(ScaleUpSetupEvent(self), scaleup_start_date) + sim.schedule_event(TbScaleUpEvent(self), scaleup_start_date) # 2) log at the end of the year sim.schedule_event(TbLoggingEvent(self), sim.date + DateOffset(years=1)) @@ -1415,7 +1415,7 @@ def apply(self, population): self.module.relapse_event(population) -class ScaleUpSetupEvent(Event, PopulationScopeEventMixin): +class TbScaleUpEvent(Event, PopulationScopeEventMixin): """ This event exists to change parameters or functions depending on the scenario for projections which has been set It only occurs once on date: scaleup_start_date, From 830f83f9afa601031bf7d80eeea5689dbb1de049 Mon Sep 17 00:00:00 2001 From: Tim Hallett <39991060+tbhallett@users.noreply.github.com> Date: Thu, 27 Jun 2024 16:00:09 +0100 Subject: [PATCH 131/131] specify year for scale-up in analysis file --- src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py b/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py index fef598a501..a89231f670 100644 --- a/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py +++ b/src/scripts/htm_scenario_analyses/analysis_htm_scaleup.py @@ -86,7 +86,7 @@ def modules(self): ] def draw_parameters(self, draw_number, rng): - scaleup_start = 2012 + scaleup_start_year = 2012 return { 'Hiv': {