Skip to content

Implement clinics in TLO#1617

Merged
tbhallett merged 105 commits intomasterfrom
fix-1608
Dec 4, 2025
Merged

Implement clinics in TLO#1617
tbhallett merged 105 commits intomasterfrom
fix-1608

Conversation

@sangeetabhatia03
Copy link
Copy Markdown
Collaborator

No description provided.

@sangeetabhatia03
Copy link
Copy Markdown
Collaborator Author

@marghe-molaro if my understanding is correct, the clinics object would be a nested dictionary - clinics = {Hiv: {Facility_id:daily_capability,...}, ...}
Can you check if this sounds correct?

@sangeetabhatia03
Copy link
Copy Markdown
Collaborator Author

In the future, we might want to separate the rescaling of fungible/non-fungible capabilities. For now, only fungible time is rescaled. TODO: Discuss with group.

@sangeetabhatia03
Copy link
Copy Markdown
Collaborator Author

hi @marghe-molaro as discussed, here is my plan for next steps:

  1. Edit HSIEventQueueItem to include a property called clinic_access (boolean).
  2. Define a getter function in HealthSystem:
def get_clinic_eligibility(str)
 """ Return true if str is one of the keys of the clinics dictionary"""
  1. Edit _add_hsi_event_queue_item_to_hsi_event_queue as follows: before creating HSIEventQueueItem, check for clinic eligibility using hsi_event.module. That is
clinic_access = get_clinic_eligibility(hsi_event.module)

and set the value in HSIEvent appropriately.

@marghe-molaro
Copy link
Copy Markdown
Collaborator

Looks good @sangeetabhatia03 - in the last step ("and set the value in HSIEvent appropriately.") I think you mean HSIEventQueueItem

@tbhallett
Copy link
Copy Markdown
Collaborator

Checking calibration work flow: https://github.com/UCL/TLOmodel/actions/runs/19100393245

Copy link
Copy Markdown
Collaborator

@marghe-molaro marghe-molaro left a comment

Choose a reason for hiding this comment

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

Hi @sangeetabhatia03,

Great job on this PR, I think looks basically ready!
I have pointed out a few things that need tidying up before it can be merged.
I would also add that the default ResourceFile_ClinicConfigurations doesn't really seem to require an "OtherClinic" clinic column.
(I would also vote to rename "OtherClinic", which sounds a bit strange in a scenario where to other clinics are present, to something like "GenericClinic".

We could have a chat about these comments over a meeting tomorrow?

Comment thread src/tlo/methods/hsi_event.py Outdated
)
)

self.eligible_clinic = health_system.get_clinic_eligibility(self)
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 the clinic eligibility should be an attribute of the HSIEventQueueItem, not of the HSI_Event itself (see original Issue #1617). This would highlight the fact that the eligibility of a given appointment for a given clinic is not an attribute of the HSI itself, but a policy level decision on how HSIs at a facility are handled (similar to priority), which in the model is always enforced through the HSIEventQueueItem class. We want to make sure that all the policy decisions can be dealt with centrally in the same location.

Notice that currently the clinic eligibility is defined both in HSI_Event and HSIEventQueueItem, which I think would create some confusion

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.

Note: I realise currently priority is an attribute of the HSI_Event too, but it should be :)

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.

This needs to be removed.

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.

Commit 9e9c9d7 removes this.

