@@ -135,6 +135,25 @@ class Stunting(Module, GenericFirstAppointmentsMixin):
135135 'prob_stunting_diagnosed_at_generic_appt' : Parameter (
136136 Types .REAL ,
137137 'Probability of a stunted or severely stunted person being checked and correctly diagnosed' ),
138+
139+ # Age and classification thresholds
140+ 'max_age_for_stunting_years' : Parameter (
141+ Types .REAL ,
142+ 'Maximum age (in years) for which stunting assessment and interventions apply' ),
143+ 'haz_threshold_stunted' : Parameter (
144+ Types .REAL ,
145+ 'HAZ score threshold below which a child is considered stunted' ),
146+ 'haz_threshold_severe_stunted' : Parameter (
147+ Types .REAL ,
148+ 'HAZ score threshold below which a child is considered severely stunted' ),
149+
150+ # Event timing
151+ 'main_polling_frequency_months' : Parameter (
152+ Types .INT ,
153+ 'Frequency in months for the main stunting polling event' ),
154+ 'followup_appointment_months' : Parameter (
155+ Types .INT ,
156+ 'Interval in months for scheduling follow-up appointments after stunting treatment' ),
138157 }
139158
140159 PROPERTIES = {
@@ -153,7 +172,7 @@ def __init__(self, name=None):
153172
154173 def read_parameters (self , resourcefilepath : Optional [Path ]= None ):
155174 self .load_parameters_from_dataframe (
156- read_csv_files (resourcefilepath / 'ResourceFile_Stunting' , files = 'Parameter_values ' )
175+ read_csv_files (resourcefilepath / 'ResourceFile_Stunting' , files = 'parameter_values ' )
157176 )
158177
159178 def initialise_population (self , population ):
@@ -171,11 +190,13 @@ def get_probs_stunting(_agegp):
171190 mean , stdev = p [f'prev_HAZ_distribution_age_{ _agegp [0 ]} _{ _agegp [1 ]} mo' ]
172191 haz_distribution = norm (loc = mean , scale = stdev )
173192
174- # Compute proportion "stunted" (HAZ <-2 )
175- p_stunted = haz_distribution .cdf (- 2.0 )
193+ # Compute proportion "stunted" (HAZ < threshold )
194+ p_stunted = haz_distribution .cdf (p [ 'haz_threshold_stunted' ] )
176195
177- # Compute proportion "severely stunted" given "stunted" (HAZ <-3 given HAZ <-2)
178- p_severely_stunted_given_stunted = haz_distribution .cdf (- 3.0 ) / haz_distribution .cdf (- 2.0 )
196+ # Compute proportion "severely stunted" given "stunted"
197+ # (HAZ < severe threshold given HAZ < stunted threshold)
198+ p_severely_stunted_given_stunted = (haz_distribution .cdf (p ['haz_threshold_severe_stunted' ]) /
199+ haz_distribution .cdf (p ['haz_threshold_stunted' ]))
179200
180201 # Return results needed as named tuple:
181202 result = namedtuple ('probs' , ['prob_stunting' , 'prob_severe_given_stunting' ])
@@ -308,7 +329,7 @@ def do_at_generic_first_appt(
308329
309330 # Schedule the HSI for provision of treatment based on the probability of
310331 # stunting diagnosis, provided the necessary symptoms are there.
311- if individual_properties ["age_years" ] <= 5 and is_stunted :
332+ if individual_properties ["age_years" ] <= self . parameters [ "max_age_for_stunting_years" ] and is_stunted :
312333 # Schedule the HSI for provision of treatment based on the probability of
313334 # stunting diagnosis
314335 if p_stunting_diagnosed > self .rng .random_sample ():
@@ -350,7 +371,7 @@ def make_lm_prob_becomes_stunted(self):
350371 ).when (
351372 "4 <= age_exact_years < 5" , p ["base_inc_rate_stunting_by_agegp" ][5 ]
352373 ).when (
353- "age_exact_years >= 5 " , 0.0
374+ f "age_exact_years >= { p [ 'max_age_for_stunting_years' ] } " , 0.0
354375 ),
355376 Predictor ('li_wealth' ,
356377 conditions_are_mutually_exclusive = True ).when (1 , 1.0 )
@@ -411,7 +432,7 @@ def make_lm_prob_progression_to_severe_stunting(self):
411432 p ['r_progression_severe_stunting_by_agegp' ][5 ]
412433 )
413434 .when (
414- 'age_exact_years >= 5.0 ' , 1.0
435+ f 'age_exact_years >= { p [ "max_age_for_stunting_years" ] } ' , 1.0
415436 ),
416437 Predictor ('un_ever_wasted' ,
417438 conditions_are_exhaustive = True ,
@@ -444,7 +465,7 @@ class StuntingPollingEvent(RegularEvent, PopulationScopeEventMixin):
444465 """Regular event that controls the onset of stunting, progression to severe stunting and natural recovery."""
445466
446467 def __init__ (self , module ):
447- super ().__init__ (module , frequency = DateOffset (months = 1 ))
468+ super ().__init__ (module , frequency = DateOffset (months = module . parameters [ 'main_polling_frequency_months' ] ))
448469 assert isinstance (module , Stunting )
449470
450471 def apply (self , population ):
@@ -459,7 +480,7 @@ def apply(self, population):
459480
460481 # Onset of Stunting
461482 eligible_for_stunting = (df .is_alive &
462- (df .age_exact_years < 5.0 ) &
483+ (df .age_exact_years < self . module . parameters [ 'max_age_for_stunting_years' ] ) &
463484 (df .un_HAZ_category == 'HAZ>=-2' ))
464485 idx_will_be_stunted = self .apply_model (
465486 model = models .lm_prob_becomes_stunted ,
@@ -470,7 +491,7 @@ def apply(self, population):
470491
471492 # Recovery from Stunting
472493 eligible_for_recovery = (df .is_alive &
473- (df .age_exact_years < 5.0 ) &
494+ (df .age_exact_years < self . module . parameters [ 'max_age_for_stunting_years' ] ) &
474495 (df .un_HAZ_category != 'HAZ>=-2' ) &
475496 ~ df .index .isin (idx_will_be_stunted ))
476497 idx_will_recover = self .apply_model (
@@ -482,7 +503,7 @@ def apply(self, population):
482503
483504 # Progression to Severe Stunting
484505 eligible_for_progression = (df .is_alive &
485- (df .age_exact_years < 5.0 ) &
506+ (df .age_exact_years < self . module . parameters [ 'max_age_for_stunting_years' ] ) &
486507 (df .un_HAZ_category == '-3<=HAZ<-2' ) & ~ df .index .isin (idx_will_be_stunted )
487508 & ~ df .index .isin (idx_will_recover ))
488509 idx_will_progress = self .apply_model (
@@ -527,8 +548,9 @@ def apply(self, population):
527548 """Log the current distribution of stunting classification by age"""
528549 df = population .props
529550
530- subset = df .loc [df .is_alive & (df .age_years < 5 )].copy ()
531- subset ["age_years" ] = pd .Categorical (subset ["age_years" ], categories = range (5 ))
551+ max_age = int (self .module .parameters ['max_age_for_stunting_years' ])
552+ subset = df .loc [df .is_alive & (df .age_years < max_age )].copy ()
553+ subset ["age_years" ] = pd .Categorical (subset ["age_years" ], categories = range (max_age ))
532554 d_to_log = subset .groupby (
533555 by = ['age_years' , 'un_HAZ_category' ]).size ().sort_index ().to_dict ()
534556
@@ -564,8 +586,9 @@ def apply(self, person_id, squeeze_factor):
564586 if not person .is_alive :
565587 return
566588
567- # Only do anything if child is under 5 and remains stunted
568- if (person .age_years < 5 ) and (person .un_HAZ_category in ['HAZ<-3' , '-3<=HAZ<-2' ]):
589+ # Only do anything if child is under max age and remains stunted
590+ if (person .age_years < self .module .parameters ['max_age_for_stunting_years' ]) and \
591+ (person .un_HAZ_category in ['HAZ<-3' , '-3<=HAZ<-2' ]):
569592
570593 # Provide supplementary feeding if consumable available, otherwise provide 'education only' (which has a
571594 # different probability of success).
@@ -585,7 +608,7 @@ def apply(self, person_id, squeeze_factor):
585608 self .sim .modules ['HealthSystem' ].schedule_hsi_event (
586609 hsi_event = self ,
587610 priority = 2 , # <-- lower priority that for wasting and most other HSI
588- topen = self .sim .date + pd .DateOffset (months = 6 )
611+ topen = self .sim .date + pd .DateOffset (months = self . module . parameters [ 'followup_appointment_months' ] )
589612 )
590613
591614
0 commit comments