Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 0 additions & 3 deletions resources/ResourceFile_Measles/beta.csv

This file was deleted.

4 changes: 2 additions & 2 deletions resources/ResourceFile_Measles/cfr.csv
Git LFS file not shown
4 changes: 2 additions & 2 deletions resources/ResourceFile_Measles/parameters.csv
Git LFS file not shown
4 changes: 2 additions & 2 deletions resources/ResourceFile_Measles/symptoms.csv
Git LFS file not shown
2 changes: 1 addition & 1 deletion src/tlo/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ def load_parameters_from_dataframe(self, resource: pd.DataFrame) -> None:

resource.set_index('parameter_name', inplace=True)
skipped_data_types = ('DATA_FRAME', 'SERIES')
acceptable_labels = ['unassigned', 'undetermined', 'universal', 'local', 'scenario']
acceptable_labels = ['unassigned', 'undetermined', 'universal', 'local', 'scenario','design feature']
param_defaults = {'param_label': 'unassigned', 'prior_min': None, 'prior_max': None }

for _col in param_defaults.keys():
Expand Down
80 changes: 62 additions & 18 deletions src/tlo/methods/measles.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,44 @@ class Measles(Module, GenericFirstAppointmentsMixin):
Types.REAL, "Risk of scheduled death occurring if on treatment for measles complications"),
"symptom_prob": Parameter(
Types.DATA_FRAME, "Probability of each symptom with measles infection"),
"case_fatality_rate": Parameter(
Types.DICT, "Probability that case of measles will result in death if not treated")
"odds_ratio_health_seeking_in_children_rash": Parameter(
Types.REAL, "Odds ratio seeking care in children rash"),
"odds_ratio_health_seeking_in_adults_rash": Parameter(
Types.REAL, "Odds ratio seeking care in adults rash"),
"odds_ratio_health_seeking_in_children_otitis_media": Parameter(
Types.REAL, "Odds ratio seeking care in children otitis media"),
"odds_ratio_health_seeking_in_adults_otitis_media": Parameter(
Types.REAL, "Odds ratio seeking care in adults otitis media"),
"reduction_in_death_risk_measle_treated": Parameter(
Types.REAL, "Reduction in death risk when measles are treated"
),
"maternal_immunity_age_threshold": Parameter(
Types.REAL, "Age threshold below which children are protected by maternal immunity"
),
"age_min_symptoms_constant_rate": Parameter(
Types.REAL, "Maximum age for measles symptom probability lookup"
),
"death_timing_min_days": Parameter(
Types.REAL, "Minimum days from symptom onset to death"
),
"death_timing_max_days": Parameter(
Types.REAL, "Maximum days from symptom onset to death"
),
"natural_resolution_min_days": Parameter(
Types.REAL, "Minimum days from symptom onset to natural resolution"
),
"natural_resolution_max_days": Parameter(
Types.REAL, "Maximum days from symptom onset to natural resolution"
),
"symptom_onset_min_days": Parameter(
Types.REAL, "Minimum days from infection to symptom onset (incubation period)"
),
"symptom_onset_max_days": Parameter(
Types.REAL, "Maximum days from infection to symptom onset (incubation period)"
),
"treatment_followup_days": Parameter(
Types.REAL, "Days until follow-up appointment after treatment"
)
}

PROPERTIES = {
Expand Down Expand Up @@ -108,6 +144,8 @@ def read_parameters(self, resourcefilepath: Optional[Path] = None):
self.parameters["case_fatality_rate"] = workbook["cfr"].set_index('age')["probability"].to_dict()

# moderate symptoms all mapped to moderate_measles, pneumonia/encephalitis mapped to severe_measles

p = self.parameters
if "HealthBurden" in self.sim.modules.keys():
self.parameters["daly_wts"] = {
"rash": self.sim.modules["HealthBurden"].get_daly_weight(sequlae_code=205),
Expand All @@ -122,14 +160,14 @@ def read_parameters(self, resourcefilepath: Optional[Path] = None):
# Declare symptoms that this module will cause and which are not included in the generic symptoms:
self.sim.modules['SymptomManager'].register_symptom(
Symptom(name='rash',
odds_ratio_health_seeking_in_children=2.5,
odds_ratio_health_seeking_in_adults=2.5) # non-emergencies
odds_ratio_health_seeking_in_children=p['odds_ratio_health_seeking_in_children_rash'],
odds_ratio_health_seeking_in_adults=p['odds_ratio_health_seeking_in_adults_rash']) # non-emergencies
)

self.sim.modules['SymptomManager'].register_symptom(
Symptom(name='otitis_media',
odds_ratio_health_seeking_in_children=2.5,
odds_ratio_health_seeking_in_adults=2.5) # non-emergencies
odds_ratio_health_seeking_in_children=p['odds_ratio_health_seeking_in_children_otitis_media'],
odds_ratio_health_seeking_in_adults=p['odds_ratio_health_seeking_in_adults_otitis_media']) # non-emergencies
)

self.sim.modules['SymptomManager'].register_symptom(Symptom.emergency('encephalitis'))
Expand All @@ -149,6 +187,8 @@ def initialise_population(self, population):

def initialise_simulation(self, sim):
"""Schedule measles event to start straight away. Each month it will assign new infections"""
p = self.parameters

sim.schedule_event(MeaslesEvent(self), sim.date)
sim.schedule_event(MeaslesLoggingEvent(self), sim.date)
sim.schedule_event(MeaslesLoggingFortnightEvent(self), sim.date)
Expand All @@ -165,6 +205,7 @@ def initialise_simulation(self, sim):
"oxygen cylinders")
}


