PrEP
name: str
Name of feature in the params file. Also used to name the attribute in Agent
stats: List[str]
PrEP collects the following stats:
- prep - number of agents with active PrEP
- prep_new - number of agents who became active PrEP this time step
- prep_injectable - number of agents on injectable PrEP
- prep_oral - number of agents on oral PrEP
add_agent(agent)
classmethod
Add an agent to the class (not instance).
Add agent to the PrEP counts by race, and add the agent to the set of new agents.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
agent |
agent.Agent |
the agent to add to the class attributes |
required |
Source code in titan/features/prep.py
@classmethod
def add_agent(cls, agent: "agent.Agent"):
"""
Add an agent to the class (not instance).
Add agent to the PrEP counts by race, and add the agent to the set of new agents.
args:
agent: the agent to add to the class attributes
"""
# set up if this is the first time being called
cls.counts[agent.race] += 1
cdc_eligible(self)
Determine agent eligibility for PrEP under CDC criteria
Returns:
Type | Description |
---|---|
bool |
cdc eligibility |
Source code in titan/features/prep.py
def cdc_eligible(self) -> bool:
"""
Determine agent eligibility for PrEP under CDC criteria
returns:
cdc eligibility
"""
if self.agent.is_msm():
return True
ongoing_duration = self.agent.location.params.partnership.ongoing_duration
for rel in self.agent.relationships:
partner = rel.get_partner(self.agent)
if rel.duration > ongoing_duration and partner.hiv.dx: # type: ignore[attr-defined]
return True
if partner.drug_type == "Inj" or partner.is_msm():
return True
return False
discontinue(self)
Discontinue PrEP usage
Source code in titan/features/prep.py
def discontinue(self):
"""
Discontinue PrEP usage
"""
self.active = False
self.type = ""
self.time = None
self.last_dose_time = None
self.remove_agent(self.agent)
eligible(self, time)
Determine if an agent is eligible for PrEP
Returns:
Type | Description |
---|---|
bool |
whether the agent is eligible |
Source code in titan/features/prep.py
def eligible(self, time) -> bool:
"""
Determine if an agent is eligible for PrEP
returns:
whether the agent is eligible
"""
params = self.agent.location.params
if self.agent.hiv.active or time < params.prep.start_time: # type: ignore[attr-defined]
return False
target_model = params.prep.target_model
gender = params.classes.sex_types[self.agent.sex_type].gender
if (
self.active
or self.agent.vaccine.active # type: ignore[attr-defined]
or params.features.random_trial
):
return False
all_eligible_models = {"Allcomers", "Racial"}
if all_eligible_models.intersection(target_model):
return True
if "cdc_women" in target_model:
if gender == "F":
if self.cdc_eligible():
return True
if "cdc_msm" in target_model:
if gender == "M" and self.cdc_eligible():
return True
if "pwid_sex" in target_model:
if self.agent.drug_type == "Inj" and self.cdc_eligible():
return True
if "pwid" in target_model:
if self.agent.drug_type == "Inj":
return True
if "ssp_sex" in target_model:
if self.agent.syringe_services.active and self.cdc_eligible(): # type: ignore[attr-defined]
return True
if "ssp" in target_model:
if self.agent.syringe_services.active: # type: ignore[attr-defined]
return True
return False
enroll(self, rand_gen, time)
Enroll an agent in PrEP
Parameters:
Name | Type | Description | Default |
---|---|---|---|
rand_gen |
|
random number generator |
required |
Source code in titan/features/prep.py
def enroll(self, rand_gen, time):
"""
Enroll an agent in PrEP
args:
rand_gen: random number generator
"""
params = self.agent.location.params
self.active = True
self.time = time
self.last_dose_time = time
self.adherent = (
rand_gen.random()
< params.demographics[self.agent.race]
.sex_type[self.agent.sex_type]
.prep.adherence
)
if "Inj" in params.prep.type and "Oral" in params.prep.type:
if rand_gen.random() < params.prep.lai.prob:
self.type = "Inj"
else:
self.type = "Oral"
else:
self.type = params.prep.type[0]
self.add_agent(self.agent)
get_acquisition_risk_multiplier(self, time, interaction_type)
Get a multiplier for how prep reduces risk of HIV acquisition.
By default, returns 1.0
Parameters:
Name | Type | Description | Default |
---|---|---|---|
time |
int |
the current model time step |
required |
interaction_type |
str |
The type of interaction where the agent could acquire HIV (e.g. 'sex', 'injection' - from [params.classes.interaction_types]) |
required |
Source code in titan/features/prep.py
def get_acquisition_risk_multiplier(self, time: int, interaction_type: str):
"""
Get a multiplier for how prep reduces risk of HIV acquisition.
By default, returns 1.0
args:
time: the current model time step
interaction_type: The type of interaction where the agent could acquire HIV (e.g. 'sex', 'injection' - from [params.classes.interaction_types])
"""
if self.active and self.last_dose_time is not None:
params = self.agent.location.params
if self.type == "Oral":
adherence = "adherent" if self.adherent else "non_adherent"
return 1.0 - params.prep.efficacy[adherence]
elif self.type == "Inj":
annualized_last_dose_time = (
time - self.last_dose_time
) / params.model.time.steps_per_year
annualized_half_life = params.prep.half_life / 365
load = params.prep.peak_load * (
(0.5) ** (annualized_last_dose_time / annualized_half_life)
)
return np.exp(-5.528636721 * load)
return 1.0
init_agent(self, pop, time)
Initialize the agent for this feature during population initialization (Population.create_agent
). Called on only features that are enabled per the params.
If an agent does not have HIV, is PrEP eligible, and time is at least the prep start time, they are randomly asigned to enroll in PrEP.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
pop |
population.Population |
the population this agent is a part of |
required |
time |
int |
the current time step |
required |
Source code in titan/features/prep.py
def init_agent(self, pop: "population.Population", time: int):
"""
Initialize the agent for this feature during population initialization (`Population.create_agent`). Called on only features that are enabled per the params.
If an agent does not have HIV, is PrEP eligible, and time is at least the prep start time, they are randomly asigned to enroll in PrEP.
args:
pop: the population this agent is a part of
time: the current time step
"""
params = self.agent.location.params
if self.eligible(time):
if "Racial" in params.prep.target_model:
if (
pop.pop_random.random()
< params.demographics[self.agent.race]
.sex_type[self.agent.sex_type]
.prep.init
):
self.enroll(pop.pop_random, time)
elif pop.pop_random.random() < params.prep.init:
self.enroll(pop.pop_random, time)
init_class(params)
classmethod
Initialize the counts dictionary for the races in the model.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
params |
ObjMap |
the population params |
required |
Source code in titan/features/prep.py
@classmethod
def init_class(cls, params: "ObjMap"):
"""
Initialize the counts dictionary for the races in the model.
args:
params: the population params
"""
cls.counts = {race: 0 for race in params.classes.races}
initiate(self, model, force=False)
Place agents onto PrEP treatment. PrEP treatment assumes that the agent knows their HIV status is negative.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
model |
model.TITAN |
instance of TITAN being run |
required |
force |
bool |
whether to force the agent to enroll instead of using the appropriate algorithm per the prep params |
False |
Source code in titan/features/prep.py
def initiate(self, model: "model.TITAN", force: bool = False):
"""
Place agents onto PrEP treatment. PrEP treatment assumes that the agent knows their HIV status is negative.
args:
model : instance of TITAN being run
force : whether to force the agent to enroll instead of using the appropriate algorithm per the prep params
"""
# Prep only valid for agents not on prep and are HIV negative
if self.active or self.agent.hiv.active: # type: ignore[attr-defined]
return
params = self.agent.location.params
if force:
self.enroll(model.run_random, model.time)
elif params.prep.cap_as_prob:
if "Racial" in params.prep.target_model:
if (
model.run_random.random()
<= params.demographics[self.agent.race]
.sex_type[self.agent.sex_type]
.prep.cap
):
self.enroll(model.run_random, model.time)
else:
if model.run_random.random() <= params.prep.cap:
self.enroll(model.run_random, model.time)
else:
if "Racial" in params.prep.target_model:
num_prep_agents = self.counts[self.agent.race]
all_hiv_agents = exposures.HIV.agents
all_race = {
a for a in model.pop.all_agents if a.race == self.agent.race
}
num_hiv_agents = len(all_hiv_agents & all_race)
target_prep = (len(all_race) - num_hiv_agents) * params.demographics[
self.agent.race
].sex_type[self.agent.sex_type].prep.cap
else:
num_prep_agents = sum(self.counts.values())
target_prep = int(
(model.pop.all_agents.num_members() - len(exposures.HIV.agents))
* params.prep.cap
)
if num_prep_agents < target_prep:
self.enroll(model.run_random, model.time)
progress(self, model, force=False)
Update agent's PrEP status and discontinue stochastically or if force
is True
Parameters:
Name | Type | Description | Default |
---|---|---|---|
model |
model.TITAN |
instance of the TITAN being run |
required |
force |
bool |
whether to force discontinuation of PrEP |
False |
Source code in titan/features/prep.py
def progress(self, model: "model.TITAN", force: bool = False):
"""
Update agent's PrEP status and discontinue stochastically or if `force` is True
args:
model: instance of the TITAN being run
force: whether to force discontinuation of PrEP
"""
if force:
self.discontinue() # TO_REVIEW should this just remove the agent from counts, or discontinue? does it depend on type?
return
if self.type == "Oral":
if (
model.run_random.random()
< self.agent.location.params.demographics[self.agent.race]
.sex_type[self.agent.sex_type]
.prep.discontinue
):
self.discontinue()
else:
self.last_dose_time = model.time
# TO_REVIEW should inj prep have a way to continue at the year mark (besides maybe getting prep again through the normal channels of enrollment)?
if (
self.type == "Inj"
and self.last_dose_time
+ self.agent.location.params.model.time.steps_per_year
== model.time
):
self.discontinue()
remove_agent(agent)
classmethod
Remove an agent from the class (not instance).
Decrement the prep counts by race.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
agent |
|
the agent to remove from the class attributes |
required |
Source code in titan/features/prep.py
@classmethod
def remove_agent(cls, agent):
"""
Remove an agent from the class (not instance).
Decrement the prep counts by race.
args:
agent: the agent to remove from the class attributes
"""
cls.counts[agent.race] -= 1
set_stats(self, stats, time)
Update the stats
dictionary passed for this agent. Called from output.get_stats
for each enabled feature in the model.
The stats to be updated must be declared in the class attribute stats
to make sure the dictionary has the expected keys/counter value initialized.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
stats |
Dict[str, int] |
the dictionary to update with this agent's feature statistics |
required |
time |
int |
the time step of the model when the stats are set |
required |
Source code in titan/features/prep.py
def set_stats(self, stats: Dict[str, int], time: int):
if self.active:
stats["prep"] += 1
if self.time == time:
stats["prep_new"] += 1
if self.type == "Inj":
stats["prep_injectable"] += 1
elif self.type == "Oral":
stats["prep_oral"] += 1
update_agent(self, model)
Update the agent for this feature for a time step. Called once per time step in TITAN.update_all_agents
. Agent level updates are done after population level updates. Called on only features that are enabled per the params.
If the agent is not hiv and time is at least the prep start time, if the agent is already on PrEP update their PrEP attributes, if the agent isn't on PrEP and is eleigible, initiate PrEP.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
model |
model.TITAN |
the instance of TITAN currently being run |
required |
Source code in titan/features/prep.py
def update_agent(self, model: "model.TITAN"):
"""
Update the agent for this feature for a time step. Called once per time step in `TITAN.update_all_agents`. Agent level updates are done after population level updates. Called on only features that are enabled per the params.
If the agent is not hiv and time is at least the prep start time, if the agent is already on PrEP update their PrEP attributes, if the agent isn't on PrEP and is eleigible, initiate PrEP.
args:
model: the instance of TITAN currently being run
"""
if (
not self.agent.hiv.active # type: ignore[attr-defined]
and model.time >= self.agent.location.params.prep.start_time
):
if self.active:
self.progress(model)
elif self.eligible(model.time):
self.initiate(model)