Skip to content
Draft
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
07b12f5
scenario file
PempheroM Nov 13, 2025
cb095e3
Merge branch 'master' into pemphero/nurses_scenario
thewati Nov 13, 2025
6617d49
changes_yearmode,hr scaling
PempheroM Nov 20, 2025
f586244
added default function
PempheroM Nov 21, 2025
94a9b35
reformat
PempheroM Nov 21, 2025
7f24781
Merge remote-tracking branch 'origin/pemphero/nurses_scenario' into p…
PempheroM Nov 21, 2025
ec05b17
remove unused input
PempheroM Nov 21, 2025
97c63a9
Merge branch 'master' into pemphero/nurses_scenario
PempheroM Nov 21, 2025
7994ed8
added files for improved and worse case scenarios
PempheroM Nov 27, 2025
9cb86db
Merge remote-tracking branch 'origin/pemphero/nurses_scenario' into p…
PempheroM Nov 27, 2025
40406ef
addressing comments
PempheroM Dec 8, 2025
6657544
.
thewati Dec 9, 2025
0a95a42
Merge remote-tracking branch 'origin/master'
thewati Dec 9, 2025
6e84902
Merge remote-tracking branch 'origin/master'
thewati Dec 28, 2025
f6cd794
Merge remote-tracking branch 'origin/master'
thewati Jan 12, 2026
660f2a0
Merge remote-tracking branch 'origin/master'
thewati Jan 19, 2026
74e7486
Merge remote-tracking branch 'origin/master'
thewati Jan 23, 2026
e3d3c41
Merge remote-tracking branch 'origin/master'
thewati Jan 27, 2026
992984a
Merge remote-tracking branch 'origin/master'
thewati Feb 3, 2026
7ef22f1
Merge branch 'master' into pemphero/nurses_scenario
thewati Feb 3, 2026
45174af
Merge remote-tracking branch 'origin/pemphero/nurses_scenario' into p…
thewati Feb 3, 2026
cc60285
setup analyses for qs
thewati Feb 3, 2026
95f4020
isort
thewati Feb 3, 2026
6d2ba99
change start and end years
thewati Feb 4, 2026
af3261e
Merge remote-tracking branch 'origin/master'
thewati Feb 4, 2026
55f31fd
Merge branch 'master' into pemphero/nurses_scenario
thewati Feb 4, 2026
b789b7a
Merge branch 'master' into pemphero/nurses_scenario
tbhallett Feb 5, 2026
9fa9a29
Merge branch 'master' into pemphero/nurses_scenario
tbhallett Feb 6, 2026
d5def29
TH suggestions
tbhallett Feb 6, 2026
beadfee
linting!
tbhallett Feb 6, 2026
8c0a02d
Merge remote-tracking branch 'origin/master'
thewati Feb 9, 2026
541555d
Merge remote-tracking branch 'origin/master'
thewati Feb 11, 2026
685d91c
Merge remote-tracking branch 'origin/master'
thewati Feb 12, 2026
481e2bf
plots of draws
thewati Feb 16, 2026
f48c011
Merge remote-tracking branch 'origin/master'
thewati Feb 16, 2026
0687ecc
Merge branch 'master' into pemphero/nurses_scenario
thewati Feb 16, 2026
29f3a3d
Merge remote-tracking branch 'origin/master'
thewati Feb 19, 2026
f8a393d
Merge branch 'master' into pemphero/nurses_scenario
thewati Feb 20, 2026
417503d
from LFS to Git
thewati Feb 20, 2026
e7c2568
Label plots with names and not numbers
thewati Feb 20, 2026
0b139b2
detailed plots
thewati Feb 24, 2026
ea79f95
plots for nurse cadre counts and appointments over time
thewati Feb 25, 2026
5436c6f
Merge remote-tracking branch 'origin/master'
thewati Feb 25, 2026
f3d65f8
Merge remote-tracking branch 'origin/master'
thewati Mar 11, 2026
6b96607
staff num more
thewati Mar 11, 2026
7d92e23
Merge remote-tracking branch 'origin/master'
thewati Mar 12, 2026
05aba63
Merge remote-tracking branch 'origin/master'
thewati Mar 19, 2026
dc00df5
Merge remote-tracking branch 'origin/master'
thewati Mar 30, 2026
dfb7ff1
Merge remote-tracking branch 'origin/master'
thewati Apr 20, 2026
2aab2a4
Merge branch 'master' into pemphero/nurses_scenario
thewati Apr 20, 2026
0b7c8a4
update for next run
thewati Apr 20, 2026
2befeab
comment out unused
thewati Apr 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Git LFS file not shown
Git LFS file not shown
195 changes: 195 additions & 0 deletions src/scripts/nurses_analyses/nurses_scenario_analyses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
"""
Comment thread
tbhallett marked this conversation as resolved.
This scenario file sets up the scenarios for simulating the effects of nursing staffing levels
The scenario
0- Baseline scenario
1-
2-


"""
from pathlib import Path
from typing import Dict

from tlo import Date, logging
from tlo.analysis.utils import get_parameters_for_status_quo, mix_scenarios
from tlo.methods.fullmodel import fullmodel
from tlo.methods.scenario_switcher import ImprovedHealthSystemAndCareSeekingScenarioSwitcher
from tlo.scenario import BaseScenario


class StaffingScenario(BaseScenario):
def __init__(self):
super().__init__()
self.seed = 0
self.start_date = Date(2010, 1, 1)
self.end_date = Date(2030, 1, 1)
self.initial_population_size = 200
self._scenarios = self._get_scenarios()
self.number_of_draws = len(self._scenarios)
self.number_of_draws = 2
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Line 29 is repeating line 28, so line 29 can be dropped, I think.

self.runs_per_draw = 2
Comment thread
tbhallett marked this conversation as resolved.
Outdated

def log_configuration(self):
return {
'filename': 'nurses_scenario_outputs',
'directory': Path('./outputs'), # <- (specified only for local running)
'custom_levels': {
'*': logging.WARNING,
'tlo.methods.demography': logging.INFO,
'tlo.methods.demography.detail': logging.WARNING,
'tlo.methods.healthburden': logging.INFO,
'tlo.methods.healthsystem.summary': logging.INFO,
}
}

def modules(self):
return fullmodel(resourcefilepath=self.resources) + [
ImprovedHealthSystemAndCareSeekingScenarioSwitcher(resourcefilepath=self.resources)]

def draw_parameters(self, draw_number, rng):
if draw_number < self.number_of_draws:
return list(self._scenarios.values())[draw_number]
else:
return

def _default_of_all_scenarios(self) -> Dict:
return mix_scenarios(
get_parameters_for_status_quo(),
{
'HealthSystem': {
'mode_appt_constraints': 1,
'mode_appt_constraints_postSwitch': 2,
"scale_to_effective_capabilities": True,
# This happens in the year before mode change, as the model calibration is done by that year
"year_mode_switch": 2020,
'cons_availability': 'default',
'cons_availability_postSwitch': "all",
# 'year_cons_availability_switch': 2025,
'yearly_HR_scaling_mode': 'historical_scaling', # for 5 years of 2020-2024; source data year 2019
"policy_name": 'Naive',
"tclose_overwrite": 1,
"tclose_days_offset_overwrite": 7,
},
'ImprovedHealthSystemAndCareSeekingScenarioSwitcher': {
'max_healthcare_seeking': [False, False],
'max_healthsystem_function': [False, False],
'year_of_switch': 2025,
}
},
)

# def _baseline_scenario(self) -> Dict:
# return mix_scenarios(
# self._default_of_all_scenarios(),
# {
# 'HealthSystem': {
# 'ResourceFile_HR_scaling_by_level_and_officer_type': "historical_scaling",
# 'mode_appt_constraints_postSwitch': 2,
# "use_funded_or_actual_staffing": "actual",
# },
# },
# )
#
# def _improved_staffing_scenario(self) -> Dict:
# return mix_scenarios(
# self._default_of_all_scenarios(),
# {
# 'HealthSystem': {
# 'ResourceFile_HR_scaling_by_level_and_officer_type': "historical_scaling",
# 'mode_appt_constraints_postSwitch': 2,
# "use_funded_or_actual_staffing": "funded_plus",
# },
# },
# )
#
# def _worst_case_scenario(self) -> Dict:
# return mix_scenarios(
# self._default_of_all_scenarios(),
# {
# 'HealthSystem': {
# 'ResourceFile_HR_scaling_by_level_and_officer_type': "historical_scaling",
# 'mode_appt_constraints_postSwitch': 2,
# "use_funded_or_actual_staffing": "actual",
# },
# },
# )
####################################################################
def _get_scenarios(self) -> Dict[str, Dict]:
"""Return the Dict with values for the parameters that are changed, keyed by a name for the scenario.
"""
return {
"Baseline":
mix_scenarios(
self._default_of_all_scenarios(),
{
"HealthSystem": {
'ResourceFile_HR_scaling_by_level_and_officer_type': "default",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is not here is implement the change of nursing cadre capabilities. It should be in the class ConstantRescalingHRCapabilities in the health system module to read the updated capabilities, and then schedule the expansion event every year from a start year to an end year (normally the simulation end) in the health system module.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies to all scenarios.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand you correctly, you want use to change the expansion every year. Aren't we just supposed to change yearly_HR_scaling_mode for that? Or are you suggesting that we have to re-write class ConstantRescalingHRCapabilities for yearly scaling?

Copy link
Copy Markdown
Collaborator

@BinglingICL BinglingICL Dec 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Wati, we are supposed to change year_HR_scaling_by_level_and_officer_type parameter here, such as set it to be 2025, so that the event of ConstantRescalingHRCapabilities will be scheduled on the first day in the year of 2025, meaning the expansion will happen on that day and the scaled up staff capabilities will remain onwards till the end of simulation

My comment above meant that 'ResourceFile_HR_scaling_by_level_and_officer_type' is not a health system module parameter, so we cannot modify it. Instead, we should modify 'HR_scaling_by_level_and_officer_type_mode' parameter, such as set it to be "default" or "custom" (the csv file that store the values of scaling up factors).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter name of yearly_HR_scaling_mode is quite similar and can be misleading, but it is related to a default historial HRH growth from 2020 to 2024, which is not relevant to our nursing expansion from 2025 here.

'mode_appt_constraints_postSwitch': 2,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All scenarios use Mode 2 since 2025; so line 61 is enough.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies to all scenarios.

"use_funded_or_actual_staffing": "actual",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This parameter is to be untouched I think. All HRH capabilities change will be implemented by changing the values in the csv files in ResourceFile_HR_scaling_by_level_and_officer_type folder.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies to all scenarios.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we did this. You will notice that we changed the csv value files in the ResourceFile_HR_scaling_by_level_and_officer_type folder for scenarios Improved Staffing Doubled Establishment, Worse Case. These are associated with csv files custom_doubling.csv and custom_worse.csv respectively. Is this what you mean? Or do you mean we should delete all lines where we set ResourceFile_HR_scaling_by_level_and_officer_type?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Pemphero, here I meant to not modifying use_funded_or_actual_staffing parameter in this script, which is not necessary. So all relevant lines on this should be dropped.

It is good that we have already the csv files to modify the nursing staffing. To implement such changes, we just need to modify the HR_scaling_by_level_and_officer_type_mode as mentioned in my response to Wati.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we remove use_funded_or_actual_staffing , how best are we going to represent the scenarios where in the baseline scenario we are using the actual (filled positions) and in the improved scenario, we are using the funded_plus (staff establishment)?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Pemphero, sorry for my possible misunderstanding, but may I confirm: in your improved scenario, you will only have the nursing cadre to achieve the establishment level? Considering that all other cadres remain as current levels across all scenarios.

If so, you do not need to modify use_funded_or_actual_staffing here but to calculate the corresponding scale-up factor and store it in the csv file in the ResourceFile_HR_scaling_by_level_and_officer_type folder, and then modify the HR_scaling_by_level_and_officer_type_mode parameter.

It would be good that we have a clear diagram for all nursing expansion scenarios, specifying the expansion time/period along the simulation timeline. And we could have further discussions on this if anything unclear. (I am attaching one example from my work, which is yearly expansion from 2025 to 2034; not a one-time scaling-up at the beginning of 2025 though.)
full expansion path

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Bingling and Pemphero,
The attached document contains the scenarios for this study. Of course, its not final.

For Improved Staffing scenario or scaling factor, I went through Tara's paper. Was thinking we could use a linear establishment filling for the scaling factor as seen below. The only challenge would be that we need to find number of filled nursing posts in the baseline year (N0) and approved nursing establishment (E) as seen below. It would also be easy to get a formula for the worst case from this. This is something we would try to find.

Improved Staffing (Nurses Only): Establishment Filling

Let:

  • N0 = number of filled nursing posts in the baseline year (maybe 2024)
  • E = approved nursing establishment
  • T = target year (2030)
  • y = current simulation year, where y ∈ {2025, 2026, …, 2030}

We assume linear filling of vacant nursing posts:

N_y = N0 + ((E - N0) / (T - 2024)) * (y - 2024)

The nurse scaling factor applied in year y is:

SF_y = N_y / N0

Nurse Scenario Shortage

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Wati and Pemphero, these look really great to me! And I think linear growth can be a good assumption. Thanks very much.

The only thing to update is for yearly scaling up factor, it is SF_y = N_y/N_(y-1), as I understand from the scaling-up mechanism in TLO. And we can double check this once the test runs are done.

},
}
),

"Improved Staffing":
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this scenario to scale up all cadres to meet the establishment level? If so, we need to calculate the correspondeing scaling up factors to be saved in a csv file in the folder ResourceFile_HR_scaling_by_level_and_officer_type.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that this scenario consists of two parts: one is to expand nurse cadre, and the other is to expand other cadres. I think these two parts may need to be explicitly separated in the scenario definitions. By which I mean, the main variable in the problem is to change the nursing capabilities, and basing on that, consider what capability levels of other cadres are to be specified across the scenarios.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In our previous discussions, we decided to keep all other cadres constant and use historical growth (i.e., Only nurses change. Other cadres remain constant)

mix_scenarios(
self._default_of_all_scenarios(),
{
"HealthSystem": {
'ResourceFile_HR_scaling_by_level_and_officer_type': "default",
'mode_appt_constraints_postSwitch': 2,
"use_funded_or_actual_staffing": "funded_plus",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in this scenario, if you want to scale up the nursing staff to the establishment level in the year of 2025: we do not use "use_funded_or_actual_staffing": "funded_plus", which will not work; instead, we create a csv file to store the scale-up factor (e.g. established nursing count/2024 nursing count) that will enable such expansion and set HR_scaling_by_level_and_officer_type_mode to the csv file name.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies to the next scenario where you want to increase nursing count to doubling establishment.

},
}
),

"Improved Staffing Doubled Establishment":
mix_scenarios(
self._default_of_all_scenarios(),
{
"HealthSystem": {
'ResourceFile_HR_scaling_by_level_and_officer_type': "custom_doubling",
'mode_appt_constraints_postSwitch': 2,
"use_funded_or_actual_staffing": "funded_plus",
},
}
),


"Worse Case":
mix_scenarios(
self._default_of_all_scenarios(),
{
"HealthSystem": {
'ResourceFile_HR_scaling_by_level_and_officer_type': "custom_worse",
'mode_appt_constraints_postSwitch': 2,
"use_funded_or_actual_staffing": "actual",
},
}
),
}




# To be sensitivity analysis
# def _baseline_scenario(self) -> Dict:
# return mix_scenarios(
# self._default_of_all_scenarios(),
# {
# 'HealthSystem': {
# 'ResourceFile_HR_scaling_by_level_and_officer_type': "historical_scaling",
# 'year_mode_switch': 2020,
# 'mode_appt_constraints_postSwitch': 2,
# 'scale_to_effective_capabilities': True,
# "use_funded_or_actual_staffing": "actual",
# "year_cons_availability_switch": 2025,
# "cons_availability_postSwitch": "all",
# },
# },
# )


if __name__ == '__main__':
from tlo.cli import scenario_run

scenario_run([__file__])
Loading