def on_birth(self, mother_id, child_id):
"""Initialise our properties for a newborn individual
assume all newborns are uninfected
Expand Down Expand Up @@ -196,12 +237,13 @@ def process_parameters(self):
"""Process the parameters (following being read-in) prior to the simulation starting.
Make `self.symptom_probs` to be a dictionary keyed by age, with values of dictionaries keyed by symptoms and
the probability of symptom onset."""
p = self.parameters
probs = self.parameters["symptom_prob"].set_index(["age", "symptom"])["probability"]
self.symptom_probs = {level: probs.loc[(level, slice(None))].to_dict() for level in probs.index.levels[0]}

# Check that a sensible value for a probability of symptom onset is declared for each symptom and for each age
# up to and including age 30
for _age in range(30 + 1):
# up to and including max age for symptom probabilities
for _age in range(int(p["age_min_symptoms_constant_rate"]) + 1):
assert set(self.symptoms) == set(self.symptom_probs.get(_age).keys())
assert all([0.0 <= x <= 1.0 for x in self.symptom_probs.get(_age).values()])

Expand Down Expand Up @@ -248,8 +290,8 @@ def apply(self, population):
protected_by_vaccine.loc[(df.va_measles == 1)] *= (1 - p["vaccine_efficacy_1"]) # partially susceptible
protected_by_vaccine.loc[(df.va_measles > 1)] *= (1 - p["vaccine_efficacy_2"]) # partially susceptible

# Find persons to be newly infected (no risk to children under 6 months as protected by maternal immunity)
new_inf = df.index[~df.me_has_measles & (df.age_exact_years >= 0.5) &
# Find persons to be newly infected (no risk to children under maternal immunity threshold as protected by maternal immunity)
new_inf = df.index[~df.me_has_measles & (df.age_exact_years >= p["maternal_immunity_age_threshold"]) &
(rng.random_sample(size=len(df)) < (trans_prob * protected_by_vaccine))]

logger.debug(key="MeaslesEvent",
Expand All @@ -274,11 +316,12 @@ def apply(self, person_id):

df = self.sim.population.props # shortcut to the dataframe
rng = self.module.rng
p = self.module.parameters

if not df.at[person_id, "is_alive"]:
return

ref_age = df.at[person_id, "age_years"].clip(max=30) # (For purpose of look-up age limit is 30 years)
ref_age = df.at[person_id, "age_years"].clip(max=p["age_min_symptoms_constant_rate"]) # (For purpose of look-up age limit)

# Determine if the person has "untreated HIV", which is defined as a person in any stage of HIV but not on
# successful treatment currently.
Expand All @@ -303,21 +346,22 @@ def apply(self, person_id):

# schedule the death
self.sim.schedule_event(
death_event, symp_onset + DateOffset(days=rng.randint(3, 7)))
death_event, symp_onset + DateOffset(days=rng.randint(p["death_timing_min_days"], p["death_timing_max_days"])))

else:
# schedule symptom resolution without treatment - this only occurs if death doesn't happen first
symp_resolve = symp_onset + DateOffset(days=rng.randint(7, 14))
symp_resolve = symp_onset + DateOffset(days=rng.randint(p["natural_resolution_min_days"], p["natural_resolution_max_days"]))
self.sim.schedule_event(MeaslesSymptomResolveEvent(self.module, person_id), symp_resolve)

def assign_symptoms(self, _age):
"""Assign symptoms for this case and returns the date on which symptom onset.
(Parameter values specify that everybody gets rash, fever and eye complain.)"""

rng = self.module.rng
p = self.module.parameters
person_id = self.target
symptom_probs_for_this_person = self.module.symptom_probs.get(_age)
date_of_symp_onset = self.sim.date + DateOffset(days=rng.randint(7, 21))
date_of_symp_onset = self.sim.date + DateOffset(days=rng.randint(p["symptom_onset_min_days"], p["symptom_onset_max_days"]))

symptoms_to_onset = [
_symp for (_symp, _prob), _rand in zip(
Expand Down Expand Up @@ -379,6 +423,7 @@ def __init__(self, module, person_id):

def apply(self, person_id):
df = self.sim.population.props
p = self.sim.parameters

if not df.at[person_id, "is_alive"]:
return
Expand All @@ -389,10 +434,8 @@ def apply(self, person_id):

if df.at[person_id, "me_on_treatment"]:

reduction_in_death_risk = 0.6

# Certain death (100%) is reduced by specified amount
p_death_with_treatment = 1. - reduction_in_death_risk
p_death_with_treatment = 1. - p['reduction_in_death_risk_measle_treated']

# If below that probability, death goes ahead
if self.module.rng.random_sample() < p_death_with_treatment:
Expand Down Expand Up @@ -438,6 +481,7 @@ def apply(self, person_id, squeeze_factor):

df = self.sim.population.props
symptoms = self.sim.modules["SymptomManager"].has_what(person_id=person_id)
p = self.module.parameters

# for non-complicated measles
item_codes = [self.module.consumables['vit_A']]
Expand All @@ -463,7 +507,7 @@ def apply(self, person_id, squeeze_factor):

# schedule symptom resolution following treatment
self.sim.schedule_event(MeaslesSymptomResolveEvent(self.module, person_id),
self.sim.date + DateOffset(days=7))
self.sim.date + DateOffset(days=p["treatment_followup_days"]))

def did_not_run(self):
logger.debug(key="HSI_Measles_Treatment",
Expand Down
Loading