Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partner services #248

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion tests/params/basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ features:
timeline_scaling: true

model:
num_pop: 100
num_pop: 1000
network:
enable: true
num_reps: 1
Expand Down
53 changes: 34 additions & 19 deletions titan/exposures/hiv.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class HIV(base_exposure.BaseExposure):
name: str = "hiv"
stats: List[str] = ["hiv", "hiv_dx", "hiv_aids", "hiv_new", "hiv_dx_new"]
stats: List[str] = ["hiv", "hiv_dx", "hiv_aids", "hiv_new", "hiv_dx_new", "hiv_tested_negative_new"]
"""
HIV collects the following stats:

Expand All @@ -34,7 +34,9 @@ def __init__(self, agent: "agent.Agent"):
self.dx = False
self.dx_time: Optional[int] = None
self.aids = False

self.tested_negative = False #has this agent tested negative for hiv?
self.tested_negative_time = None #time agent tested negative

@classmethod
def init_class(cls, params):
"""
Expand Down Expand Up @@ -67,7 +69,7 @@ def init_agent(self, pop: "population.Population", time: int):

# HIV
if (
pop.pop_random.random() < agent_params.hiv.init
pop.pop_random.random() < agent_params.hiv.init #0.13 for white, 0.175 for latino and 0.434 for blacks
and time >= pop.params.hiv.start_time
):
self.active = True
Expand All @@ -82,7 +84,7 @@ def init_agent(self, pop: "population.Population", time: int):
if pop.pop_random.random() < agent_params.hiv.aids.init:
self.aids = True

if pop.pop_random.random() < agent_params.hiv.dx.init:
if pop.pop_random.random() < agent_params.hiv.dx.init: #0.6 - 0.8 depending on race
self.dx = True
# agent was diagnosed at a random time between conversion and now
self.dx_time = utils.safe_random_int(self.time, time, pop.pop_random)
Expand All @@ -99,23 +101,34 @@ def update_agent(self, model: "model.TITAN"):
args:
model: the instance of TITAN currently being run
"""

if self.active and model.time >= model.params.hiv.start_time:
if not self.dx:
test_prob = (
self.agent.location.params.demographics[self.agent.race]
.sex_type[self.agent.sex_type]
.drug_type[self.agent.drug_type]
.hiv.dx.prob
)

# Rescale based on calibration param
test_prob *= model.calibration.test_frequency

if model.run_random.random() < test_prob:
self.diagnose(model)

self.progress_to_aids(model)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Why was this moved to after progress_to_aids?

test_prob = (
self.agent.location.params.demographics[self.agent.race]
.sex_type[self.agent.sex_type]
.drug_type[self.agent.drug_type]
.hiv.dx.prob
)

# Rescale based on calibration param
test_prob *= model.calibration.test_frequency #freq is 1 so test_prob remains at 0.025

if (
model.run_random.random() < test_prob
and not self.dx # has not yet been diagnosed
):
if self.active and model.time >= model.params.hiv.start_time:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

hiv.start_time should be determined above. Nothing should happen with HIV before the start time.

self.diagnose(model)
else:
self.tested_negative = True
self.tested_negative_time = model.time

if self.tested_negative and self.tested_negative_time < model.time:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What does this do? Comment!

self.tested_negative = False
self.tested_negative_time = None

@classmethod
def add_agent(cls, agent: "agent.Agent"):
"""
Expand Down Expand Up @@ -157,7 +170,9 @@ def set_stats(self, stats: Dict[str, int], time: int):
stats["hiv_dx"] += 1
if self.dx_time == time:
stats["hiv_dx_new"] += 1

if self.tested_negative and self.tested_negative_time == time:
stats["hiv_tested_negative_new"] += 1

