From c3326cd5e44797e5f7b6dd2054e9c2b198ba336f Mon Sep 17 00:00:00 2001 From: mmsuarezcosta Date: Mon, 29 Sep 2025 13:27:52 -0400 Subject: [PATCH 01/10] param revamp schisto --- resources/ResourceFile_Schisto/Parameters.csv | 4 +- src/tlo/methods/schisto.py | 172 +++++++++++++----- 2 files changed, 131 insertions(+), 45 deletions(-) diff --git a/resources/ResourceFile_Schisto/Parameters.csv b/resources/ResourceFile_Schisto/Parameters.csv index 58333c68af..94665dcadf 100644 --- a/resources/ResourceFile_Schisto/Parameters.csv +++ b/resources/ResourceFile_Schisto/Parameters.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a565dd79a08bdc49f3a9c2ee55c20b32ebcad8620d6e2c9ccf3f8f41098a94d1 -size 1747 +oid sha256:8482832364b890f8d46fcc19b36948500dc91041b9165f6f09ded67d359fbecd +size 4621 diff --git a/src/tlo/methods/schisto.py b/src/tlo/methods/schisto.py index b7079e95d3..9593c15776 100644 --- a/src/tlo/methods/schisto.py +++ b/src/tlo/methods/schisto.py @@ -23,15 +23,6 @@ logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) -# Definition of the age-groups used in the module, as a tuple of two integers (a,b) such that the given age group -# is in range a <= group <= b. i.e., -# 2 <= PSAC <= 4 -# 5 <= SAC <= 14 -# 15 <= Adults -# 0 <= All -_AGE_GROUPS = {'Infant': (0, 1), 'PSAC': (2, 4), 'SAC': (5, 14), 'Adults': (15, 120), 'All': (0, 120)} - - class Schisto(Module, GenericFirstAppointmentsMixin): """Schistosomiasis module. Two species of worm that cause Schistosomiasis are modelled independently. Worms are acquired by persons via the @@ -81,16 +72,34 @@ class Schisto(Module, GenericFirstAppointmentsMixin): 'infection with improved WASH'), 'calibration_scenario': Parameter(Types.REAL, 'Scenario used to reset parameters to run calibration sims'), + 'urine_filtration_sensitivity_noneWB': Parameter(Types.REAL, + 'Sensitivity of urine filtration test for non-infected cases'), 'urine_filtration_sensitivity_lowWB': Parameter(Types.REAL, 'Sensitivity of UF in detecting low WB'), 'urine_filtration_sensitivity_moderateWB': Parameter(Types.REAL, 'Sensitivity of UF in detecting moderate WB'), 'urine_filtration_sensitivity_heavyWB': Parameter(Types.REAL, 'Sensitivity of UF in detecting heavy WB'), + 'urine_filtration_specificity_noneWB': Parameter(Types.REAL, + 'Specificity of urine filtration test'), + 'urine_filtration_specificity_lowWB': Parameter(Types.REAL, + 'Specificity of UF in detecting low WB'), + 'urine_filtration_specificity_moderateWB': Parameter(Types.REAL, + 'Specificity of UF in detecting moderate WB'), + 'urine_filtration_specificity_heavyWB': Parameter(Types.REAL, + 'Specificity of UF in detecting heavy WB'), + 'kato_katz_sensitivity_lowWB': Parameter(Types.REAL, + 'Sensitivity of Kato-Katz test for non-infected cases'), 'kato_katz_sensitivity_moderateWB': Parameter(Types.REAL, 'Sensitivity of KK in detecting moderate WB'), 'kato_katz_sensitivity_heavyWB': Parameter(Types.REAL, - 'Sensitivity of KK in detecting heavy WB'), + 'Sensitivity of KK in detecting heavy WB'), + 'kato_katz_specificity_lowWB': Parameter(Types.REAL, + 'Sensitivity of Kato-Katz test for non-infected cases'), + 'kato_katz_specificity_moderateWB': Parameter(Types.REAL, + 'Specificity of KK in detecting moderate WB'), + 'kato_katz_specificity_heavyWB': Parameter(Types.REAL, + 'Specificity of KK in detecting moderate WB'), 'scaleup_WASH': Parameter(Types.STRING, 'Whether to scale-up WASH during simulation, pause fixes values at 2024 ' 'levels with no further improvement, continue allows historical trends to continue, ' @@ -130,6 +139,32 @@ class Schisto(Module, GenericFirstAppointmentsMixin): 'MDA_coverage_historical': Parameter(Types.DATA_FRAME, 'Probability of getting PZQ in the MDA for PSAC, SAC and Adults ' 'in historic rounds'), + 'odds_ratio_health_seeking_children_schisto_low': Parameter( + Types.REAL, 'Odds ratio for health seeking in children with schisto low symptoms'), + 'odds_ratio_health_seeking_adults_schisto_low': Parameter( + Types.REAL, 'Odds ratio for health seeking in adults with schisto low symptoms'), + 'mda_coverage_upper_limit': Parameter(Types.REAL, + 'Upper limit for MDA coverage'), + 'single_district_calibration_number': Parameter(Types.INT, + 'District number for single district calibration runs'), + 'mda_schedule_month': Parameter(Types.INT, + 'Month for scheduling MDA events'), + 'mda_schedule_day': Parameter(Types.INT, + 'Day for scheduling MDA events'), + 'minimum_baseline_prevalence': Parameter(Types.REAL, + 'Minimum baseline prevalence to prevent division by zero'), + 'prevalence_lower_bound': Parameter(Types.REAL, + 'Lower bound for prevalence clamping'), + 'prevalence_upper_bound': Parameter(Types.REAL, + 'Upper bound for prevalence clamping'), + 'infant_min_age': Parameter(Types.INT, 'Minimum age for Infant group'), + 'infant_max_age': Parameter(Types.INT, 'Maximum age for Infant group'), + 'psac_min_age': Parameter(Types.INT, 'Minimum age for PSAC group'), + 'psac_max_age': Parameter(Types.INT, 'Maximum age for PSAC group'), + 'sac_min_age': Parameter(Types.INT, 'Minimum age for SAC group'), + 'sac_max_age': Parameter(Types.INT, 'Maximum age for SAC group'), + 'adults_min_age': Parameter(Types.INT, 'Minimum age for Adults group'), + 'adults_max_age': Parameter(Types.INT, 'Maximum age for Adults group'), } def __init__(self, name=None, mda_execute=True, single_district=False): @@ -159,12 +194,8 @@ def __init__(self, name=None, mda_execute=True, single_district=False): # create future mda strategy self.prognosed_mda = None - # Age-group mapper - s = pd.Series(index=range(1 + 120), data='object') - for name, (low_limit, heavy_limit) in _AGE_GROUPS.items(): - if name != 'All': - s.loc[(s.index >= low_limit) & (s.index <= heavy_limit)] = name - self.age_group_mapper = s.to_dict() + # Age-group mapper will be created in read_parameters after parameters are loaded + self.age_group_mapper = None def read_parameters(self, resourcefilepath: Optional[Path] = None): """Read parameters and register symptoms.""" @@ -176,6 +207,14 @@ def read_parameters(self, resourcefilepath: Optional[Path] = None): workbook = read_csv_files(Path(resourcefilepath) / 'ResourceFile_Schisto', files=None) self.parameters = self._load_parameters_from_workbook(workbook) + # Create age-group mapper now that parameters are loaded + s = pd.Series(index=range(1 + int(self.parameters['adults_max_age'])), data='object') + age_groups = self._get_age_groups() + for name, (low_limit, heavy_limit) in age_groups.items(): + if name != 'All': + s.loc[(s.index >= low_limit) & (s.index <= heavy_limit)] = name + self.age_group_mapper = s.to_dict() + # check WASH scaleup specified correctly assert self.parameters['scaleup_WASH'] in ['pause', 'continue', 'scaleup'] @@ -212,9 +251,10 @@ def initialise_population(self, population): df.loc[df.is_alive, f'{self.module_prefix}_MDA_treatment_counter'] = 0 # reset all to one district if doing calibration or test runs - # choose Zomba (district 19) as it has ~10% prev of both species + # choose district based on parameter (default Zomba district 19) as it has ~10% prev of both species if self.single_district: - df['district_num_of_residence'] = pd.Categorical([19] * len(df), + district_num = int(self.parameters['single_district_calibration_number']) + df['district_num_of_residence'] = pd.Categorical([district_num] * len(df), categories=df['district_num_of_residence'].cat.categories) df['district_of_residence'] = pd.Categorical(['Zomba'] * len(df), @@ -356,16 +396,15 @@ def _load_parameters_from_workbook(self, workbook) -> dict: parameters = dict() # HSI and treatment params: - param_list = workbook['Parameters'].set_index("Parameter")['Value'] + param_list = workbook['Parameters'].set_index("parameter_name")['value'] def try_cast_to_float(val): try: - # Don't convert strings that contain alphabetic characters - if isinstance(val, str) and any(c.isalpha() for c in val): - return val + # Try to convert to float first return float(val) except (ValueError, TypeError): - return val # Fall back to original value + # If conversion fails, return the original value + return val # parameters are all converted to strings if any strings are present for _param_name in ( @@ -376,8 +415,13 @@ def try_cast_to_float(val): 'urine_filtration_sensitivity_lowWB', 'urine_filtration_sensitivity_moderateWB', 'urine_filtration_sensitivity_heavyWB', + 'urine_filtration_specificity_lowWB', + 'urine_filtration_specificity_moderateWB', + 'urine_filtration_specificity_heavyWB', 'kato_katz_sensitivity_moderateWB', + 'kato_katz_specificity_moderateWB', 'kato_katz_sensitivity_heavyWB', + 'kato_katz_specificity_heavyWB', 'scaleup_WASH', # Needs to be included 'scaleup_WASH_start_year', 'mda_coverage', @@ -392,6 +436,27 @@ def try_cast_to_float(val): 'daly_weight_heavy_s_mansoni', 'daly_weight_moderate_s_haematobium', 'daly_weight_heavy_s_haematobium', + 'odds_ratio_health_seeking_children_schisto_low', + 'odds_ratio_health_seeking_adults_schisto_low', + 'kato_katz_sensitivity_lowWB', + 'kato_katz_specificity_lowWB', + 'urine_filtration_sensitivity_noneWB', + 'urine_filtration_specificity_noneWB', + 'mda_coverage_upper_limit', + 'single_district_calibration_number', + 'mda_schedule_month', + 'mda_schedule_day', + 'minimum_baseline_prevalence', + 'prevalence_lower_bound', + 'prevalence_upper_bound', + 'infant_min_age', + 'infant_max_age', + 'psac_min_age', + 'psac_max_age', + 'sac_min_age', + 'sac_max_age', + 'adults_min_age', + 'adults_max_age' ): value = param_list[_param_name] parameters[_param_name] = try_cast_to_float(value) @@ -403,11 +468,23 @@ def try_cast_to_float(val): historical_mda.columns = historical_mda.columns.str.replace('EpiCov_', '') parameters['MDA_coverage_historical'] = historical_mda.astype(float) - # clip upper limit of MDA coverage at 99% - parameters['MDA_coverage_historical'] = parameters['MDA_coverage_historical'].clip(upper=0.99) + # clip upper limit of MDA coverage at the specified limit + parameters['MDA_coverage_historical'] = parameters['MDA_coverage_historical'].clip( + upper=parameters['mda_coverage_upper_limit']) return parameters + def _get_age_groups(self) -> dict: + """Create age groups dictionary from parameters.""" + p = self.parameters + return { + 'Infant': (int(p['infant_min_age']), int(p['infant_max_age'])), + 'PSAC': (int(p['psac_min_age']), int(p['psac_max_age'])), + 'SAC': (int(p['sac_min_age']), int(p['sac_max_age'])), + 'Adults': (int(p['adults_min_age']), int(p['adults_max_age'])), + 'All': (int(p['infant_min_age']), int(p['adults_max_age'])) + } + def _create_mda_strategy(self) -> pd.DataFrame: """ this uses the parameters set in the module to create a pd.DataFrame that contains the MDA strategy for future MDA activities. This will take effect the year after the last entry in @@ -465,10 +542,11 @@ def _register_symptoms(self) -> None: unless otherwise specified.""" # Declare symptoms that this module will cause and which are not included in the generic symptoms: + p = self.parameters self.sim.modules['SymptomManager'].register_symptom( Symptom(name='schisto_low', - odds_ratio_health_seeking_in_children=0.01, - odds_ratio_health_seeking_in_adults=0.01) # no health-seeking + odds_ratio_health_seeking_in_children=p['odds_ratio_health_seeking_children_schisto_low'], + odds_ratio_health_seeking_in_adults=p['odds_ratio_health_seeking_adults_schisto_low']) ) self.sim.modules['SymptomManager'].register_symptom( @@ -545,8 +623,8 @@ def _get_consumables_for_dx(self): KK_schisto_test_lowWB=DxTest( property='ss_sm_infection_status', target_categories=["Non-infected", "Low-infection"], - sensitivity=0.0, - specificity=0.0, + sensitivity=p['kato_katz_sensitivity_lowWB'], + specificity=p['kato_katz_specificity_lowWB'], item_codes={self.item_codes_for_consumables_required['microscope_slide']: 2}, optional_item_codes=[ self.item_codes_for_consumables_required['malachite_stain'], @@ -560,7 +638,7 @@ def _get_consumables_for_dx(self): property='ss_sm_infection_status', target_categories=["Moderate-infection"], sensitivity=p["kato_katz_sensitivity_moderateWB"], - specificity=1.0, + specificity=p['kato_katz_specificity_moderateWB'], item_codes={self.item_codes_for_consumables_required['microscope_slide']: 2}, optional_item_codes=[ self.item_codes_for_consumables_required['malachite_stain'], @@ -573,7 +651,7 @@ def _get_consumables_for_dx(self): property='ss_sm_infection_status', target_categories=["Heavy-infection"], sensitivity=p["kato_katz_sensitivity_heavyWB"], - specificity=1.0, + specificity=p['kato_katz_specificity_heavyWB'], item_codes={self.item_codes_for_consumables_required['microscope_slide']: 2}, optional_item_codes=[ self.item_codes_for_consumables_required['malachite_stain'], @@ -586,8 +664,8 @@ def _get_consumables_for_dx(self): UF_schisto_test_noWB=DxTest( property='ss_sh_infection_status', target_categories=["Non-infected"], - sensitivity=0.0, - specificity=0.0, + sensitivity=p['urine_filtration_sensitivity_noneWB'], + specificity=p['urine_filtration_specificity_noneWB'], item_codes={self.item_codes_for_consumables_required['microscope_slide']: 2}, optional_item_codes=[ self.item_codes_for_consumables_required['filter_paper'], @@ -600,7 +678,7 @@ def _get_consumables_for_dx(self): property='ss_sh_infection_status', target_categories=["Low-infection"], sensitivity=p["urine_filtration_sensitivity_lowWB"], - specificity=1.0, + specificity=p['urine_filtration_specificity_lowWB'], item_codes={self.item_codes_for_consumables_required['microscope_slide']: 2}, optional_item_codes=[ self.item_codes_for_consumables_required['filter_paper'], @@ -614,7 +692,7 @@ def _get_consumables_for_dx(self): property='ss_sh_infection_status', target_categories=["Moderate-infection"], sensitivity=p["urine_filtration_sensitivity_moderateWB"], - specificity=1.0, + specificity=p['urine_filtration_specificity_moderateWB'], item_codes={self.item_codes_for_consumables_required['microscope_slide']: 2}, optional_item_codes=[ self.item_codes_for_consumables_required['filter_paper'], @@ -627,7 +705,7 @@ def _get_consumables_for_dx(self): property='ss_sh_infection_status', target_categories=["Heavy-infection"], sensitivity=p["urine_filtration_sensitivity_heavyWB"], - specificity=1.0, + specificity=p['urine_filtration_specificity_heavyWB'], item_codes={self.item_codes_for_consumables_required['microscope_slide']: 2}, optional_item_codes=[ self.item_codes_for_consumables_required['filter_paper'], @@ -646,7 +724,9 @@ def _schedule_mda_events(self) -> None: district=district, coverage=cov.to_dict(), months_between_repeats=None), - Date(year=year, month=7, day=1) + Date(year=year, + month=int(self.parameters['mda_schedule_month']), + day=int(self.parameters['mda_schedule_day'])) ) # Schedule the first occurrence of a future MDA in each district. It will occur after the last historical MDA. @@ -661,7 +741,9 @@ def _schedule_mda_events(self) -> None: district=district, coverage=cov.to_dict(), months_between_repeats=frequency_in_months if frequency_in_months > 0 else None), - Date(year=year_first_simulated_mda, month=7, day=1) + Date(year=year_first_simulated_mda, + month=int(self.parameters['mda_schedule_month']), + day=int(self.parameters['mda_schedule_day'])) ) def get_infection_status(self, age, aggregate_worm_burden, species_prefix): @@ -901,7 +983,7 @@ def load_parameters_from_workbook(self, workbook) -> dict: parameters = dict() # Natural history params - param_list = workbook['Parameters'].set_index("Parameter")['Value'] + param_list = workbook['Parameters'].set_index("parameter_name")['value'] for _param_name in ('R0', 'beta_PSAC', 'beta_SAC', @@ -1078,8 +1160,9 @@ def _assign_initial_worm_burden(self, population) -> None: # Determine a 'contact rate' for each person contact_and_susceptibility = df.loc[in_the_district, prop('susceptibility')] + age_groups = self.schisto_module._get_age_groups() for age_group in ['PSAC', 'SAC', 'Adults']: - age_range = _AGE_GROUPS[age_group] + age_range = age_groups[age_group] in_the_district_and_age_group = \ df.index[(df['district_of_residence'] == district) & (df['age_years'].between(age_range[0], age_range[1]))] @@ -1231,8 +1314,10 @@ def apply(self, population): prevalence_now = (df.loc[where, prop('aggregate_worm_burden')] > 0).mean() # prevalence (Bool True/# entries) # Clamp to safe bounds - baseline_prevalence = max(params['baseline_prevalence'] , 1e-6) # fixed reference prevalence - prevalence_now = min(max(prevalence_now, 0.0), 1.0) + baseline_prevalence = max(params['baseline_prevalence'], + global_params['minimum_baseline_prevalence']) # fixed reference prevalence + prevalence_now = min(max(prevalence_now, global_params['prevalence_lower_bound']), + global_params['prevalence_upper_bound']) # Scale in [0,1]: 1 at baseline; → 0 as national prevalence → 0 scale = min(1.0, (prevalence_now / baseline_prevalence) ** global_params['background_gamma']) @@ -1424,7 +1509,8 @@ def _select_recipients(self, district, age_group, coverage) -> list: df = self.sim.population.props rng = self.module.rng - age_range = _AGE_GROUPS[age_group] # returns a tuple (a,b) a <= age_group <= b + age_groups = self.module._get_age_groups() + age_range = age_groups[age_group] # returns a tuple (a,b) a <= age_group <= b eligible = df.index[ df['is_alive'] From b6ccece010f3636af8ca3c2eeeed0f0c34e2f679 Mon Sep 17 00:00:00 2001 From: mmsuarezcosta Date: Tue, 7 Oct 2025 16:09:21 -0400 Subject: [PATCH 02/10] Parameterize scenario parameters mda_execute and single_district --- resources/ResourceFile_Schisto/Parameters.csv | 4 ++-- src/tlo/methods/schisto.py | 17 ++++++++++------- tests/test_schisto.py | 4 +++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/resources/ResourceFile_Schisto/Parameters.csv b/resources/ResourceFile_Schisto/Parameters.csv index 94665dcadf..86ea802dae 100644 --- a/resources/ResourceFile_Schisto/Parameters.csv +++ b/resources/ResourceFile_Schisto/Parameters.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8482832364b890f8d46fcc19b36948500dc91041b9165f6f09ded67d359fbecd -size 4621 +oid sha256:7efeb948758dc1e6ffe032a8483744de0957bb179256b64c79525b11fd4f178f +size 4712 diff --git a/src/tlo/methods/schisto.py b/src/tlo/methods/schisto.py index 9593c15776..2cd7f9b935 100644 --- a/src/tlo/methods/schisto.py +++ b/src/tlo/methods/schisto.py @@ -165,12 +165,12 @@ class Schisto(Module, GenericFirstAppointmentsMixin): 'sac_max_age': Parameter(Types.INT, 'Maximum age for SAC group'), 'adults_min_age': Parameter(Types.INT, 'Minimum age for Adults group'), 'adults_max_age': Parameter(Types.INT, 'Maximum age for Adults group'), + 'mda_execute': Parameter(Types.BOOL, 'Whether to execute MDA events'), + 'single_district': Parameter(Types.BOOL, 'Whether to run simulation for a single district only'), } - def __init__(self, name=None, mda_execute=True, single_district=False): + def __init__(self, name=None): super().__init__(name) - self.mda_execute = mda_execute - self.single_district = single_district # Create pointer that will be to dict of disability weights self.disability_weights = None @@ -248,11 +248,12 @@ def initialise_population(self, population): """Set the property values for the initial population.""" df = population.props + p = self.parameters df.loc[df.is_alive, f'{self.module_prefix}_MDA_treatment_counter'] = 0 # reset all to one district if doing calibration or test runs # choose district based on parameter (default Zomba district 19) as it has ~10% prev of both species - if self.single_district: + if p['single_district']: district_num = int(self.parameters['single_district_calibration_number']) df['district_num_of_residence'] = pd.Categorical([district_num] * len(df), categories=df['district_num_of_residence'].cat.categories) @@ -266,7 +267,7 @@ def initialise_population(self, population): def initialise_simulation(self, sim): """Get ready for simulation start.""" - + p = self.parameters # Look-up DALY weights if 'HealthBurden' in self.sim.modules: self.disability_weights = self._get_disability_weight() @@ -298,7 +299,7 @@ def initialise_simulation(self, sim): # {286: 1.0}) # Schedule MDA events - if self.mda_execute: + if p['mda_execute']: # update future mda strategy from default values self.prognosed_mda = self._create_mda_strategy() @@ -456,7 +457,9 @@ def try_cast_to_float(val): 'sac_min_age', 'sac_max_age', 'adults_min_age', - 'adults_max_age' + 'adults_max_age', + 'mda_execute', + 'single_district' ): value = param_list[_param_name] parameters[_param_name] = try_cast_to_float(value) diff --git a/tests/test_schisto.py b/tests/test_schisto.py index 02bbba707f..c133f85cb0 100644 --- a/tests/test_schisto.py +++ b/tests/test_schisto.py @@ -33,8 +33,10 @@ def get_simulation(seed, start_date, mda_execute=True): healthburden.HealthBurden(), healthsystem.HealthSystem(cons_availability='all'), simplified_births.SimplifiedBirths(), - schisto.Schisto(mda_execute=mda_execute), + schisto.Schisto(), ) + # Override the mda_execute parameter + sim.modules['Schisto'].parameters['mda_execute'] = mda_execute return sim From e1f1e0ca39776f3da482ea467f697d4e9bb618ac Mon Sep 17 00:00:00 2001 From: mmsuarezcosta Date: Tue, 7 Oct 2025 17:10:06 -0400 Subject: [PATCH 03/10] update schisto files based on parameterization of schisto.py, add district calibration params as params, begin parameterizing dates --- resources/ResourceFile_Schisto/Parameters.csv | 4 ++-- src/scripts/schistosomiasis/baseline_run.py | 4 +--- .../schistosomiasis/schisto_analysis.py | 4 ++-- .../schisto_calibration_check.py | 5 ++++- .../schisto_single_run_local.py | 6 +++-- src/tlo/methods/schisto.py | 22 ++++++++++++++----- 6 files changed, 29 insertions(+), 16 deletions(-) diff --git a/resources/ResourceFile_Schisto/Parameters.csv b/resources/ResourceFile_Schisto/Parameters.csv index 86ea802dae..935591e31e 100644 --- a/resources/ResourceFile_Schisto/Parameters.csv +++ b/resources/ResourceFile_Schisto/Parameters.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7efeb948758dc1e6ffe032a8483744de0957bb179256b64c79525b11fd4f178f -size 4712 +oid sha256:72966be8965d04d52e1f7df2eb980bf7cbe2b314bdba6737b5e11a33ae6a7258 +size 4838 diff --git a/src/scripts/schistosomiasis/baseline_run.py b/src/scripts/schistosomiasis/baseline_run.py index 7d8da416f1..608ceb410c 100644 --- a/src/scripts/schistosomiasis/baseline_run.py +++ b/src/scripts/schistosomiasis/baseline_run.py @@ -89,9 +89,7 @@ def modules(self): bladder_cancer.BladderCancer(resourcefilepath=self.resources), diarrhoea.Diarrhoea(resourcefilepath=self.resources), hiv.Hiv(resourcefilepath=self.resources), - schisto.Schisto(resourcefilepath=self.resources, - mda_execute=self.mda_execute, - single_district=self.single_district), + schisto.Schisto(resourcefilepath=self.resources), stunting.Stunting(resourcefilepath=self.resources), tb.Tb(resourcefilepath=self.resources), wasting.Wasting(resourcefilepath=self.resources), diff --git a/src/scripts/schistosomiasis/schisto_analysis.py b/src/scripts/schistosomiasis/schisto_analysis.py index c7362d29a0..bd6d1946e9 100644 --- a/src/scripts/schistosomiasis/schisto_analysis.py +++ b/src/scripts/schistosomiasis/schisto_analysis.py @@ -16,7 +16,7 @@ from tlo.util import read_csv_files -def run_simulation(popsize=10000, haem=True, mansoni=True, mda_execute=True): +def run_simulation(popsize=10000, haem=True, mansoni=True): outputpath = Path("./outputs") # folder for convenience of storing outputs # The resource files resourcefilepath = Path("./resources") @@ -59,7 +59,7 @@ def run_simulation(popsize=10000, haem=True, mansoni=True, mda_execute=True): return sim, output -sim, output = run_simulation(popsize=10000, haem=True, mansoni=False, mda_execute=False) +sim, output = run_simulation(popsize=10000, haem=True, mansoni=False) # --------------------------------------------------------------------------------------------------------- diff --git a/src/scripts/schistosomiasis/schisto_calibration_check.py b/src/scripts/schistosomiasis/schisto_calibration_check.py index 3c46bdad76..a3573aff71 100644 --- a/src/scripts/schistosomiasis/schisto_calibration_check.py +++ b/src/scripts/schistosomiasis/schisto_calibration_check.py @@ -65,9 +65,12 @@ def run_simulation(popsize=popsize, mda_execute=True): healthburden.HealthBurden(), healthsystem.HealthSystem(), simplified_births.SimplifiedBirths(), - schisto.Schisto(mda_execute=mda_execute), + schisto.Schisto(), ) + # Override schisto parameters + sim.modules["Schisto"].parameters["mda_execute"] = mda_execute + # initialise the population sim.make_initial_population(n=popsize) diff --git a/src/scripts/schistosomiasis/schisto_single_run_local.py b/src/scripts/schistosomiasis/schisto_single_run_local.py index 6e84efb3e1..05cf2a93f6 100644 --- a/src/scripts/schistosomiasis/schisto_single_run_local.py +++ b/src/scripts/schistosomiasis/schisto_single_run_local.py @@ -72,10 +72,12 @@ def run_simulation(popsize, healthsystem.HealthSystem(disable_and_reject_all=hs_disable_and_reject_all, cons_availability='all'), simplified_births.SimplifiedBirths(), - schisto.Schisto(mda_execute=mda_execute, - single_district=single_district), + schisto.Schisto(), ) + # Override schisto parameters + sim.modules["Schisto"].parameters["mda_execute"] = mda_execute + sim.modules["Schisto"].parameters["single_district"] = single_district # sim.modules["Schisto"].parameters["calibration_scenario"] = 0 # sim.modules["Schisto"].parameters["scaleup_WASH"] = 0.0 # 1.0=True # sim.modules["Schisto"].parameters["scaleup_WASH_start_year"] = 2011 diff --git a/src/tlo/methods/schisto.py b/src/tlo/methods/schisto.py index 2cd7f9b935..c569276e9e 100644 --- a/src/tlo/methods/schisto.py +++ b/src/tlo/methods/schisto.py @@ -147,6 +147,10 @@ class Schisto(Module, GenericFirstAppointmentsMixin): 'Upper limit for MDA coverage'), 'single_district_calibration_number': Parameter(Types.INT, 'District number for single district calibration runs'), + 'single_district_calibration_name': Parameter(Types.STRING, + 'District name for single district calibration runs'), + 'single_district_calibration_region': Parameter(Types.STRING, + 'District region for single district calibration runs'), 'mda_schedule_month': Parameter(Types.INT, 'Month for scheduling MDA events'), 'mda_schedule_day': Parameter(Types.INT, @@ -167,6 +171,8 @@ class Schisto(Module, GenericFirstAppointmentsMixin): 'adults_max_age': Parameter(Types.INT, 'Maximum age for Adults group'), 'mda_execute': Parameter(Types.BOOL, 'Whether to execute MDA events'), 'single_district': Parameter(Types.BOOL, 'Whether to run simulation for a single district only'), + 'main_polling_frequency': Parameter(Types.INT, 'Polling freq main schisto event in months'), + } def __init__(self, name=None): @@ -252,15 +258,15 @@ def initialise_population(self, population): df.loc[df.is_alive, f'{self.module_prefix}_MDA_treatment_counter'] = 0 # reset all to one district if doing calibration or test runs - # choose district based on parameter (default Zomba district 19) as it has ~10% prev of both species + # choose district based on parameter (in Malawi, default Zomba district 19) as it has ~10% prev of both species if p['single_district']: - district_num = int(self.parameters['single_district_calibration_number']) + district_num = int(p['single_district_calibration_number']) df['district_num_of_residence'] = pd.Categorical([district_num] * len(df), categories=df['district_num_of_residence'].cat.categories) - df['district_of_residence'] = pd.Categorical(['Zomba'] * len(df), + df['district_of_residence'] = pd.Categorical([p['single_district_calibration_name']] * len(df), categories=df['district_of_residence'].cat.categories) - df['region_of_residence'] = pd.Categorical(['Southern'] * len(df), + df['region_of_residence'] = pd.Categorical([p['single_district_calibration_region']] * len(df), categories=df['region_of_residence'].cat.categories) for _spec in self.species.values(): _spec.initialise_population(population) @@ -445,6 +451,8 @@ def try_cast_to_float(val): 'urine_filtration_specificity_noneWB', 'mda_coverage_upper_limit', 'single_district_calibration_number', + 'single_district_calibration_name', + 'single_district_calibration_region', 'mda_schedule_month', 'mda_schedule_day', 'minimum_baseline_prevalence', @@ -459,7 +467,8 @@ def try_cast_to_float(val): 'adults_min_age', 'adults_max_age', 'mda_execute', - 'single_district' + 'single_district', + 'main_polling_frequency' ): value = param_list[_param_name] parameters[_param_name] = try_cast_to_float(value) @@ -1265,7 +1274,8 @@ class SchistoInfectionWormBurdenEvent(RegularEvent, PopulationScopeEventMixin): """ def __init__(self, module: Module, species: SchistoSpecies): - super().__init__(module, frequency=DateOffset(months=1)) + p = self.module.parameters + super().__init__(module, frequency=DateOffset(months=p['main_polling_frequency'])) self.species = species def apply(self, population): From 74f9647e5c7217e0b0ebcf380aabfea0ad55844f Mon Sep 17 00:00:00 2001 From: mmsuarezcosta Date: Tue, 7 Oct 2025 18:03:11 -0400 Subject: [PATCH 04/10] remove param mda_coverage_upper_limit, add date interval parameters, add param for age group weights, change age ranges to be undetermined + structural --- resources/ResourceFile_Schisto/Parameters.csv | 4 +- src/tlo/methods/schisto.py | 94 +++++++++++++------ 2 files changed, 66 insertions(+), 32 deletions(-) diff --git a/resources/ResourceFile_Schisto/Parameters.csv b/resources/ResourceFile_Schisto/Parameters.csv index 935591e31e..cec615329f 100644 --- a/resources/ResourceFile_Schisto/Parameters.csv +++ b/resources/ResourceFile_Schisto/Parameters.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72966be8965d04d52e1f7df2eb980bf7cbe2b314bdba6737b5e11a33ae6a7258 -size 4838 +oid sha256:744ac7c0a5b581eb03d6ca0afc68fbd5b6219f53276512fbd6a97923580a17d7 +size 5416 diff --git a/src/tlo/methods/schisto.py b/src/tlo/methods/schisto.py index c569276e9e..a5d44a7b73 100644 --- a/src/tlo/methods/schisto.py +++ b/src/tlo/methods/schisto.py @@ -172,7 +172,22 @@ class Schisto(Module, GenericFirstAppointmentsMixin): 'mda_execute': Parameter(Types.BOOL, 'Whether to execute MDA events'), 'single_district': Parameter(Types.BOOL, 'Whether to run simulation for a single district only'), 'main_polling_frequency': Parameter(Types.INT, 'Polling freq main schisto event in months'), - + 'worm_maturation_period_months': Parameter(Types.INT, + 'Time in months for juvenile worms to mature into adult worms'), + 'worm_death_check_frequency_years': Parameter(Types.INT, + 'Frequency in years to check for worm deaths'), + 'mda_appointment_window_months': Parameter(Types.INT, + 'Time window in months for MDA appointment scheduling'), + 'wash_scaleup_frequency_years': Parameter(Types.INT, + 'Frequency in years to check and apply WASH scale-up'), + 'recent_sanitation_window_years': Parameter(Types.INT, + 'Time window in years to consider sanitation acquisition as recent'), + 'avg_weight_psac_kg': Parameter(Types.REAL, + 'Average weight in kg for PSAC age group for drug dosing'), + 'avg_weight_sac_kg': Parameter(Types.REAL, + 'Average weight in kg for SAC age group for drug dosing'), + 'avg_weight_adult_kg': Parameter(Types.REAL, + 'Average weight in kg for adults for drug dosing'), } def __init__(self, name=None): @@ -286,8 +301,10 @@ def initialise_simulation(self, sim): self._get_consumables_for_dx() # schedule regular events - sim.schedule_event(SchistoMatureJuvenileWormsEvent(self), sim.date + pd.DateOffset(months=1)) - sim.schedule_event(SchistoWormDeathEvent(self), sim.date + pd.DateOffset(years=1)) + sim.schedule_event(SchistoMatureJuvenileWormsEvent(self), + sim.date + pd.DateOffset(months=p['worm_maturation_period_months'])) + sim.schedule_event(SchistoWormDeathEvent(self), + sim.date + pd.DateOffset(years=p['worm_death_check_frequency_years'])) # Initialise the simulation for each species for _spec in self.species.values(): @@ -312,7 +329,8 @@ def initialise_simulation(self, sim): self._schedule_mda_events() # schedule WASH scale-up - sim.schedule_event(SchistoWashScaleUp(self), sim.date + pd.DateOffset(years=1)) + sim.schedule_event(SchistoWashScaleUp(self), + sim.date + pd.DateOffset(years=p['wash_scaleup_frequency_years'])) def on_birth(self, mother_id, child_id): """Initialise our properties for a newborn individual. @@ -449,7 +467,6 @@ def try_cast_to_float(val): 'kato_katz_specificity_lowWB', 'urine_filtration_sensitivity_noneWB', 'urine_filtration_specificity_noneWB', - 'mda_coverage_upper_limit', 'single_district_calibration_number', 'single_district_calibration_name', 'single_district_calibration_region', @@ -468,7 +485,15 @@ def try_cast_to_float(val): 'adults_max_age', 'mda_execute', 'single_district', - 'main_polling_frequency' + 'main_polling_frequency', + 'worm_maturation_period_months', + 'worm_death_check_frequency_years', + 'mda_appointment_window_months', + 'wash_scaleup_frequency_years', + 'recent_sanitation_window_years', + 'avg_weight_psac_kg', + 'avg_weight_sac_kg', + 'avg_weight_adult_kg' ): value = param_list[_param_name] parameters[_param_name] = try_cast_to_float(value) @@ -482,7 +507,7 @@ def try_cast_to_float(val): parameters['MDA_coverage_historical'] = historical_mda.astype(float) # clip upper limit of MDA coverage at the specified limit parameters['MDA_coverage_historical'] = parameters['MDA_coverage_historical'].clip( - upper=parameters['mda_coverage_upper_limit']) + upper=0.99) return parameters @@ -599,17 +624,16 @@ def _get_item_code_for_praziquantel(self, MDA=False) -> int: def calculate_praziquantel_dosage(self, person_id): age = self.sim.population.props.at[person_id, "age_years"] + # Dosing based on age group # 40mg per kg, as single dose (MSTG) # in children <4 years, 20mg/kg - # assume child 0-5, maximum weight 17.5kg (WHO- average between girls/boys) - if age < 5: - dose = 20 * 17.5 - # child aged 5-15, 40mg/kg, use mid-point age 10: 50th percentile weight=30kg - elif age >= 5 and age < 15: - dose = 40 * 30 - # adult, 40mg/kg, average weight 62kg - else: - dose = 40 * 62 + p = self.parameters + if age <= p['psac_max_age']: + dose = 20 * p['avg_weight_psac_kg'] + elif age >= p['sac_min_age'] and age <= p['sac_max_age']: + dose = 40 * p['avg_weight_sac_kg'] + else: # adults + dose = 40 * p['avg_weight_adult_kg'] return int(dose) @@ -766,9 +790,11 @@ def get_infection_status(self, age, aggregate_worm_burden, species_prefix): else: params = self.sim.modules['Schisto'].species['haematobium'].params + p = self.parameters + status = pd.Series("Non-infected", index=age.index, dtype="object") - heavy_group = ((age < 5) & (aggregate_worm_burden >= params["heavy_intensity_threshold_PSAC"])) | ( + heavy_group = ((age <= p["psac_max_age"]) & (aggregate_worm_burden >= params["heavy_intensity_threshold_PSAC"])) | ( aggregate_worm_burden >= params["heavy_intensity_threshold"]) moderate_group = ~heavy_group & (aggregate_worm_burden >= params["low_intensity_threshold"]) low_group = (aggregate_worm_burden < params["low_intensity_threshold"]) & (aggregate_worm_burden > 0) @@ -897,7 +923,8 @@ def reduce_susceptibility(self, df, species_column): # Find the number of individuals with currently susceptible to species and no sanitation # susceptible_no_sanitation = df.query(f"{species_column} == 1 and li_unimproved_sanitation").index - recent_sanitation = df['li_date_acquire_improved_sanitation'] >= (self.sim.date - pd.DateOffset(years=1)) + recent_sanitation = df['li_date_acquire_improved_sanitation'] >= ( + self.sim.date - pd.DateOffset(years=p['recent_sanitation_window_years'])) # Restrict to those who are susceptible, had no sanitation before, and recently acquired sanitation condition = (df[species_column] == 1) & df['li_unimproved_sanitation'] & recent_sanitation @@ -1057,7 +1084,7 @@ def initialise_simulation(self, sim): SchistoInfectionWormBurdenEvent( module=self.schisto_module, species=self), - sim.date + DateOffset(months=1) + sim.date + DateOffset(months=p['main_polling_frequency']) ) def on_birth(self, mother_id, child_id): @@ -1274,13 +1301,14 @@ class SchistoInfectionWormBurdenEvent(RegularEvent, PopulationScopeEventMixin): """ def __init__(self, module: Module, species: SchistoSpecies): - p = self.module.parameters + p = module.parameters super().__init__(module, frequency=DateOffset(months=p['main_polling_frequency'])) self.species = species def apply(self, population): df = population.props params = self.species.params + p = self.module.parameters global_params = self.module.parameters rng = self.module.rng # prop calls the property starting with the prefix species property, i.e. ss_sm or ss_sh @@ -1295,7 +1323,9 @@ def apply(self, population): R0 = params['R0'] where = df.is_alive - age_group = pd.cut(df.loc[where, 'age_years'], [0, 4, 14, 120], labels=['PSAC', 'SAC', 'Adults'], + age_group = pd.cut(df.loc[where, 'age_years'], + [0, p['psac_max_age'], p['sac_max_age'], p['adults_max_age']], + labels=['PSAC', 'SAC', 'Adults'], include_lowest=True) age_group = age_group.astype('category') # Convert to a categorical type for memory efficiency age_group.name = 'age_group' @@ -1381,8 +1411,9 @@ class SchistoMatureJuvenileWormsEvent(RegularEvent, PopulationScopeEventMixin): * Matures the juvenile worms into adult worms """ def __init__(self, module): + p = module.parameters super().__init__( - module, frequency=DateOffset(months=1) + module, frequency=DateOffset(months=p['worm_maturation_period_months']) ) def apply(self, population): @@ -1398,7 +1429,8 @@ def juvenile_worms_to_adults(df, species_column_juvenile, species_column_aggrega this is called separately for each species """ # all new juvenile infections will have same infection date - if (df[juvenile_infection_date] <= self.sim.date - pd.DateOffset(months=1)).any(): + p = self.module.parameters + if (df[juvenile_infection_date] <= self.sim.date - pd.DateOffset(months=p['worm_maturation_period_months'])).any(): df[species_column_aggregate] += df[species_column_juvenile] # Set 'juvenile' column to zeros @@ -1425,8 +1457,9 @@ class SchistoWormDeathEvent(RegularEvent, PopulationScopeEventMixin): """ def __init__(self, module): + p = module.parameters super().__init__( - module, frequency=DateOffset(years=1) + module, frequency=DateOffset(years=p['worm_death_check_frequency_years']) ) def apply(self, population): @@ -1503,7 +1536,7 @@ def apply(self, population): age_group_included=age_included, ), topen=self.sim.date, - tclose=self.sim.date + pd.DateOffset(months=1), + tclose=self.sim.date + pd.DateOffset(months=self.module.parameters['mda_appointment_window_months']), priority=2 # A long time-window of operation and a low priority is used for this MDA Appointment, to represent # that the MDA would not take a priority over other appointments. @@ -1550,8 +1583,9 @@ class SchistoWashScaleUp(RegularEvent, PopulationScopeEventMixin): """ def __init__(self, module): + p = module.parameters super().__init__( - module, frequency=DateOffset(years=1) + module, frequency=DateOffset(years=p['wash_scaleup_frequency_years']) ) def apply(self, population): @@ -1750,11 +1784,11 @@ def apply(self, person_id, squeeze_factor): # using total_dosage = sum(self.module.calculate_praziquantel_dosage(pid) for pid in beneficiaries_still_alive) # is very slow if 'Adults' in self.age_group_included: - # adult, 40mg/kg, average weight 62kg - total_dosage = 40 * 62 * len(beneficiaries_still_alive) + # adult dosing + total_dosage = 40 * p['avg_weight_adult_kg'] * len(beneficiaries_still_alive) else: - # child aged 5-15, 40mg/kg, use mid-point age 10: 50th percentile weight=30kg - total_dosage = 40 * 30 * len(beneficiaries_still_alive) + # child (SAC/PSAC) dosing + total_dosage = 40 * p['avg_weight_sac_kg'] * len(beneficiaries_still_alive) # Let the key consumable be "optional" in order that provision of the treatment is NOT conditional on the drugs # being available.This is because we expect that special planning would be undertaken in order to ensure the From de99c53c9e48559abaf5f2e0d2bc711b4e530f6b Mon Sep 17 00:00:00 2001 From: mmsuarezcosta Date: Tue, 7 Oct 2025 18:03:14 -0400 Subject: [PATCH 05/10] remove param mda_coverage_upper_limit, add date interval parameters, add param for age group weights, change age ranges to be undetermined + structural --- src/tlo/methods/schisto.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tlo/methods/schisto.py b/src/tlo/methods/schisto.py index a5d44a7b73..c01ab34492 100644 --- a/src/tlo/methods/schisto.py +++ b/src/tlo/methods/schisto.py @@ -143,8 +143,6 @@ class Schisto(Module, GenericFirstAppointmentsMixin): Types.REAL, 'Odds ratio for health seeking in children with schisto low symptoms'), 'odds_ratio_health_seeking_adults_schisto_low': Parameter( Types.REAL, 'Odds ratio for health seeking in adults with schisto low symptoms'), - 'mda_coverage_upper_limit': Parameter(Types.REAL, - 'Upper limit for MDA coverage'), 'single_district_calibration_number': Parameter(Types.INT, 'District number for single district calibration runs'), 'single_district_calibration_name': Parameter(Types.STRING, @@ -1080,6 +1078,7 @@ def initialise_simulation(self, sim): * Schedule the WormBurdenEvent for this species. (A recurring instance of this event will be scheduled for each species independently.)""" + p = self.schisto_module.parameters sim.schedule_event( SchistoInfectionWormBurdenEvent( module=self.schisto_module, @@ -1783,6 +1782,7 @@ def apply(self, person_id, squeeze_factor): # if MDA includes SAC/PSAC, return average dose for SAC for all beneficiaries # using total_dosage = sum(self.module.calculate_praziquantel_dosage(pid) for pid in beneficiaries_still_alive) # is very slow + p = self.module.parameters if 'Adults' in self.age_group_included: # adult dosing total_dosage = 40 * p['avg_weight_adult_kg'] * len(beneficiaries_still_alive) From eeb3d9615e56a06a14d663e7175a5e19c83e781d Mon Sep 17 00:00:00 2001 From: mmsuarezcosta Date: Tue, 7 Oct 2025 18:04:58 -0400 Subject: [PATCH 06/10] clean param file --- resources/ResourceFile_Schisto/Parameters.csv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/ResourceFile_Schisto/Parameters.csv b/resources/ResourceFile_Schisto/Parameters.csv index cec615329f..f0a63e7ec5 100644 --- a/resources/ResourceFile_Schisto/Parameters.csv +++ b/resources/ResourceFile_Schisto/Parameters.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:744ac7c0a5b581eb03d6ca0afc68fbd5b6219f53276512fbd6a97923580a17d7 -size 5416 +oid sha256:253eee94eeeb39d2a74ec3e6c37ddb5868b21b999c967dacee2bf7d0a56fe639 +size 5356 From 1fdfbee9429b964716a25b880f178f2d782fb089 Mon Sep 17 00:00:00 2001 From: mmsuarezcosta Date: Tue, 7 Oct 2025 18:09:08 -0400 Subject: [PATCH 07/10] linting fixes --- src/tlo/methods/schisto.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tlo/methods/schisto.py b/src/tlo/methods/schisto.py index c01ab34492..e104a976f0 100644 --- a/src/tlo/methods/schisto.py +++ b/src/tlo/methods/schisto.py @@ -792,8 +792,8 @@ def get_infection_status(self, age, aggregate_worm_burden, species_prefix): status = pd.Series("Non-infected", index=age.index, dtype="object") - heavy_group = ((age <= p["psac_max_age"]) & (aggregate_worm_burden >= params["heavy_intensity_threshold_PSAC"])) | ( - aggregate_worm_burden >= params["heavy_intensity_threshold"]) + heavy_group = ((age <= p["psac_max_age"]) & (aggregate_worm_burden >= + params["heavy_intensity_threshold_PSAC"])) | (aggregate_worm_burden >= params["heavy_intensity_threshold"]) moderate_group = ~heavy_group & (aggregate_worm_burden >= params["low_intensity_threshold"]) low_group = (aggregate_worm_burden < params["low_intensity_threshold"]) & (aggregate_worm_burden > 0) @@ -1429,7 +1429,8 @@ def juvenile_worms_to_adults(df, species_column_juvenile, species_column_aggrega """ # all new juvenile infections will have same infection date p = self.module.parameters - if (df[juvenile_infection_date] <= self.sim.date - pd.DateOffset(months=p['worm_maturation_period_months'])).any(): + if (df[juvenile_infection_date] <= self.sim.date - + pd.DateOffset(months=p['worm_maturation_period_months'])).any(): df[species_column_aggregate] += df[species_column_juvenile] # Set 'juvenile' column to zeros From d23ec0ba55664dd080668ec0e4f2ac23ae49b442 Mon Sep 17 00:00:00 2001 From: mmsuarezcosta Date: Wed, 8 Oct 2025 09:13:25 -0400 Subject: [PATCH 08/10] remove mda_execute from schisto_analysis.py --- src/scripts/schistosomiasis/schisto_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scripts/schistosomiasis/schisto_analysis.py b/src/scripts/schistosomiasis/schisto_analysis.py index bd6d1946e9..263da8a1d9 100644 --- a/src/scripts/schistosomiasis/schisto_analysis.py +++ b/src/scripts/schistosomiasis/schisto_analysis.py @@ -41,7 +41,7 @@ def run_simulation(popsize=10000, haem=True, mansoni=True): sim.register(healthsystem.HealthSystem()) sim.register(healthburden.HealthBurden()) sim.register(contraception.Contraception()) - sim.register(schisto.Schisto(mda_execute=mda_execute)) + sim.register(schisto.Schisto()) if haem: sim.register(schisto.Schisto_Haematobium(symptoms_and_HSI=False)) if mansoni: From ec2f8da7f2d73eeb2fa36713aaa03dc0a5e46681 Mon Sep 17 00:00:00 2001 From: mmsuarezcosta Date: Wed, 8 Oct 2025 13:58:25 -0400 Subject: [PATCH 09/10] delete unused files and add param_label 'calibration data' for all data used to ensure calibration OK --- resources/ResourceFile_Schisto/DALYs.csv | 3 --- .../ResourceFile_Schisto/District_Params_haematobium.csv | 4 ++-- resources/ResourceFile_Schisto/District_Params_mansoni.csv | 4 ++-- resources/ResourceFile_Schisto/ESPEN.csv | 3 --- resources/ResourceFile_Schisto/ESPEN_MDA.csv | 4 ++-- resources/ResourceFile_Schisto/ESPEN_codebook.csv | 3 --- resources/ResourceFile_Schisto/InitialData_haematobium.csv | 3 --- resources/ResourceFile_Schisto/InitialData_mansoni.csv | 3 --- resources/ResourceFile_Schisto/LatestData_haematobium.csv | 4 ++-- resources/ResourceFile_Schisto/LatestData_mansoni.csv | 4 ++-- resources/ResourceFile_Schisto/MDA_historical_Coverage.csv | 3 --- resources/ResourceFile_Schisto/Symptoms.csv | 3 --- 12 files changed, 10 insertions(+), 31 deletions(-) delete mode 100644 resources/ResourceFile_Schisto/DALYs.csv delete mode 100644 resources/ResourceFile_Schisto/ESPEN.csv delete mode 100644 resources/ResourceFile_Schisto/ESPEN_codebook.csv delete mode 100644 resources/ResourceFile_Schisto/InitialData_haematobium.csv delete mode 100644 resources/ResourceFile_Schisto/InitialData_mansoni.csv delete mode 100644 resources/ResourceFile_Schisto/MDA_historical_Coverage.csv delete mode 100644 resources/ResourceFile_Schisto/Symptoms.csv diff --git a/resources/ResourceFile_Schisto/DALYs.csv b/resources/ResourceFile_Schisto/DALYs.csv deleted file mode 100644 index 8dbfe6482e..0000000000 --- a/resources/ResourceFile_Schisto/DALYs.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:29a8a184620e63cbf7587761edd70a18a56aa8d67ab1021cff18b8f9105afb21 -size 2027 diff --git a/resources/ResourceFile_Schisto/District_Params_haematobium.csv b/resources/ResourceFile_Schisto/District_Params_haematobium.csv index bec2962fbb..180993a6c3 100644 --- a/resources/ResourceFile_Schisto/District_Params_haematobium.csv +++ b/resources/ResourceFile_Schisto/District_Params_haematobium.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4d33d4671f46665054389b102b28d6a745f08a7836b3833da013684bcb73917 -size 2383 +oid sha256:dac49e0c4ae4b35cd5e1bafc1935a28b9832f8b670d38cb31c908bde5a954300 +size 2939 diff --git a/resources/ResourceFile_Schisto/District_Params_mansoni.csv b/resources/ResourceFile_Schisto/District_Params_mansoni.csv index bec2962fbb..180993a6c3 100644 --- a/resources/ResourceFile_Schisto/District_Params_mansoni.csv +++ b/resources/ResourceFile_Schisto/District_Params_mansoni.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4d33d4671f46665054389b102b28d6a745f08a7836b3833da013684bcb73917 -size 2383 +oid sha256:dac49e0c4ae4b35cd5e1bafc1935a28b9832f8b670d38cb31c908bde5a954300 +size 2939 diff --git a/resources/ResourceFile_Schisto/ESPEN.csv b/resources/ResourceFile_Schisto/ESPEN.csv deleted file mode 100644 index ffcaee6e7e..0000000000 --- a/resources/ResourceFile_Schisto/ESPEN.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4b06dc289a0fd8fe105d8ff5fc1850a2e4c442539367be4131073647f1fd088d -size 46746 diff --git a/resources/ResourceFile_Schisto/ESPEN_MDA.csv b/resources/ResourceFile_Schisto/ESPEN_MDA.csv index ffcaee6e7e..fe8a582d73 100644 --- a/resources/ResourceFile_Schisto/ESPEN_MDA.csv +++ b/resources/ResourceFile_Schisto/ESPEN_MDA.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4b06dc289a0fd8fe105d8ff5fc1850a2e4c442539367be4131073647f1fd088d -size 46746 +oid sha256:769f51280332063ec4740ea19ca8fd629525673690cb1a6a35386a30df8d07d2 +size 52198 diff --git a/resources/ResourceFile_Schisto/ESPEN_codebook.csv b/resources/ResourceFile_Schisto/ESPEN_codebook.csv deleted file mode 100644 index ab11ccedd3..0000000000 --- a/resources/ResourceFile_Schisto/ESPEN_codebook.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1fa6be9bf0b1831572a484aa16f0bb4026c9b7a2a720c77bf35996467cdf05a8 -size 2054 diff --git a/resources/ResourceFile_Schisto/InitialData_haematobium.csv b/resources/ResourceFile_Schisto/InitialData_haematobium.csv deleted file mode 100644 index c14feeba63..0000000000 --- a/resources/ResourceFile_Schisto/InitialData_haematobium.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8699708701eb31891cb93de816b8d06b67657c023d540d59a9fd07a884c829f8 -size 2967 diff --git a/resources/ResourceFile_Schisto/InitialData_mansoni.csv b/resources/ResourceFile_Schisto/InitialData_mansoni.csv deleted file mode 100644 index 32c1e8fabe..0000000000 --- a/resources/ResourceFile_Schisto/InitialData_mansoni.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d530845117fd9d1783c6ba9b307b5a6c85997015de80f4a8ddc09c4e9a0c8a0c -size 2466 diff --git a/resources/ResourceFile_Schisto/LatestData_haematobium.csv b/resources/ResourceFile_Schisto/LatestData_haematobium.csv index 3928ad1ee7..251a8ce527 100644 --- a/resources/ResourceFile_Schisto/LatestData_haematobium.csv +++ b/resources/ResourceFile_Schisto/LatestData_haematobium.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8dbb1c32331e3ea32a59069106a5764347441f71ed040b49cc52c7865498d28a -size 4818 +oid sha256:c29a8b647800559dd4e9e8aca93b6dd7a55b643fa5215a06fd5e7c51075a4814 +size 5375 diff --git a/resources/ResourceFile_Schisto/LatestData_mansoni.csv b/resources/ResourceFile_Schisto/LatestData_mansoni.csv index bee8929d5b..723e8ba8d5 100644 --- a/resources/ResourceFile_Schisto/LatestData_mansoni.csv +++ b/resources/ResourceFile_Schisto/LatestData_mansoni.csv @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ffc7ede1a3a76a7c0f24f79f1adbfc523d3d49ddd3800c75c77e9142516840a8 -size 4401 +oid sha256:7d0bcfc339f0aa0201c249eb4aa9e62a57cbc2a0ab59534e7c19a67d5037aa50 +size 4957 diff --git a/resources/ResourceFile_Schisto/MDA_historical_Coverage.csv b/resources/ResourceFile_Schisto/MDA_historical_Coverage.csv deleted file mode 100644 index 817c86f952..0000000000 --- a/resources/ResourceFile_Schisto/MDA_historical_Coverage.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8fc1e35dea3ef950c8b2a3ec2f634a407ca24d19810b5726357ed3a57344a59c -size 7183 diff --git a/resources/ResourceFile_Schisto/Symptoms.csv b/resources/ResourceFile_Schisto/Symptoms.csv deleted file mode 100644 index 13bfb3eeb7..0000000000 --- a/resources/ResourceFile_Schisto/Symptoms.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0ecdb2d8740aee748927a414f7680e22c5e808d4b3a81907467cc1944bf35124 -size 943 From 95ca91ff5b3bafaeac85d9ca321678d53e08290c Mon Sep 17 00:00:00 2001 From: mmsuarezcosta Date: Fri, 10 Oct 2025 09:32:14 -0400 Subject: [PATCH 10/10] revert function try_cast_to_float, refactor file name --- resources/ResourceFile_Schisto/Parameters.csv | 3 --- resources/ResourceFile_Schisto/parameter_values.csv | 3 +++ src/tlo/methods/schisto.py | 11 ++++++----- 3 files changed, 9 insertions(+), 8 deletions(-) delete mode 100644 resources/ResourceFile_Schisto/Parameters.csv create mode 100644 resources/ResourceFile_Schisto/parameter_values.csv diff --git a/resources/ResourceFile_Schisto/Parameters.csv b/resources/ResourceFile_Schisto/Parameters.csv deleted file mode 100644 index f0a63e7ec5..0000000000 --- a/resources/ResourceFile_Schisto/Parameters.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:253eee94eeeb39d2a74ec3e6c37ddb5868b21b999c967dacee2bf7d0a56fe639 -size 5356 diff --git a/resources/ResourceFile_Schisto/parameter_values.csv b/resources/ResourceFile_Schisto/parameter_values.csv new file mode 100644 index 0000000000..35dd37f4ae --- /dev/null +++ b/resources/ResourceFile_Schisto/parameter_values.csv @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fc49b667b56b4157056ff0a5df292630a10f4dc0541b768776f77bb4ab7ba742 +size 5356 diff --git a/src/tlo/methods/schisto.py b/src/tlo/methods/schisto.py index e104a976f0..292b886181 100644 --- a/src/tlo/methods/schisto.py +++ b/src/tlo/methods/schisto.py @@ -419,15 +419,16 @@ def _load_parameters_from_workbook(self, workbook) -> dict: parameters = dict() # HSI and treatment params: - param_list = workbook['Parameters'].set_index("parameter_name")['value'] + param_list = workbook['parameter_values'].set_index("parameter_name")['value'] def try_cast_to_float(val): try: - # Try to convert to float first + # Don't convert strings that contain alphabetic characters + if isinstance(val, str) and any(c.isalpha() for c in val): + return val return float(val) except (ValueError, TypeError): - # If conversion fails, return the original value - return val + return val # Fall back to original value # parameters are all converted to strings if any strings are present for _param_name in ( @@ -1020,7 +1021,7 @@ def load_parameters_from_workbook(self, workbook) -> dict: parameters = dict() # Natural history params - param_list = workbook['Parameters'].set_index("parameter_name")['value'] + param_list = workbook['parameter_values'].set_index("parameter_name")['value'] for _param_name in ('R0', 'beta_PSAC', 'beta_SAC',