Comment thread src/tlo/methods/hsi_event.py Outdated
this does not demand officers that are _never_ available), and issue warning if not.
"""
if self.healthcare_system._officers_with_availability.issuperset(
if self.healthcare_system._officers_with_availability[self.eligible_clinic].issuperset(
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 eligible_clinic were to be moved to HSIEventQueueEvent this check would have to be moved to when item is added to the queue

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.

Margherita thinks _check_if_appt_footprint_can_run can be removed; I'll comment it out and proceed as it is not there and wait for Margherita's PR to remove this.

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.

_check_if_appt_footprint_can_run has been removed, and I have also removed the duplication of clinic eligibility from hsi_event

Comment thread src/tlo/methods/healthsystem.py Outdated
level = self._facility_by_facility_id[facility_id].level
# Only rescale if rescaling factor is greater than 1 (i.e. don't reduce
# available capabilities if these were under-used the previous year).
rescaling_factor = self._summary_counter.frac_time_used_by_officer_type_and_level(
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 we need to break down the rescaling factor by clinic
(and actually, it would be good to do this by facility_id too, but this is beyond the scope of the PR)

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.

Waiting for Margherita's PR to be merged.

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.

After merging the latest PR that sets up rescaling factors by facility-id, the rescaling now happens by clinic and by facility id.

Comment thread src/tlo/methods/healthsystem.py Outdated
def get_squeeze_factors(self, footprints_per_event, total_footprint, current_capabilities,
compute_squeeze_factor_to_district_level: bool
):
## TODO: check if there is a better place to intitalise this store
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.

Mhmh I don't think this would be the best place to initialise, as it would alter our estimation of the squeeze factor, since we are effectively "resetting" the squeeze factor every time this function is called?
I would move it to just after the capabilities are initialised

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.

Move it somehwere in pre_initialise_population

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.

Moved to the end of pre_initialise_population as discussed.

Comment thread src/tlo/methods/healthsystem.py Outdated
_priority = event.priority
event = event.hsi_event
squeeze_factor = squeeze_factor_per_hsi_event[ev_num] # todo use zip here!
clinic = self.get_clinic_eligibility(event)
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 would make more sense to call event.clinic_eligibility here, and not have to re-estimate this every time we go through the queue for every hsi.
Btw: I'm confused as to why the function self.get_clinic_eligibility works with event here (which is a HSIEventQueueItem) when it was defined as only accepting HSI_Event as an input (which here would be event.hsi_event)? Am I missing something?

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 had not set up get_clinic_eligibility to expect HSIEventQueueItem; I think this code works because I only use the TREATMENT_ID property, which the passed object has. I will now amend get_clinic_eligibility to expect HSIEventQueueItem only.

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.

Also addressed your original comment about using event.clinic_eligibility

Comment thread src/tlo/methods/healthsystem.py Outdated
else:
rand_queue = self.hsi_event_queue_counter

clinic_eligibility = self.get_clinic_eligibility(hsi_event)
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 would be best to move this to schedule_hsi_event, and pass it as an argument to this function, since clinic_eligibility is an attribute of the HSIEventQueueItem itself

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.

Done.

assert all(2010 in sheet['year'].values for sheet in self.parameters['yearly_HR_scaling'].values())


def validate_clinic_configuration(self, clinic_capabilities_df: pd.DataFrame):
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 we also need to double check that the clinics defined in the two separate resource files match. Is this checked somewhere else atm?

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.

Implemented this check via commit #89ab2f62576f9d6ead7372397913136df2627652

@sangeetabhatia03
Copy link
Copy Markdown
Collaborator Author

Hi @sangeetabhatia03,

Great job on this PR, I think looks basically ready! I have pointed out a few things that need tidying up before it can be merged. I would also add that the default ResourceFile_ClinicConfigurations doesn't really seem to require an "OtherClinic" clinic column. (I would also vote to rename "OtherClinic", which sounds a bit strange in a scenario where to other clinics are present, to something like "GenericClinic".

We could have a chat about these comments over a meeting tomorrow?

Renamed OtherClinic to GenericClinic #e233df1b9bd429b0f5e2769ddcc7a61fccfb1903

@sangeetabhatia03 sangeetabhatia03 marked this pull request as draft November 24, 2025 14:05
@sangeetabhatia03 sangeetabhatia03 marked this pull request as ready for review November 24, 2025 16:58
@sangeetabhatia03
Copy link
Copy Markdown
Collaborator Author

@tbhallett this PR is now ready for your review.

@tbhallett
Copy link
Copy Markdown
Collaborator

tbhallett commented Nov 27, 2025

Calibration workflow happening here: https://github.com/UCL/TLOmodel/actions/runs/19718098020

.... which all looks fine,

Copy link
Copy Markdown
Collaborator

@tbhallett tbhallett left a comment

Choose a reason for hiding this comment

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

This is looking very good.

Just some very minor quibbles noted here, that should be very quick.

One important thing though: I do think that we would like to have a "wiki" page about this to explain the changes and also to give examples of how these new ResourceFile need to look (for whoever is going to create them).

  • so many changes due just to formatting -- I presume this was neccessary to overcome the ruff changes!!?

Comment thread tests/test_healthsystem.py Outdated
Comment thread tests/test_healthsystem.py Outdated
Comment thread tests/test_healthsystem.py Outdated
Comment thread src/tlo/methods/healthsystem.py
Comment thread src/tlo/methods/healthsystem.py Outdated
Comment thread src/tlo/methods/healthsystem.py Outdated
Comment thread src/tlo/methods/healthsystem.py Outdated
@sangeetabhatia03
Copy link
Copy Markdown
Collaborator Author

@tbhallett tbhallett merged commit ca9458f into master Dec 4, 2025
64 of 65 checks passed
@tbhallett tbhallett deleted the fix-1608 branch December 4, 2025 17:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants