Skip to content

Commit

Permalink
Merge branch 'refs/heads/master' into rmw/graph_dependencies
Browse files Browse the repository at this point in the history
  • Loading branch information
RachelMurray-Watson committed Oct 17, 2024
2 parents 987152f + 8d088f0 commit 8c4f656
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 12 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div style="text-align: center" align="center">
<img src="docs/thanzi-la-onse.png" alt="Thanzi La Onze" />
<img src="docs/thanzi-la-onse.png" alt="Thanzi la Onse" />
<br />
<h1>Thanzi la Onse model</h1>
</div>
Expand All @@ -24,7 +24,7 @@ The __Thanzi la Onse model (TLOmodel)__ is a part of the [Thanzi la Onse][thanzi
TLOmodel is developed in a collaboration between:

- [Kamuzu University of Health Sciences][kuhes-link]
- [MRC Centre for Global Infectioous Disease Analysis][mrc-gida-link], [Imperial College London][imperial-link]
- [MRC Centre for Global Infectious Disease Analysis][mrc-gida-link], [Imperial College London][imperial-link]
- [Institute for Global Health][igh-link], [University College London][ucl-link]
- [Centre for Advanced Research Computing][arc-link], [University College London][ucl-link]
- [Centre for Health Economics][che-link], [University of York][york-link]
Expand Down
4 changes: 3 additions & 1 deletion docs/publications.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,16 @@ Analyses Using The Model

* `Factors Associated with Consumable Stock-Outs in Malawi: Evidence from a Facility Census <https://www.sciencedirect.com/science/article/pii/S2214109X24000950>`_

* `The Effects of Health System Frailties on the Projected Impact of the HIV and TB Programmes in Malawi <https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4508436>`_
* `The Effects of Health System Frailties on the Projected Impact of the HIV and TB Programmes in Malawi <https://www.sciencedirect.com/science/article/pii/S2214109X24002596>`_

* `Estimating the health burden of road traffic injuries in Malawi using an individual-based model <https://injepijournal.biomedcentral.com/articles/10.1186/s40621-022-00386-6>`_

* `The potential impact of intervention strategies on COVID-19 transmission in Malawi: A mathematical modelling study. <https://bmjopen.bmj.com/content/11/7/e045196>`_

* `The potential impact of including pre-school aged children in the praziquantel mass-drug administration programmes on the S.haematobium infections in Malawi: a modelling study <https://www.medrxiv.org/content/10.1101/2020.12.09.20246652v1>`_

* `A Decade of Progress in HIV, Malaria, and Tuberculosis Initiatives in Malawi. <https://www.medrxiv.org/content/10.1101/2024.10.08.24315077v1>`_


Healthcare Seeking Behaviour
============================
Expand Down
79 changes: 70 additions & 9 deletions src/tlo/methods/healthsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,9 @@ def initialise_simulation(self, sim):
# whilst the actual scaling will only take effect from 2011 onwards.
sim.schedule_event(DynamicRescalingHRCapabilities(self), Date(sim.date))

# Schedule the logger to occur at the start of every year
sim.schedule_event(HealthSystemLogger(self), Date(sim.date.year, 1, 1))

def on_birth(self, mother_id, child_id):
self.bed_days.on_birth(self.sim.population.props, mother_id, child_id)

Expand Down Expand Up @@ -936,29 +939,32 @@ def setup_daily_capabilities(self, use_funded_or_actual_staffing):
This is called when the value for `use_funded_or_actual_staffing` is set - at the beginning of the simulation
and when the assumption when the underlying assumption for `use_funded_or_actual_staffing` is updated"""
# * Store 'DailyCapabilities' in correct format and using the specified underlying assumptions
self._daily_capabilities = self.format_daily_capabilities(use_funded_or_actual_staffing)
self._daily_capabilities, self._daily_capabilities_per_staff = self.format_daily_capabilities(use_funded_or_actual_staffing)

# Also, store the set of officers with non-zero daily availability
# (This is used for checking that scheduled HSI events do not make appointment requiring officers that are
# never available.)
self._officers_with_availability = set(self._daily_capabilities.index[self._daily_capabilities > 0])

def format_daily_capabilities(self, use_funded_or_actual_staffing: str) -> pd.Series:
def format_daily_capabilities(self, use_funded_or_actual_staffing: str) -> tuple[pd.Series,pd.Series]:
"""
This will updates the dataframe for the self.parameters['Daily_Capabilities'] so as to include
every permutation of officer_type_code and facility_id, with zeros against permutations where no capacity
This will updates the dataframe for the self.parameters['Daily_Capabilities'] so as to:
1. include every permutation of officer_type_code and facility_id, with zeros against permutations where no capacity
is available.
It also give the dataframe an index that is useful for merging on (based on Facility_ID and Officer Type)
2. Give the dataframe an index that is useful for merging on (based on Facility_ID and Officer Type)
(This is so that its easier to track where demands are being placed where there is no capacity)
3. Compute daily capabilities per staff. This will be used to compute staff count in a way that is independent of assumed efficiency.
"""

# Get the capabilities data imported (according to the specified underlying assumptions).
capabilities = pool_capabilities_at_levels_1b_and_2(
self.parameters[f'Daily_Capabilities_{use_funded_or_actual_staffing}']
)
capabilities = capabilities.rename(columns={'Officer_Category': 'Officer_Type_Code'}) # neaten

# Create new column where capabilities per staff are computed
capabilities['Mins_Per_Day_Per_Staff'] = capabilities['Total_Mins_Per_Day']/capabilities['Staff_Count']


# Create dataframe containing background information about facility and officer types
facility_ids = self.parameters['Master_Facilities_List']['Facility_ID'].values
Expand All @@ -978,7 +984,10 @@ def format_daily_capabilities(self, use_funded_or_actual_staffing: str) -> pd.Se
# Merge in information about facility from Master Facilities List
mfl = self.parameters['Master_Facilities_List']
capabilities_ex = capabilities_ex.merge(mfl, on='Facility_ID', how='left')


# Create a copy of this to store staff counts
capabilities_per_staff_ex = capabilities_ex.copy()

# Merge in information about officers
# officer_types = self.parameters['Officer_Types_Table'][['Officer_Type_Code', 'Officer_Type']]
# capabilities_ex = capabilities_ex.merge(officer_types, on='Officer_Type_Code', how='left')
Expand All @@ -991,6 +1000,13 @@ def format_daily_capabilities(self, use_funded_or_actual_staffing: str) -> pd.Se
how='left',
)
capabilities_ex = capabilities_ex.fillna(0)

capabilities_per_staff_ex = capabilities_per_staff_ex.merge(
capabilities[['Facility_ID', 'Officer_Type_Code', 'Mins_Per_Day_Per_Staff']],
on=['Facility_ID', 'Officer_Type_Code'],
how='left',
)
capabilities_per_staff_ex = capabilities_per_staff_ex.fillna(0)

# Give the standard index:
capabilities_ex = capabilities_ex.set_index(
Expand All @@ -999,16 +1015,25 @@ def format_daily_capabilities(self, use_funded_or_actual_staffing: str) -> pd.Se
+ '_Officer_'
+ capabilities_ex['Officer_Type_Code']
)

# Give the standard index:
capabilities_per_staff_ex = capabilities_per_staff_ex.set_index(
'FacilityID_'
+ capabilities_ex['Facility_ID'].astype(str)
+ '_Officer_'
+ capabilities_ex['Officer_Type_Code']
)

# Rename 'Total_Minutes_Per_Day'
capabilities_ex = capabilities_ex.rename(columns={'Total_Mins_Per_Day': 'Total_Minutes_Per_Day'})

# Checks
assert abs(capabilities_ex['Total_Minutes_Per_Day'].sum() - capabilities['Total_Mins_Per_Day'].sum()) < 1e-7
assert len(capabilities_ex) == len(facility_ids) * len(officer_type_codes)
assert len(capabilities_per_staff_ex) == len(facility_ids) * len(officer_type_codes)

# return the pd.Series of `Total_Minutes_Per_Day' indexed for each type of officer at each facility
return capabilities_ex['Total_Minutes_Per_Day']
return capabilities_ex['Total_Minutes_Per_Day'], capabilities_per_staff_ex['Mins_Per_Day_Per_Staff']

def _rescale_capabilities_to_capture_effective_capability(self):
# Notice that capabilities will only be expanded through this process
Expand All @@ -1030,6 +1055,11 @@ def _rescale_capabilities_to_capture_effective_capability(self):
)
if rescaling_factor > 1 and rescaling_factor != float("inf"):
self._daily_capabilities[officer] *= rescaling_factor

# We assume that increased daily capabilities is a result of each staff performing more
# daily patient facing time per day than contracted (or equivalently performing appts more
# efficiently).
self._daily_capabilities_per_staff[officer] *= rescaling_factor

def update_consumables_availability_to_represent_merging_of_levels_1b_and_2(self, df_original):
"""To represent that facility levels '1b' and '2' are merged together under the label '2', we replace the
Expand Down Expand Up @@ -2969,3 +2999,34 @@ def apply(self, population):
f"Now using mode: "
f"{self.module.mode_appt_constraints}"
)


class HealthSystemLogger(RegularEvent, PopulationScopeEventMixin):
""" This event runs at the start of each year and does any logging jobs for the HealthSystem module."""

def __init__(self, module):
super().__init__(module, frequency=DateOffset(years=1))

def apply(self, population):
"""Things to do at the start of the year"""
self.log_number_of_staff()

def log_number_of_staff(self):
"""Write to the summary log with the counts of staff (by cadre/facility/level) taking into account:
* Any scaling of capabilities that has taken place, year-by-year, or cadre-by-cadre
* Any re-scaling that has taken place at the transition into Mode 2.
"""

hs = self.module # HealthSystem module

# Compute staff counts from available capabilities (hs.capabilities_today) and daily capabilities per staff,
# both of which would have been rescaled to current efficiency levels if scale_to_effective_capabilities=True
# This returns the number of staff counts normalised by the self.capabilities_coefficient parameter
current_staff_count = dict((hs.capabilities_today/hs._daily_capabilities_per_staff).sort_index())

logger_summary.info(
key="number_of_hcw_staff",
description="The number of hcw_staff this year",
data=current_staff_count,
)

0 comments on commit 8c4f656

Please sign in to comment.