Skip to content

Commit e4ef5de

Browse files
Remove mode 0 option from HealthSystem (#1690)
* Remove mode 0 option from HealthSystem * Remove mode = 0 from depression and epilepsy tests * Style fix * Remove mention of mode 0 from malaria and CMD tests --------- Co-authored-by: Tim Hallett <[email protected]>
1 parent eda323f commit e4ef5de

File tree

6 files changed

+44
-158
lines changed

6 files changed

+44
-158
lines changed

src/tlo/methods/healthsystem.py

Lines changed: 32 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -325,11 +325,11 @@ class HealthSystem(Module):
325325

326326
# Mode Appt Constraints
327327
'mode_appt_constraints': Parameter(
328-
Types.INT, 'Integer code in `{0, 1, 2}` determining mode of constraints with regards to officer numbers '
329-
'and time - 0: no constraints, all HSI events run with no squeeze factor, 1: elastic constraints'
330-
', all HSI events run with squeeze factor, 2: hard constraints, only HSI events with no squeeze '
331-
'factor run. N.B. This parameter is over-ridden if an argument is provided'
332-
' to the module initialiser.',
328+
Types.INT, 'Integer code in `{1, 2}` determining mode of constraints with regards to officer numbers '
329+
'and time - 1: elastic constraints, all HSI events run with squeeze factor, provided '
330+
'officers required to deliver the HSI have capabilities > 0'
331+
'2: hard constraints, only HSI events with no squeeze factor run. N.B. This parameter'
332+
'is over-ridden if an argument is provided to the module initialiser.',
333333
),
334334
'mode_appt_constraints_postSwitch': Parameter(
335335
Types.INT, 'Mode considered after a mode switch in year_mode_switch.'),
@@ -368,10 +368,10 @@ def __init__(
368368
"""
369369
:param name: Name to use for module, defaults to module class name if ``None``.
370370
:param service_availability: A list of treatment IDs to allow.
371-
:param mode_appt_constraints: Integer code in ``{0, 1, 2}`` determining mode of
372-
constraints with regards to officer numbers and time - 0: no constraints,
373-
all HSI events run with no squeeze factor, 1: elastic constraints, all HSI
374-
events run with squeeze factor, 2: hard constraints, only HSI events with
371+
:param mode_appt_constraints: Integer code in ``{1, 2}`` determining mode of
372+
constraints with regards to officer numbers and time - 1: elastic constraints, all HSI
373+
events run with squeeze factor provided officers required have nonzero capabilities,
374+
2: hard constraints, only HSI events with
375375
no squeeze factor run.
376376
:param cons_availability: If 'default' then use the availability specified in the ResourceFile; if 'none', then
377377
let no consumable be ever be available; if 'all', then all consumables are always available. When using 'all'
@@ -422,7 +422,7 @@ def __init__(
422422

423423
self.mode_appt_constraints = None # Will be the final determination of the `mode_appt_constraints'
424424
if mode_appt_constraints is not None:
425-
assert mode_appt_constraints in {0, 1, 2}
425+
assert mode_appt_constraints in {1, 2}
426426
self.arg_mode_appt_constraints = mode_appt_constraints
427427

428428
self.rng_for_hsi_queue = None # Will be a dedicated RNG for the purpose of randomising the queue
@@ -2020,12 +2020,12 @@ def on_end_of_year(self) -> None:
20202020
self._write_hsi_event_counts_to_log_and_reset()
20212021
self._write_never_ran_hsi_event_counts_to_log_and_reset()
20222022

2023-
def run_individual_level_events_in_mode_0_or_1(self,
2023+
def run_individual_level_events_in_mode_1(self,
20242024
_list_of_individual_hsi_event_tuples:
20252025
List[HSIEventQueueItem]) -> List:
20262026
"""Run a list of individual level events. Returns: list of events that did not run (maybe an empty list)."""
20272027
_to_be_held_over = list()
2028-
assert self.mode_appt_constraints in (0, 1)
2028+
assert self.mode_appt_constraints == 1
20292029

20302030
if _list_of_individual_hsi_event_tuples:
20312031
# Examine total call on health officers time from the HSI events in the list:
@@ -2044,18 +2044,12 @@ def run_individual_level_events_in_mode_0_or_1(self,
20442044
self.running_total_footprint.update(footprint)
20452045

20462046
# Estimate Squeeze-Factors for today
2047-
if self.mode_appt_constraints == 0:
2048-
# For Mode 0 (no Constraints), the squeeze factors are all zero.
2049-
squeeze_factor_per_hsi_event = np.zeros(
2050-
len(footprints_of_all_individual_level_hsi_event))
2051-
else:
2052-
# For Other Modes, the squeeze factors must be computed
2053-
squeeze_factor_per_hsi_event = self.get_squeeze_factors(
2054-
footprints_per_event=footprints_of_all_individual_level_hsi_event,
2055-
total_footprint=self.running_total_footprint,
2056-
current_capabilities=self.capabilities_today,
2057-
compute_squeeze_factor_to_district_level=self.compute_squeeze_factor_to_district_level,
2058-
)
2047+
squeeze_factor_per_hsi_event = self.get_squeeze_factors(
2048+
footprints_per_event=footprints_of_all_individual_level_hsi_event,
2049+
total_footprint=self.running_total_footprint,
2050+
current_capabilities=self.capabilities_today,
2051+
compute_squeeze_factor_to_district_level=self.compute_squeeze_factor_to_district_level,
2052+
)
20592053

20602054
for ev_num, event in enumerate(_list_of_individual_hsi_event_tuples):
20612055
_priority = event.priority
@@ -2065,7 +2059,6 @@ def run_individual_level_events_in_mode_0_or_1(self,
20652059
# store appt_footprint before running
20662060
_appt_footprint_before_running = event.EXPECTED_APPT_FOOTPRINT
20672061

2068-
# Mode 0: All HSI Event run, with no squeeze
20692062
# Mode 1: All HSI Events run with squeeze provided latter is not inf
20702063
ok_to_run = True
20712064

@@ -2106,15 +2099,13 @@ def run_individual_level_events_in_mode_0_or_1(self,
21062099
self.running_total_footprint -= original_call
21072100
self.running_total_footprint += updated_call
21082101

2109-
# Don't recompute for mode=0
2110-
if self.mode_appt_constraints != 0:
2111-
squeeze_factor_per_hsi_event = self.get_squeeze_factors(
2112-
footprints_per_event=footprints_of_all_individual_level_hsi_event,
2113-
total_footprint=self.running_total_footprint,
2114-
current_capabilities=self.capabilities_today,
2115-
compute_squeeze_factor_to_district_level=self.
2116-
compute_squeeze_factor_to_district_level,
2117-
)
2102+
squeeze_factor_per_hsi_event = self.get_squeeze_factors(
2103+
footprints_per_event=footprints_of_all_individual_level_hsi_event,
2104+
total_footprint=self.running_total_footprint,
2105+
current_capabilities=self.capabilities_today,
2106+
compute_squeeze_factor_to_district_level=self.
2107+
compute_squeeze_factor_to_district_level,
2108+
)
21182109

21192110
else:
21202111
# no actual footprint is returned so take the expected initial declaration as the actual,
@@ -2277,7 +2268,7 @@ def _get_events_due_today(self) -> List:
22772268

22782269
return due_today
22792270

2280-
def process_events_mode_0_and_1(self, hold_over: List[HSIEventQueueItem]) -> None:
2271+
def process_events_mode_1(self, hold_over: List[HSIEventQueueItem]) -> None:
22812272
while True:
22822273
# Get the events that are due today:
22832274
list_of_individual_hsi_event_tuples_due_today = self._get_events_due_today()
@@ -2296,7 +2287,7 @@ def process_events_mode_0_and_1(self, hold_over: List[HSIEventQueueItem]) -> Non
22962287
list_of_individual_hsi_event_tuples_due_today_that_have_essential_equipment.append(item)
22972288

22982289
# Try to run the list of individual-level events that have their essential equipment
2299-
_to_be_held_over = self.module.run_individual_level_events_in_mode_0_or_1(
2290+
_to_be_held_over = self.module.run_individual_level_events_in_mode_1(
23002291
list_of_individual_hsi_event_tuples_due_today_that_have_essential_equipment,
23012292
)
23022293
hold_over.extend(_to_be_held_over)
@@ -2494,9 +2485,9 @@ def process_events_mode_2(self, hold_over: List[HSIEventQueueItem]) -> None:
24942485
# were exhausted, so here we traverse the queue again to ensure that if any events expired were
24952486
# left unchecked they are properly removed from the queue, and did_not_run() is invoked for all
24962487
# postponed events. (This should still be more efficient than querying the queue as done in
2497-
# mode_appt_constraints = 0 and 1 while ensuring mid-day effects are avoided.)
2488+
# mode_appt_constraints = 1 while ensuring mid-day effects are avoided.)
24982489
# We also schedule a call_never_run for any HSI below the lowest_priority_considered,
2499-
# in case any of them where left in the queue due to a transition from mode 0/1 to mode 2
2490+
# in case any of them where left in the queue due to a transition from mode 1 to mode 2
25002491
while len(self.module.HSI_EVENT_QUEUE) > 0:
25012492

25022493
next_event_tuple = hp.heappop(self.module.HSI_EVENT_QUEUE)
@@ -2527,7 +2518,7 @@ def process_events_mode_2(self, hold_over: List[HSIEventQueueItem]) -> None:
25272518
elif self.sim.date < next_event_tuple.topen:
25282519
# The event is not yet due (before topen). Do not stop querying the queue here if we have
25292520
# reached the lowest_priority_considered, as we want to make sure HSIs with lower priority
2530-
# (which may have been scheduled during a prior mode 0/1 period) are flushed from the queue.
2521+
# (which may have been scheduled during a prior mode 1 period) are flushed from the queue.
25312522
hp.heappush(list_of_events_not_due_today, next_event_tuple)
25322523

25332524
else:
@@ -2603,11 +2594,11 @@ def apply(self, population):
26032594
# Create hold-over list. This will hold events that cannot occur today before they are added back to the queue.
26042595
hold_over = list()
26052596

2606-
if self.module.mode_appt_constraints in (0, 1):
2597+
if self.module.mode_appt_constraints == 1:
26072598
# Run all events due today, repeating the check for due events until none are due
26082599
# (this allows for HSI that are added to the queue in the course of other HSI
26092600
# for this today to be run this day).
2610-
self.process_events_mode_0_and_1(hold_over)
2601+
self.process_events_mode_1(hold_over)
26112602

26122603
elif self.module.mode_appt_constraints == 2:
26132604
self.process_events_mode_2(hold_over)

tests/test_cardiometabolicdisorders.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ def make_simulation_health_system_functional(seed, cons_availability='all'):
331331
# Register the appropriate modules
332332
sim.register(demography.Demography(),
333333
enhanced_lifestyle.Lifestyle(),
334-
healthsystem.HealthSystem(disable=False, mode_appt_constraints=0,
334+
healthsystem.HealthSystem(disable=False, mode_appt_constraints=1,
335335
cons_availability=cons_availability),
336336
symptommanager.SymptomManager(),
337337
healthseekingbehaviour.HealthSeekingBehaviour(),

tests/test_depression.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def test_hsi_functions(tmpdir, seed):
122122
sim.register(demography.Demography(),
123123
simplified_births.SimplifiedBirths(),
124124
enhanced_lifestyle.Lifestyle(),
125-
healthsystem.HealthSystem(mode_appt_constraints=0, cons_availability='all',
125+
healthsystem.HealthSystem(mode_appt_constraints=1, cons_availability='all',
126126
hsi_event_count_log_period="simulation"),
127127
symptommanager.SymptomManager(),
128128
healthseekingbehaviour.HealthSeekingBehaviour(),
@@ -179,7 +179,7 @@ def test_hsi_functions_no_medication_available(tmpdir, seed):
179179
sim.register(demography.Demography(),
180180
simplified_births.SimplifiedBirths(),
181181
enhanced_lifestyle.Lifestyle(),
182-
healthsystem.HealthSystem(mode_appt_constraints=0, cons_availability='none',
182+
healthsystem.HealthSystem(mode_appt_constraints=1, cons_availability='none',
183183
hsi_event_count_log_period="simulation"),
184184
symptommanager.SymptomManager(),
185185
healthseekingbehaviour.HealthSeekingBehaviour(),

tests/test_epilepsy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def simulation(seed):
2727
sim.register(
2828
demography.Demography(),
2929
enhanced_lifestyle.Lifestyle(),
30-
healthsystem.HealthSystem( mode_appt_constraints=0),
30+
healthsystem.HealthSystem( mode_appt_constraints=1),
3131
healthburden.HealthBurden(),
3232
healthseekingbehaviour. HealthSeekingBehaviour(),
3333
symptommanager.SymptomManager(),

tests/test_healthsystem.py

Lines changed: 7 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -240,106 +240,6 @@ def test_policy_has_no_effect_on_mode1(tmpdir, seed):
240240
output[i]['tlo.methods.healthsystem']['HSI_Event'])
241241

242242

243-
@pytest.mark.slow
244-
def test_run_in_mode_0_with_capacity(tmpdir, seed):
245-
# Events should run and there be no squeeze factors
246-
# (Mode 0 -> No Constraints)
247-
248-
# Establish the simulation object
249-
sim = Simulation(
250-
start_date=start_date,
251-
seed=seed,
252-
log_config={
253-
"filename": "log",
254-
"directory": tmpdir,
255-
"custom_levels": {
256-
"tlo.methods.healthsystem": logging.DEBUG,
257-
}
258-
}, resourcefilepath=resourcefilepath
259-
)
260-
261-
# Define the service availability
262-
service_availability = ['*']
263-
264-
# Register the core modules
265-
sim.register(demography.Demography(),
266-
simplified_births.SimplifiedBirths(),
267-
enhanced_lifestyle.Lifestyle(),
268-
healthsystem.HealthSystem(service_availability=service_availability, capabilities_coefficient=1.0,
269-
mode_appt_constraints=0),
270-
symptommanager.SymptomManager(),
271-
healthseekingbehaviour.HealthSeekingBehaviour(),
272-
mockitis.Mockitis(),
273-
chronicsyndrome.ChronicSyndrome(),
274-
)
275-
276-
# Run the simulation
277-
sim.make_initial_population(n=popsize)
278-
sim.simulate(end_date=end_date)
279-
check_dtypes(sim)
280-
281-
# read the results
282-
output = parse_log_file(sim.log_filepath, level=logging.DEBUG)
283-
284-
# Do the checks for health system appts
285-
assert len(output['tlo.methods.healthsystem']['HSI_Event']) > 0
286-
assert output['tlo.methods.healthsystem']['HSI_Event']['did_run'].all()
287-
assert (output['tlo.methods.healthsystem']['HSI_Event']['Squeeze_Factor'] == 0.0).all()
288-
289-
# Check that some Mockitis cured occurred (though health system)
290-
assert any(sim.population.props['mi_status'] == 'P')
291-
292-
293-
@pytest.mark.slow
294-
def test_run_in_mode_0_no_capacity(tmpdir, seed):
295-
# Every events should run (no did_not_run) and no squeeze factors
296-
# (Mode 0 -> No Constraints)
297-
298-
# Establish the simulation object
299-
sim = Simulation(
300-
start_date=start_date,
301-
seed=seed,
302-
log_config={
303-
"filename": "log",
304-
"directory": tmpdir,
305-
"custom_levels": {
306-
"tlo.methods.healthsystem": logging.DEBUG,
307-
}
308-
}, resourcefilepath=resourcefilepath
309-
)
310-
311-
# Define the service availability
312-
service_availability = ['*']
313-
314-
# Register the core modules
315-
sim.register(demography.Demography(),
316-
simplified_births.SimplifiedBirths(),
317-
healthsystem.HealthSystem(service_availability=service_availability, capabilities_coefficient=0.0,
318-
mode_appt_constraints=0),
319-
symptommanager.SymptomManager(),
320-
healthseekingbehaviour.HealthSeekingBehaviour(),
321-
mockitis.Mockitis(),
322-
chronicsyndrome.ChronicSyndrome(),
323-
enhanced_lifestyle.Lifestyle()
324-
)
325-
326-
# Run the simulation
327-
sim.make_initial_population(n=popsize)
328-
sim.simulate(end_date=end_date)
329-
check_dtypes(sim)
330-
331-
# read the results
332-
output = parse_log_file(sim.log_filepath, level=logging.DEBUG)
333-
334-
# Do the checks
335-
assert len(output['tlo.methods.healthsystem']['HSI_Event']) > 0
336-
assert output['tlo.methods.healthsystem']['HSI_Event']['did_run'].all()
337-
assert (output['tlo.methods.healthsystem']['HSI_Event']['Squeeze_Factor'] == 0.0).all()
338-
339-
# Check that some mockitis cured occurred (though health system)
340-
assert any(sim.population.props['mi_status'] == 'P')
341-
342-
343243
@pytest.mark.slow
344244
def test_run_in_mode_1_with_capacity(tmpdir, seed):
345245
# All events should run with some zero squeeze factors
@@ -1320,8 +1220,8 @@ def test_HealthSystemChangeParameters(seed, tmpdir):
13201220
check that this is effectual in the case of consumables."""
13211221

13221222
initial_parameters = {
1323-
'mode_appt_constraints': 0,
1324-
'ignore_priority': False,
1223+
'mode_appt_constraints': 1,
1224+
'ignore_priority': True,
13251225
'capabilities_coefficient': 0.5,
13261226
'cons_availability': 'all',
13271227
'beds_availability': 'default',
@@ -1330,7 +1230,7 @@ def test_HealthSystemChangeParameters(seed, tmpdir):
13301230
}
13311231
new_parameters = {
13321232
'mode_appt_constraints': 2,
1333-
'ignore_priority': True,
1233+
'ignore_priority': False,
13341234
'capabilities_coefficient': 1.0,
13351235
'cons_availability': 'none',
13361236
'beds_availability': 'none',
@@ -1621,7 +1521,7 @@ def initialise_simulation(self, sim):
16211521
# Schedule an event that will schedule an HSI to run on the same day
16221522
sim.schedule_event(Event_To_Run_On_First_Day_Of_Simulation(self, person_id=0), sim.date)
16231523

1624-
for mode in (0, 1, 2):
1524+
for mode in (1, 2):
16251525

16261526
log_config = {
16271527
"filename": "log",
@@ -2058,7 +1958,7 @@ def collapse_into_set_of_strings(df: pd.DataFrame) -> Set:
20581958
# For each Mode and assumption on HR resources, test whether each type of appointment can run in each district
20591959
# at each level for which it is defined.
20601960
results = list()
2061-
for mode_appt_constraints in (0, 1, 2):
1961+
for mode_appt_constraints in (1, 2):
20621962
for use_funded_or_actual_staffing in ('actual', 'funded', 'funded_plus'):
20631963
sim = Simulation(start_date=Date(2010, 1, 1),
20641964
seed=seed, resourcefilepath=resourcefilepath)
@@ -2135,15 +2035,10 @@ def check_appt_works(district, level, appt_type) -> Tuple:
21352035

21362036
results = pd.DataFrame(results)
21372037

2138-
# check under each mode (0, 1, 2) and each HR scenario (actual, funded, funded_plus), the hsi runs as we expect.
2038+
# check under each mode (1, 2) and each HR scenario (actual, funded, funded_plus), the hsi runs as we expect.
21392039
# note that in both actual and funded scenarios, there are some required (by appt time) HCW cadres not there, i.e.,
21402040
# those cadres with 0-minute capability.
21412041

2142-
# mode 0 - actual, funded, funded_plus -> every hsi runs, with sqz=0.0
2143-
# as in mode 0, we assume no constraints at all
2144-
assert results.loc[results['mode_appt_constraints'] == 0, 'hsi_did_run'].all()
2145-
assert (results.loc[results['mode_appt_constraints'] == 0, 'sqz'] == 0.0).all()
2146-
21472042
# mode 1 - actual, funded, funded_plus -> every hsi that does run, has sqz in [0.0, Inf)
21482043
res = results.loc[(results['mode_appt_constraints'] == 1) & (results['hsi_did_run'])]
21492044
assert res['sqz'].between(0.0, float('inf'), 'left').all()
@@ -2583,7 +2478,7 @@ def initialise_simulation(self, sim):
25832478
resourcefilepath=resourcefilepath)
25842479
sim.register(
25852480
demography.Demography(),
2586-
healthsystem.HealthSystem(mode_appt_constraints=0),
2481+
healthsystem.HealthSystem(mode_appt_constraints=1),
25872482
DummyModule(),
25882483
# Disable sorting + checks to avoid error due to missing dependencies
25892484
sort_modules=False,

0 commit comments

Comments
 (0)