@staticmethod
def expose(
model: "model.TITAN",
Expand Down
55 changes: 47 additions & 8 deletions titan/features/haart.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class HAART(base_feature.BaseFeature):
"""

name = "haart"
stats = ["haart"]
stats = ["haart", "ps_haart_count"]
"""
HAART collects the following stats:

Expand All @@ -28,7 +28,7 @@ def __init__(self, agent: "agent.Agent"):
self.active = False
self.ever = False
self.adherent = False

@classmethod
def init_class(cls, params: "ObjMap"):
"""
Expand All @@ -41,6 +41,8 @@ def init_class(cls, params: "ObjMap"):
race: {sex_type: 0 for sex_type in params.classes.sex_types}
for race in params.classes.races
}

cls.ps_haart_count = 0

def init_agent(self, pop: "population.Population", time: int):
"""
Expand Down Expand Up @@ -132,10 +134,25 @@ def remove_agent(cls, agent: "agent.Agent"):
"""
cls.counts[agent.race][agent.sex_type] -= 1

@classmethod
def ps_add_stat(cls):
cls.ps_haart_count += 1

@classmethod
def ps_remove_stat(cls): # do we subtract agents who stop using haart?
pass
#cls.ps_haart_count -= 1

@classmethod
def ps_get_stat(cls):
return cls.ps_haart_count

def set_stats(self, stats: Dict[str, int], time: int):
if self.active:
stats["haart"] += 1


stats["ps_haart_count"] = self.ps_get_stat()

def get_transmission_risk_multiplier(self, time: int, interaction_type: str):
"""
Get a multiplier for how haart reduces hiv transmission risk based on interaction type and params.
Expand Down Expand Up @@ -182,6 +199,7 @@ def enroll(self, model: "model.TITAN", haart_params: ObjMap):
model: the instance of TITAN currently being run
haart_params: the HAART demographic params for this agent
"""

if self.agent.location.params.haart.use_cap:
self.enroll_cap(model, haart_params)
else:
Expand All @@ -201,11 +219,22 @@ def enroll_cap(self, model: "model.TITAN", haart_params: ObjMap):
# HAART agents based on % of diagnosed agents
num_dx_agents = self.agent.hiv.dx_counts[race][sex_type] # type: ignore[attr-defined]
num_haart_agents = self.counts[race][sex_type]

# take value from dictionary for cap
if num_haart_agents < (haart_params.cap * num_dx_agents):
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Why was this deleted?

self.initiate(model.run_random, haart_params, "prob")


# if agent is diagnosed through PS, use PS parameter
if self.agent.partner_tracing.ps_dx:
cap = self.agent.partner_tracing.get_positive_count() * self.agent.location.params.partner_tracing.treatment_prob
# initiate agent only if required capacity is not yet met
if self.ps_get_stat() < cap:
self.initiate(model.run_random, haart_params, "prob")
self.ps_add_stat()

# else, use base parameter
else:
cap = haart_params.cap
# initiate agent only if required capacity is not yet met
if num_haart_agents < (cap * num_dx_agents):
self.initiate(model.run_random, haart_params, "prob")

def enroll_prob(self, model: "model.TITAN", haart_params: ObjMap):
"""
Determine whether to enroll an agent in HAART using probability method.
Expand All @@ -214,9 +243,19 @@ def enroll_prob(self, model: "model.TITAN", haart_params: ObjMap):
model: the instance of TITAN currently being run
haart_params: the HAART demographic params for this agent
"""
# if agent has ever been on haart and use_reinit is true, use reinit probability
if self.ever and self.agent.location.params.haart.use_reinit:
if model.run_random.random() < haart_params.reinit.prob:
self.initiate(model.run_random, haart_params, "prob")

# if agent has been diagnosed through PS, use PS treatment (haart) probability
elif self.agent.partner_tracing.ps_dx:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just for safety, I would also check that partner tracing is enabled

enroll_prob = self.agent.location.params.partner_tracing.treatment_prob
if model.run_random.random() < enroll_prob:
self.initiate(model.run_random, haart_params, "prob")
self.ps_add_stat()

# else, use normal base probability scaled by time since diagnoses
else:
# Find enroll probability based on time since diagnosis
enroll_prob = 0.0
Expand Down
Loading
Loading