From 9cdf1cacb82ce7f83a974564a2d59958a3a9cba7 Mon Sep 17 00:00:00 2001 From: sbessey Date: Fri, 18 Dec 2020 17:42:15 -0500 Subject: [PATCH 01/49] feat(haart): add ability to change haart probability given time since dx Real-world, people are more likely to start treatment soon after diagnosis. This change allows that to be reflected for agents in the model. BREAKING CHANGE: Need to provide haart initiation based on time --- settings/atlanta/demographics.yml | 4 ++-- tests/params/simple_integration.yml | 12 ++++++++++-- titan/features/haart.py | 13 +++++++++++-- titan/params/demographics.yml | 29 ++++++++++++++++++++++++----- 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/settings/atlanta/demographics.yml b/settings/atlanta/demographics.yml index 9bc31445..5667f1f3 100644 --- a/settings/atlanta/demographics.yml +++ b/settings/atlanta/demographics.yml @@ -36,7 +36,7 @@ demographics: init: 0.232 haart: &black_haart init: 0.625 - prob: 0.35 + cap: 0.35 adherence: init: 0.885 prob: 0.885 @@ -78,7 +78,7 @@ demographics: aids: 34.4 haart: &white_haart init: 0.585 - prob: 0.20 + cap: 0.20 adherence: init: 0.817 prob: 0.817 diff --git a/tests/params/simple_integration.yml b/tests/params/simple_integration.yml index 81a550a8..709c7707 100644 --- a/tests/params/simple_integration.yml +++ b/tests/params/simple_integration.yml @@ -48,7 +48,11 @@ demographics: init: 0.1 haart: init: 0.1 - prob: 0.1 + prob: + 1: + enroll_prob: 0.1 + start: 0 + stop: 999 adherence: init: 0.1 prob: 0.1 @@ -89,7 +93,11 @@ demographics: init: 0.1 haart: init: 0.1 - prob: 0.1 + prob: + 1: + enroll_prob: 0.1 + start: 0 + stop: 999 adherence: init: 0.1 prob: 0.1 diff --git a/titan/features/haart.py b/titan/features/haart.py index 5175b24e..690f0c7f 100644 --- a/titan/features/haart.py +++ b/titan/features/haart.py @@ -94,18 +94,27 @@ def update_agent(self, model: "model.TITAN"): if self.agent.location.params.hiv.haart_cap: # if HAART is based on cap instead of prob, determine number of # HAART agents based on % of diagnosed agents + assert len(haart_params) == 1 # should only have one val for cap TODO check earlier num_dx_agents = self.agent.hiv.dx_counts[self.agent.race][ # type: ignore[attr-defined] self.agent.sex_type ] num_haart_agents = self.counts[self.agent.race][self.agent.sex_type] - if num_haart_agents < (haart_params.prob * num_dx_agents): + # take value from dictionary for cap + if num_haart_agents < (haart_params.cap * num_dx_agents): self.initiate(model) else: + # Find enroll probability based on time since diagnosis + for i in haart_params.prob.values(): + if i.start <= (model.time - self.agent.hiv.dx_time) < i.stop: + enroll_prob = i.enroll_prob + break + if model.run_random.random() < ( - haart_params.prob * model.calibration.haart.coverage + enroll_prob * model.calibration.haart.coverage ): self.initiate(model) + # Go off HAART elif self.active and model.run_random.random() < haart_params.discontinue: self.active = False diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index ba680e82..1c6e8677 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -98,7 +98,7 @@ demographics: max: 1.0 duration: prob: - type: bins + type: bin description: Binned probabilities of incarceration duration during model run fields: prob: @@ -137,7 +137,7 @@ demographics: min: 130 max: 260 init: - type: bins + type: bin description: Binned probabilities of incarceration duration when initializing population fields: prob: @@ -297,12 +297,31 @@ demographics: type: float min: 0.0 max: 1.0 - prob: - default: 0.0 - description: "Probability that an agent in this class with HIV becomes enrolled in HAART at a given time step" + cap: type: float + default: 0.0 + description: "Percent of agents on haart if haart cap is used" min: 0.0 max: 1.0 + prob: + type: bin + description: "Probability that an agent in this class with HIV becomes enrolled in HAART at a given time step based on time since diagnosis (use 0 for haart cap)" + fields: + enroll_prob: + type: float + min: 0.0 + max: 1.0 + start: + type: int + min: 0 + stop: + type: int + min: 1 + default: + 0: + start: 0 + stop: 999 + enroll_prob: 0.0 adherence: init: default: 0.0 From 8dd31ceaa8e0bb92fc487e581cbd1d6a0d1faf29 Mon Sep 17 00:00:00 2001 From: sbessey Date: Mon, 4 Jan 2021 11:23:09 -0500 Subject: [PATCH 02/49] feat(haart): make haart initiation dependent on time since dx For probability-based (not cap-based) haart, allows flexibility to vary probabilities based on time since the agent was diagnosed. This reflects real-world, where people are more likely to initiate treatment soon after diagnosis. BREAKING CHANGE: Divide haart probability into bins based on time since dx. Use single bin accounting for all times (e.g. start: 0, stop: 9999) if dx-duration basis is unneeded. No change necessary for haart cap. --- settings/mississippi/demographics.yml | 14 ++++++- settings/nyc-msm/demographics.yml | 54 +++++++++++++++++++++++++-- settings/scott/demographics.yml | 16 ++++---- tests/features/haart_test.py | 20 +++++++++- tests/params/basic.yml | 12 +++++- tests/params/basic_seeded.yml | 12 +++++- tests/params/basic_seeded_error.yml | 12 +++++- titan/features/haart.py | 3 +- 8 files changed, 121 insertions(+), 22 deletions(-) diff --git a/settings/mississippi/demographics.yml b/settings/mississippi/demographics.yml index fd0989db..690f4496 100644 --- a/settings/mississippi/demographics.yml +++ b/settings/mississippi/demographics.yml @@ -25,7 +25,19 @@ demographics: init: 0.46 haart: init: 0.690 - prob: 0.057 + prob: + 0: + enroll_prob: 0.609 + start: 0 + stop: 1 + 1: + enroll_prob: 0.074 + start: 1 + stop: 12 + 2: + enroll_prob: 0.0 + start: 12 + stop: 9999 adherence: init: 0.500 prob: 0.500 diff --git a/settings/nyc-msm/demographics.yml b/settings/nyc-msm/demographics.yml index 99096618..5841f726 100644 --- a/settings/nyc-msm/demographics.yml +++ b/settings/nyc-msm/demographics.yml @@ -27,7 +27,23 @@ demographics: init: 0.500 haart: init: 0.764 - prob: 0.212 + prob: + 0: + start: 0 + stop: 1 + enroll_prob: 0.709 + 1: + start: 1 + stop: 3 + enroll_prob: 0.212 + 2: + start: 3 + stop: 12 + enroll_prob: 0.051 + 3: + start: 12 + stop: 9999 + enroll_prob: 0.0 adherence: init: 0.489 prob: 0.489 @@ -81,7 +97,23 @@ demographics: init: 0.487 haart: init: 0.764 - prob: 0.193 + prob: + 0: + enroll_prob: 0.278 + start: 0 + stop: 1 + 1: + enroll_prob: 0.193 + start: 1 + stop: 3 + 2: + enroll_prob: 0.037 + start: 3 + stop: 12 + 3: + start: 12 + stop: 9999 + enroll_prob: 0.0 adherence: init: 0.719 prob: 0.719 @@ -135,7 +167,23 @@ demographics: init: 0.471 haart: init: 0.791 - prob: 0.163 + prob: + 0: + start: 0 + stop: 1 + enroll_prob: 0.742 + 1: + start: 1 + stop: 3 + enroll_prob: 0.163 + 2: + start: 3 + stop: 12 + enroll_prob: 0.029 + 3: + start: 12 + stop: 9999 + enroll_prob: 0.0 adherence: init: 0.707 prob: 0.707 diff --git a/settings/scott/demographics.yml b/settings/scott/demographics.yml index ce8b837c..b25754aa 100644 --- a/settings/scott/demographics.yml +++ b/settings/scott/demographics.yml @@ -36,9 +36,13 @@ demographics: dx: prob: 0.202 init: 1.0 - haart: + haart: &haart_vals init: 1.0 - prob: 0.679 + prob: + 0: + enroll_prob: 0.679 + start: 0 + stop: 999 adherence: init: 1.0 prob: 1.0 @@ -82,13 +86,7 @@ demographics: hiv: 3.1 aids: 10.4 haart_adherent: 1.0 - haart: - init: 1.0 - prob: 0.679 - adherence: - init: 1.0 - prob: 1.0 - discontinue: 0.000 + haart: *haart_vals num_partners: Sex: dist_type: poisson diff --git a/tests/features/haart_test.py b/tests/features/haart_test.py index fd535a8e..bc4e8dee 100644 --- a/tests/features/haart_test.py +++ b/tests/features/haart_test.py @@ -18,6 +18,7 @@ def test_update_haart_t1(make_model, make_agent): # t0 agent initialized HAART a.hiv.dx = True + a.hiv.dx_time = model.time # go on haart model.run_random = FakeRandom( @@ -48,7 +49,24 @@ def test_update_haart_t1(make_model, make_agent): # Increase cap. Agent goes on haart a.location.params.demographics[a.race].sex_type[a.sex_type].drug_type[ a.drug_type - ].haart.prob = 2.0 + ].haart.cap = 2.0 a.haart.update_agent(model) assert a.haart.active assert a.haart.adherent is True + + +def test_update_haart_dx_time(make_model, make_agent): + model = make_model() + model.time = 1 + a = make_agent(race="white") + model.run_random = FakeRandom(0.5) + + a.hiv.active = True + a.hiv.dx = True + a.hiv.dx_time = 1 # agent dx duration is 0 + a.haart.update_agent(model) + assert not a.haart.active + + a.hiv.dx_time -= 10 + a.haart.update_agent(model) + assert a.haart.active diff --git a/tests/params/basic.yml b/tests/params/basic.yml index dfda011e..f7338e99 100644 --- a/tests/params/basic.yml +++ b/tests/params/basic.yml @@ -41,7 +41,15 @@ demographics: init: 0.1 haart: init: 0.1 - prob: 0.1 + prob: &haart_prob + 0: + enroll_prob: 0.1 + start: 0 + stop: 10 + 1: + enroll_prob: 1.0 + start: 10 + stop: 100 adherence: init: 0.1 prob: 0.1 @@ -64,7 +72,7 @@ demographics: value_type: float haart: init: 0.1 - prob: 0.1 + prob: *haart_prob adherence: init: 0.1 prob: 0.1 diff --git a/tests/params/basic_seeded.yml b/tests/params/basic_seeded.yml index b5837111..97aedda8 100644 --- a/tests/params/basic_seeded.yml +++ b/tests/params/basic_seeded.yml @@ -41,7 +41,11 @@ demographics: init: 0.1 haart: init: 0.1 - prob: 0.1 + prob: + 0: + enroll_prob: 0.1 + start: 0 + stop: 99 adherence: init: 0.1 prob: 0.1 @@ -64,7 +68,11 @@ demographics: value_type: float haart: init: 0.1 - prob: 0.1 + prob: + 0: + enroll_prob: 0.1 + start: 0 + stop: 99 adherence: init: 0.1 prob: 0.1 diff --git a/tests/params/basic_seeded_error.yml b/tests/params/basic_seeded_error.yml index 83cc3f7e..447b4460 100644 --- a/tests/params/basic_seeded_error.yml +++ b/tests/params/basic_seeded_error.yml @@ -41,7 +41,11 @@ demographics: init: 0.1 haart: init: 0.1 - prob: 0.1 + prob: + 0: + enroll_prob: 0.1 + start: 0 + stop: 99 adherence: init: 0.1 prob: 0.1 @@ -64,7 +68,11 @@ demographics: value_type: float haart: init: 0.1 - prob: 0.1 + prob: + 0: + enroll_prob: 0.1 + start: 0 + stop: 99 adherence: init: 0.1 prob: 0.1 diff --git a/titan/features/haart.py b/titan/features/haart.py index 690f0c7f..34971738 100644 --- a/titan/features/haart.py +++ b/titan/features/haart.py @@ -94,7 +94,6 @@ def update_agent(self, model: "model.TITAN"): if self.agent.location.params.hiv.haart_cap: # if HAART is based on cap instead of prob, determine number of # HAART agents based on % of diagnosed agents - assert len(haart_params) == 1 # should only have one val for cap TODO check earlier num_dx_agents = self.agent.hiv.dx_counts[self.agent.race][ # type: ignore[attr-defined] self.agent.sex_type ] @@ -106,7 +105,7 @@ def update_agent(self, model: "model.TITAN"): else: # Find enroll probability based on time since diagnosis for i in haart_params.prob.values(): - if i.start <= (model.time - self.agent.hiv.dx_time) < i.stop: + if i.start <= (model.time - self.agent.hiv.dx_time) < i.stop: # type: ignore[attr-defined] enroll_prob = i.enroll_prob break From 830f401069dd818938f2b1bc2a3c1dbd78cde30c Mon Sep 17 00:00:00 2001 From: sbessey Date: Mon, 4 Jan 2021 15:38:45 -0500 Subject: [PATCH 03/49] feat(relationship): allow relationship durations to differ by agent race This change allows relationships to have different relanship durations depending on race of agents. Right now, duration of mixed-race partnerships chooses a random agent from the partnership and uses their params BREAKING CHANGE: requires subdict-ing duration off of both race and bond type --- settings/atlanta/partnership.yml | 30 +++++----- settings/chicago/chicago_baseCase.yml | 45 +++++++------- settings/mississippi/model.yml | 23 ++++---- settings/nyc-msm/partnership.yml | 84 +++++++++++++++++++++++---- settings/scott/model.yml | 57 +++++++++--------- tests/params/basic.yml | 64 +++++++++++--------- tests/partnering_test.py | 4 +- titan/params/partnership.yml | 3 +- titan/partnering.py | 47 ++++++++------- titan/population.py | 7 ++- 10 files changed, 224 insertions(+), 140 deletions(-) diff --git a/settings/atlanta/partnership.yml b/settings/atlanta/partnership.yml index 21d1d610..5a479b6d 100644 --- a/settings/atlanta/partnership.yml +++ b/settings/atlanta/partnership.yml @@ -1,20 +1,22 @@ partnership: duration: Sex: - type: bins - bins: - 1: - prob: 0.456 - min: 1 - max: 3 - 2: - prob: 0.756 - min: 3 - max: 12 - 3: - prob: 1.0 - min: 13 - max: 24 + white: &partnership_duration + type: bins + bins: + 1: + prob: 0.456 + min: 1 + max: 3 + 2: + prob: 0.756 + min: 3 + max: 12 + 3: + prob: 1.0 + min: 13 + max: 24 + black: *partnership_duration sex: frequency: Sex: diff --git a/settings/chicago/chicago_baseCase.yml b/settings/chicago/chicago_baseCase.yml index 7acd63b1..db136488 100644 --- a/settings/chicago/chicago_baseCase.yml +++ b/settings/chicago/chicago_baseCase.yml @@ -144,28 +144,29 @@ demographics: partnership: duration: Social: - type: bins - bins: - 1: - prob: 0.585 - min: 1 - max: 6 - 2: - prob: 0.701 - min: 7 - max: 12 - 3: - prob: 0.822 - min: 13 - max: 24 - 4: - prob: 0.8819999999999999 - min: 25 - max: 36 - 5: - prob: 1.0 - min: 37 - max: 48 + black: + type: bins + bins: + 1: + prob: 0.585 + min: 1 + max: 6 + 2: + prob: 0.701 + min: 7 + max: 12 + 3: + prob: 0.822 + min: 13 + max: 24 + 4: + prob: 0.8819999999999999 + min: 25 + max: 36 + 5: + prob: 1.0 + min: 37 + max: 48 sex: acquisition: MSM: diff --git a/settings/mississippi/model.yml b/settings/mississippi/model.yml index e3bb5414..4017c65c 100644 --- a/settings/mississippi/model.yml +++ b/settings/mississippi/model.yml @@ -57,17 +57,18 @@ partnership: versatile: 0.0026625 duration: Sex: - type: distribution - distribution: - dist_type: weibull_modified - vars: - 1: - value: 0.586 - value_type: float - 2: - value: 11.532 - value_type: float - mean: 16 + black: + type: distribution + distribution: + dist_type: weibull_modified + vars: + 1: + value: 0.586 + value_type: float + 2: + value: 11.532 + value_type: float + mean: 16 prep: type: diff --git a/settings/nyc-msm/partnership.yml b/settings/nyc-msm/partnership.yml index d76b0a3b..fb566253 100644 --- a/settings/nyc-msm/partnership.yml +++ b/settings/nyc-msm/partnership.yml @@ -35,15 +35,77 @@ partnership: duration: Main: - type: distribution - distribution: - dist_type: wald - vars: - 1: - value: 52.88 - value_type: float - 2: - value: 5.248 - value_type: float - mean: 52.88 + white: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 70.44 + value_type: float + 2: + value: 6.72 + value_type: float + mean: 70.44 + black: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 20.88 + value_type: float + 2: + value: 2.76 + value_type: float + mean: 20.88 + latino: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 35.52 + value_type: float + 2: + value: 3.84 + value_type: float + mean: 3.84 + Casual: + white: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 11.76 + value_type: float + 2: + value: 1.32 + value_type: float + mean: 11.76 + black: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 17.16 + value_type: float + 2: + value: 1.56 + value_type: float + mean: 17.16 + latino: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 16.32 + value_type: float + 2: + value: 1.20 + value_type: float + mean: 16.32 diff --git a/settings/scott/model.yml b/settings/scott/model.yml index c21faf8d..a3ddf473 100644 --- a/settings/scott/model.yml +++ b/settings/scott/model.yml @@ -180,35 +180,38 @@ partnership: adherent: 0.06 duration: Sex: - type: distribution - distribution: - dist_type: set_value - vars: - 1: - value: 12100 - value_type: int - mean: 12100 + white: + type: distribution + distribution: + dist_type: set_value + vars: + 1: + value: 12100 + value_type: int + mean: 12100 SexInj: - type: distribution - distribution: - dist_type: set_value - vars: - 1: - value: 121 - value_type: int - mean: 121 + white: + type: distribution + distribution: + dist_type: set_value + vars: + 1: + value: 121 + value_type: int + mean: 121 Inj: - type: distribution - distribution: - dist_type: gamma - vars: - 1: - value: 5.37 - value_type: float - 2: - value: 23.28 - value_type: float - mean: 125 + white: + type: distribution + distribution: + dist_type: gamma + vars: + 1: + value: 5.37 + value_type: float + 2: + value: 23.28 + value_type: float + mean: 125 assort_mix: assort_pwid: diff --git a/tests/params/basic.yml b/tests/params/basic.yml index f7338e99..e21ec975 100644 --- a/tests/params/basic.yml +++ b/tests/params/basic.yml @@ -225,35 +225,43 @@ partnership: receptive: 0.0138 versatile: 0.00745 duration: - Sex: - type: bins - bins: - 1: - prob: 1.0 - min: 1 - max: 2 - Inj: - type: distribution - distribution: - dist_type: randint - vars: - 1: - value: 1 - value_type: int - 2: - value: 3 - value_type: int - mean: 1 - SexInj: &poisson_one - type: distribution - distribution: - dist_type: poisson - vars: + Sex: + white: &sex_dur + type: bins + bins: 1: - value: 1 - value_type: float - mean: 1 - Social: *poisson_one + prob: 1.0 + min: 1 + max: 2 + black: *sex_dur + Inj: + white: &inj_dur + type: distribution + distribution: + dist_type: randint + vars: + 1: + value: 1 + value_type: int + 2: + value: 3 + value_type: int + mean: 1 + black: *inj_dur + SexInj: + white: &poisson_one + type: distribution + distribution: + dist_type: poisson + vars: + 1: + value: 1 + value_type: float + mean: 1 + black: *poisson_one + Social: + white: *poisson_one + black: *poisson_one prep: target: 0.1 diff --git a/tests/partnering_test.py b/tests/partnering_test.py index 74951f6c..d60500ec 100644 --- a/tests/partnering_test.py +++ b/tests/partnering_test.py @@ -11,9 +11,9 @@ def test_partnership_duration(params): # test duration with randint - assert get_partnership_duration(params, FakeRandom(1.0), "Inj") == 1 + assert get_partnership_duration(params, FakeRandom(1.0), "Inj", "white") == 1 # test duration with bins - assert get_partnership_duration(params, FakeRandom(0.1), "Sex") == 1 + assert get_partnership_duration(params, FakeRandom(0.1), "Sex", "white") == 1 @pytest.mark.unit diff --git a/titan/params/partnership.yml b/titan/params/partnership.yml index b229ca35..53b8d722 100644 --- a/titan/params/partnership.yml +++ b/titan/params/partnership.yml @@ -179,7 +179,8 @@ partnership: type: sub-dict keys: - bond_types - description: "Duration of relationships given bond type" + - races + description: "Duration of relationships given agent race and partnership bond type" default: type: type: enum diff --git a/titan/partnering.py b/titan/partnering.py index df0436e1..24cd90b3 100644 --- a/titan/partnering.py +++ b/titan/partnering.py @@ -124,30 +124,34 @@ def get_mean_rel_duration(params: "parse_params.ObjMap"): args: params: The current model's parameters """ - mean_rel_duration: Dict[str, int] = {} + mean_rel_duration: Dict[str, Dict] = {} for bond in params.partnership.duration: - if params.partnership.duration[bond].type == "bins": - weights = [] - vals = [] - dur_bins = params.partnership.duration[bond].bins - for bins in dur_bins: - if bins > 1: - weights.append(dur_bins[bins].prob - dur_bins[bins - 1].prob) - else: - weights.append(dur_bins[bins].prob) - vals.append(np.average([dur_bins[bins].min, dur_bins[bins].max])) - mean_rel_duration[bond] = np.average(vals, weights=weights) - else: - mean_rel_duration[bond] = params.partnership.duration[ - bond - ].distribution.mean - assert mean_rel_duration[bond] > 0, "All bonds must have a positive duration!" + mean_rel_duration[bond] = {} + for race in params.classes.races: + if params.partnership.duration[bond][race].type == "bins": + weights = [] + vals = [] + dur_bins = params.partnership.duration[bond][race].bins + for bins in dur_bins: + if bins > 1: + weights.append(dur_bins[bins].prob - dur_bins[bins - 1].prob) + else: + weights.append(dur_bins[bins].prob) + vals.append(np.average([dur_bins[bins].min, dur_bins[bins].max])) + mean_rel_duration[bond][race] = np.average(vals, weights=weights) + else: + mean_rel_duration[bond][race] = params.partnership.duration[bond][ + race + ].distribution.mean + assert ( + mean_rel_duration[bond][race] > 0 + ), "All bonds must have a positive duration!" return mean_rel_duration def get_partnership_duration( - params: "parse_params.ObjMap", rand_gen, bond_type: str + params: "parse_params.ObjMap", rand_gen, bond_type: str, race: Optional[str] ) -> int: """ Get duration of a relationship drawn from bins or a distribution per the params [params.partnership.duration] @@ -161,10 +165,11 @@ def get_partnership_duration( number of time steps the partnership should endure """ - if params.partnership.duration[bond_type].type == "bins": - dur_info = params.partnership.duration[bond_type].bins + if params.partnership.duration[bond_type][race].type == "bins": + dur_info = params.partnership.duration[bond_type][race].bins diceroll = rand_gen.random() + print(dur_info) dur_bin = dur_info[5] for i in range(1, 5): if diceroll < dur_info[i].prob: @@ -174,7 +179,7 @@ def get_partnership_duration( duration = rand_gen.randint(dur_bin.min, dur_bin.max) else: - dist = params.partnership.duration[bond_type].distribution + dist = params.partnership.duration[bond_type][race].distribution duration = utils.safe_dist(dist, rand_gen) return duration diff --git a/titan/population.py b/titan/population.py index b9175b6d..64ddaba0 100644 --- a/titan/population.py +++ b/titan/population.py @@ -91,7 +91,7 @@ def __init__(self, params: "parse_params.ObjMap", id: Optional[str] = None): self.relationships: Set["ag.Relationship"] = set() # find average partnership durations - self.mean_rel_duration: Dict[str, int] = partnering.get_mean_rel_duration( + self.mean_rel_duration: Dict[str, Dict] = partnering.get_mean_rel_duration( self.params ) @@ -191,7 +191,7 @@ def create_agent( utils.safe_dist(dist_info, self.np_random) * utils.safe_divide( agent.location.params.calibration.sex.partner, - self.mean_rel_duration[bond], + self.mean_rel_duration[bond][race], ) ) # so not zero if added mid-year @@ -353,8 +353,9 @@ def update_agent_partners( no_match = True if partner: + race = utils.safe_random_choice([agent.race, partner.race], self.pop_random) duration = partnering.get_partnership_duration( - agent.location.params, self.np_random, bond_type + agent.location.params, self.np_random, bond_type, race ) relationship = ag.Relationship( agent, partner, duration, bond_type=bond_type From 58e412490788588c030fe2ec5c908dec22db63b0 Mon Sep 17 00:00:00 2001 From: sbessey Date: Tue, 5 Jan 2021 11:41:31 -0500 Subject: [PATCH 04/49] feat(sex): allow flexible condom use based on bond type Bond type influences condom use in real-world relationships. For example, casual relationships may have more consistent condom use than committed, long-term relationships. BREAKING CHANGE: must sub-dict safe sex off of bond type --- settings/atlanta/demographics.yml | 8 ++++++-- settings/chicago/chicago_baseCase.yml | 4 ---- settings/mississippi/demographics.yml | 4 +++- settings/nyc-msm/demographics.yml | 18 ++++++++++++++--- settings/scott/demographics.yml | 16 +++++++++++++-- tests/integration_test.py | 19 +++++++++++++++--- tests/params/basic.yml | 27 +++++++++++++++----------- tests/params/basic_seeded.yml | 28 +++++++++++++++++---------- tests/params/basic_seeded_error.yml | 28 +++++++++++++++++---------- tests/params/simple_integration.yml | 13 ++++++++++--- titan/interactions/sex.py | 3 ++- titan/params/demographics.yml | 15 +++++++++----- titan/params/syringe_services.yml | 2 -- titan/partnering.py | 1 - 14 files changed, 128 insertions(+), 58 deletions(-) diff --git a/settings/atlanta/demographics.yml b/settings/atlanta/demographics.yml index 5667f1f3..76b88d57 100644 --- a/settings/atlanta/demographics.yml +++ b/settings/atlanta/demographics.yml @@ -11,7 +11,9 @@ demographics: versatile: 0.437 insertive: 0.242 receptive: 0.321 - safe_sex: 0.688 + safe_sex: + Sex: + prob: 0.688 prep: discontinue: 0.0 adherence: 0.568 @@ -56,7 +58,9 @@ demographics: versatile: 0.544 insertive: 0.228 receptive: 0.228 - safe_sex: 0.528 + safe_sex: + Sex: + prob: 0.528 prep: adherence: 0.911 target: 0.415 diff --git a/settings/chicago/chicago_baseCase.yml b/settings/chicago/chicago_baseCase.yml index db136488..c9a9b71e 100644 --- a/settings/chicago/chicago_baseCase.yml +++ b/settings/chicago/chicago_baseCase.yml @@ -102,10 +102,6 @@ demographics: incar: init: 0.0 prob: 0.0 - safe_sex: 0.312 - injection: - unsafe_prob: 0.27 - num_acts: 5.0 prep: discontinue: 0.0 adherence: 0.568 diff --git a/settings/mississippi/demographics.yml b/settings/mississippi/demographics.yml index 690f4496..80b56ce5 100644 --- a/settings/mississippi/demographics.yml +++ b/settings/mississippi/demographics.yml @@ -9,7 +9,9 @@ demographics: versatile: 0.671 insertive: 0.169 receptive: 0.160 - safe_sex: 0.214 + safe_sex: + Sex: + prob: 0.214 prep: discontinue: 0.034 adherence: 0.860 diff --git a/settings/nyc-msm/demographics.yml b/settings/nyc-msm/demographics.yml index 5841f726..55f60ff0 100644 --- a/settings/nyc-msm/demographics.yml +++ b/settings/nyc-msm/demographics.yml @@ -9,7 +9,11 @@ demographics: versatile: 0.200 insertive: 0.480 receptive: 0.320 - safe_sex: 0.275 + safe_sex: + Main: + prob: 0.275 + Casual: + prob: 0.409 prep: discontinue: 0.034 # calibration adherence: 0.565 @@ -79,7 +83,11 @@ demographics: insertive: 0.220 receptive: 0.195 versatile: 0.585 - safe_sex: 0.262 + safe_sex: + Main: + prob: 0.183 + Casual: + prob: 0.341 prep: discontinue: 0.025 # calibration adherence: 0.719 @@ -144,7 +152,11 @@ demographics: sex_type: MSM: ppl: 1.0 - safe_sex: 0.126 + safe_sex: + Main: + prob: 0.126 + Casual: + prob: 0.277 sex_role: init: insertive: 0.439 diff --git a/settings/scott/demographics.yml b/settings/scott/demographics.yml index b25754aa..205f85fa 100644 --- a/settings/scott/demographics.yml +++ b/settings/scott/demographics.yml @@ -24,7 +24,13 @@ demographics: sex_role: init: insertive: 1.0 - safe_sex: 0.1 + safe_sex: + Sex: + prob: 0.1 + Inj: + prob: 0.1 + SexInj: + prob: 0.1 injection: unsafe_prob: 0.321 num_acts: 5 @@ -99,7 +105,13 @@ demographics: sex_role: init: receptive: 1.0 - safe_sex: 0.2 + safe_sex: + Sex: + prob: 0.2 + Inj: + prob: 0.2 + SexInj: + prob: 0.2 injection: unsafe_prob: 0.321 num_acts: 5 diff --git a/tests/integration_test.py b/tests/integration_test.py index 5f9431a4..5bd6fe2f 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -232,6 +232,7 @@ def test_prep_coverage(make_model_integration, tmpdir): t10_hiv_b = res_b["10"]["hiv"] t10_diff = t10_hiv_a - t10_hiv_b # a should be higher assert res_a["10"]["prep"] < res_b["10"]["prep"] + assert t10_hiv_a > t0_hiv_a assert t10_diff > t0_diff @@ -243,10 +244,11 @@ def test_syringe_services(params_integration, tmpdir): params_integration.demographics.black.sex_type.MSM.drug_type.Inj.ppl = 1.0 params_integration.demographics.black.sex_type.MSM.drug_type["None"].ppl = 0.0 model_a = TITAN(params_integration) - model_a.params.partnership.sex.frequency.Sex = ObjMap( - {"type": "bins", "bins": {1: {"prob": 1.0, "min": 0, "max": 1}}} + model_a.params.partnership.sex.frequency.Sex = ( + ObjMap( # turn off sex to show only injection effects + {"type": "bins", "bins": {1: {"prob": 1.0, "min": 0, "max": 1}}} + ) ) - path_a = tmpdir.mkdir("a") path_a.mkdir("network") path_b = tmpdir.mkdir("b") @@ -254,6 +256,15 @@ def test_syringe_services(params_integration, tmpdir): # run with default bins (0-9) model_a.run(path_a) + num_bonds = 0 + num_ag = 0 + for ag in model_a.pop.all_agents: + if ag.hiv.active: + num_ag += 1 + for ptnr in ag.get_partners(["Inj", "SexInj"]): + if not ptnr.hiv.active: + num_bonds += 1 + assert num_bonds # make sure there are serodiscordant partnerships # change the coverage upward for creating model b, use same seeds model_a.params.features.syringe_services = True @@ -290,6 +301,8 @@ def test_syringe_services(params_integration, tmpdir): t10_hiv_a = res_a["10"] t10_hiv_b = res_b["10"] t10_diff = t10_hiv_a - t10_hiv_b # a should be higher + assert t10_hiv_a > t0_hiv_a + assert t10_hiv_b > t0_hiv_b assert t10_diff > t0_diff diff --git a/tests/params/basic.yml b/tests/params/basic.yml index e21ec975..f3b6330b 100644 --- a/tests/params/basic.yml +++ b/tests/params/basic.yml @@ -81,7 +81,13 @@ demographics: incar: &base_incar init: 0.02 prob: 0.02 - safe_sex: 0.1 + safe_sex: &safe_sex_default + Sex: + prob: 0.1 + Inj: + prob: 0.1 + SexInj: + prob: 0.1 injection: &base_injection unsafe_prob: 0.1 num_acts: 10 @@ -102,7 +108,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: adherence: 0.1 @@ -115,7 +121,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -124,7 +130,7 @@ demographics: ppl: 0.2 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -133,7 +139,7 @@ demographics: ppl: 0.1 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -145,7 +151,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -154,7 +160,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -163,7 +169,7 @@ demographics: ppl: 0.2 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -172,7 +178,7 @@ demographics: ppl: 0.1 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: unsafe_prob: 0. num_acts: 10 @@ -183,7 +189,7 @@ demographics: ppl: 0.1 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: unsafe_prob: 0. num_acts: 10 @@ -233,7 +239,6 @@ partnership: prob: 1.0 min: 1 max: 2 - black: *sex_dur Inj: white: &inj_dur type: distribution diff --git a/tests/params/basic_seeded.yml b/tests/params/basic_seeded.yml index 97aedda8..62e04d91 100644 --- a/tests/params/basic_seeded.yml +++ b/tests/params/basic_seeded.yml @@ -81,7 +81,15 @@ demographics: incar: &base_incar init: 0.02 prob: 0.02 - safe_sex: 0.1 + safe_sex: &safe_sex_default + Sex: + prob: 0.1 + Inj: + prob: 0.1 + SexInj: + prob: 0.1 + Social: + prob: 0.1 injection: &base_injection unsafe_prob: 0.1 num_acts: 10 @@ -102,7 +110,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: adherence: 0.1 @@ -115,7 +123,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -124,7 +132,7 @@ demographics: ppl: 0.2 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -133,7 +141,7 @@ demographics: ppl: 0.1 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -145,7 +153,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -154,7 +162,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -163,7 +171,7 @@ demographics: ppl: 0.2 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -172,7 +180,7 @@ demographics: ppl: 0.1 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: unsafe_prob: 0. num_acts: 10 @@ -183,7 +191,7 @@ demographics: ppl: 0.1 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: unsafe_prob: 0. num_acts: 10 diff --git a/tests/params/basic_seeded_error.yml b/tests/params/basic_seeded_error.yml index 447b4460..cc4fcb88 100644 --- a/tests/params/basic_seeded_error.yml +++ b/tests/params/basic_seeded_error.yml @@ -81,7 +81,15 @@ demographics: incar: &base_incar init: 0.02 prob: 0.02 - safe_sex: 0.1 + safe_sex: &safe_sex_default + Sex: + prob: 0.1 + Inj: + prob: 0.1 + SexInj: + prob: 0.1 + Social: + prob: 0.1 injection: &base_injection unsafe_prob: 0.1 num_acts: 10 @@ -102,7 +110,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: adherence: 0.1 @@ -115,7 +123,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -124,7 +132,7 @@ demographics: ppl: 0.2 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -133,7 +141,7 @@ demographics: ppl: 0.1 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -145,7 +153,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -154,7 +162,7 @@ demographics: ppl: 0.3 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -163,7 +171,7 @@ demographics: ppl: 0.2 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: *base_injection prep: *base_prep high_risk: *base_high_risk @@ -172,7 +180,7 @@ demographics: ppl: 0.1 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: unsafe_prob: 0. num_acts: 10 @@ -183,7 +191,7 @@ demographics: ppl: 0.1 drug_type: *base_drug_type incar: *base_incar - safe_sex: 0.1 + safe_sex: *safe_sex_default injection: unsafe_prob: 0. num_acts: 10 diff --git a/tests/params/simple_integration.yml b/tests/params/simple_integration.yml index 709c7707..18582b79 100644 --- a/tests/params/simple_integration.yml +++ b/tests/params/simple_integration.yml @@ -20,9 +20,16 @@ demographics: ppl: 1.0 incar: prob: 0.5 # default is off, so this only hits when turned on in integration test - safe_sex: 0.1 + safe_sex: + Sex: + prob: 1.0 + SexInj: + prob: 1.0 + Inj: + prob: 1.0 injection: - unsafe_prob: 0.1 + unsafe_prob: 0.3 + num_acts: 10 prep: adherence: 0.9 discontinue: 0.1 @@ -159,7 +166,7 @@ partnership: syringe_services: timeline: ssp_open: - start_time: 2 + start_time: 0 stop_time: 12 num_slots_start: 500 num_slots_stop: 500 diff --git a/titan/interactions/sex.py b/titan/interactions/sex.py index 298cec46..fe005c47 100644 --- a/titan/interactions/sex.py +++ b/titan/interactions/sex.py @@ -27,7 +27,8 @@ def get_num_acts(cls, model: "model.TITAN", rel: "agent.Relationship") -> int: p_safe_sex = ( rel.agent1.location.params.demographics[rel.agent1.race] .sex_type[rel.agent1.sex_type] - .safe_sex + .safe_sex[rel.bond_type] + .prob ) # increase condom usage if diagnosed diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index 1c6e8677..eb7dd40f 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -189,11 +189,16 @@ demographics: min: 0.0 max: 1.0 safe_sex: - default: 0.0 - description: "Probability of safe sex (condom use) per sex act" - type: float - min: 0.0 - max: 1.0 + type: sub-dict + description: "Parameters controlling condom use (safe sex) given bond type" + keys: + - bond_types + default: + prob: + default: 0.0 + type: float + min: 0.0 + max: 1.0 prep: discontinue: default: 0.0 diff --git a/titan/params/syringe_services.yml b/titan/params/syringe_services.yml index 8f258ff5..32ad2034 100644 --- a/titan/params/syringe_services.yml +++ b/titan/params/syringe_services.yml @@ -5,11 +5,9 @@ syringe_services: start_time: type: int description: "What time to start this prevalence of syringe services program" - min: 1 stop_time: type: int description: "What time to stop this prevalence of syringe services program" - min: 1 num_slots_start: type: int description: "Cap for number of available slots in SSP at beginning of time period" diff --git a/titan/partnering.py b/titan/partnering.py index 24cd90b3..ef130051 100644 --- a/titan/partnering.py +++ b/titan/partnering.py @@ -169,7 +169,6 @@ def get_partnership_duration( dur_info = params.partnership.duration[bond_type][race].bins diceroll = rand_gen.random() - print(dur_info) dur_bin = dur_info[5] for i in range(1, 5): if diceroll < dur_info[i].prob: From 25145ad76dd56a935b8220b3eb7de0e5c1b9418f Mon Sep 17 00:00:00 2001 From: sbessey Date: Tue, 5 Jan 2021 15:39:42 -0500 Subject: [PATCH 05/49] feat(haart): allow reinitiation of haart Agents who have previously been on haart can now reinitiate at a separate probability from normal initiation. --- settings/mississippi/demographics.yml | 3 +++ settings/nyc-msm/demographics.yml | 9 +++++++++ titan/features/haart.py | 27 +++++++++++++++++---------- titan/params/demographics.yml | 9 +++++++++ 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/settings/mississippi/demographics.yml b/settings/mississippi/demographics.yml index 80b56ce5..931cc51e 100644 --- a/settings/mississippi/demographics.yml +++ b/settings/mississippi/demographics.yml @@ -40,6 +40,9 @@ demographics: enroll_prob: 0.0 start: 12 stop: 9999 + reinit: + allow: true + prob: 0.01 adherence: init: 0.500 prob: 0.500 diff --git a/settings/nyc-msm/demographics.yml b/settings/nyc-msm/demographics.yml index 55f60ff0..d25dbfa5 100644 --- a/settings/nyc-msm/demographics.yml +++ b/settings/nyc-msm/demographics.yml @@ -48,6 +48,9 @@ demographics: start: 12 stop: 9999 enroll_prob: 0.0 + reinit: + allow: true + prob: 0.035 adherence: init: 0.489 prob: 0.489 @@ -122,6 +125,9 @@ demographics: start: 12 stop: 9999 enroll_prob: 0.0 + reinit: + allow: true + prob: 0.032 adherence: init: 0.719 prob: 0.719 @@ -196,6 +202,9 @@ demographics: start: 12 stop: 9999 enroll_prob: 0.0 + reinit: + allow: true + prob: 0.041 adherence: init: 0.707 prob: 0.707 diff --git a/titan/features/haart.py b/titan/features/haart.py index 34971738..b85e059b 100644 --- a/titan/features/haart.py +++ b/titan/features/haart.py @@ -26,6 +26,7 @@ def __init__(self, agent: "agent.Agent"): super().__init__(agent) self.active = False + self.ever = False self.adherent = False @classmethod @@ -62,6 +63,7 @@ def init_agent(self, pop: "population.Population", time: int): and pop.pop_random.random() < agent_params.haart.init ): self.active = True + self.ever = True self.add_agent(self.agent) haart_adh = agent_params.haart.adherence.init @@ -103,16 +105,20 @@ def update_agent(self, model: "model.TITAN"): if num_haart_agents < (haart_params.cap * num_dx_agents): self.initiate(model) else: - # Find enroll probability based on time since diagnosis - for i in haart_params.prob.values(): - if i.start <= (model.time - self.agent.hiv.dx_time) < i.stop: # type: ignore[attr-defined] - enroll_prob = i.enroll_prob - break - - if model.run_random.random() < ( - enroll_prob * model.calibration.haart.coverage - ): - self.initiate(model) + if self.ever and haart_params.reinit.allow: + if model.run_random.random() < haart_params.reinit.prob: + self.initiate(model) + else: + # Find enroll probability based on time since diagnosis + for i in haart_params.prob.values(): + if i.start <= (model.time - self.agent.hiv.dx_time) < i.stop: # type: ignore[attr-defined] + enroll_prob = i.enroll_prob + break + + if model.run_random.random() < ( + enroll_prob * model.calibration.haart.coverage + ): + self.initiate(model) # Go off HAART elif self.active and model.run_random.random() < haart_params.discontinue: @@ -195,4 +201,5 @@ def initiate(self, model: "model.TITAN"): # Add agent to HAART class set, update agent params self.active = True + self.ever = True self.add_agent(self.agent) diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index eb7dd40f..ebcf5d5f 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -327,6 +327,15 @@ demographics: start: 0 stop: 999 enroll_prob: 0.0 + reinit: + allow: + type: boolean + description: "Whether or not agents who have previously been on prep have a separate haart probability" + default: false + prob: + type: float + description: "Probability of reinitiating haart" + default: 0.0 adherence: init: default: 0.0 From c1919bd803cee9fb9bc77bdbd5dce84a19a217ea Mon Sep 17 00:00:00 2001 From: sbessey Date: Wed, 6 Jan 2021 10:45:57 -0500 Subject: [PATCH 06/49] test(population/partnering): test race-based duration --- tests/features/haart_test.py | 12 ++++++++++++ tests/params/basic.yml | 13 ++++++++++++- tests/partnering_test.py | 2 ++ tests/population_test.py | 13 ++++++++++++- 4 files changed, 38 insertions(+), 2 deletions(-) diff --git a/tests/features/haart_test.py b/tests/features/haart_test.py index bc4e8dee..ac735b4d 100644 --- a/tests/features/haart_test.py +++ b/tests/features/haart_test.py @@ -70,3 +70,15 @@ def test_update_haart_dx_time(make_model, make_agent): a.hiv.dx_time -= 10 a.haart.update_agent(model) assert a.haart.active + + +def test_update_haart_reinit(make_model, make_agent): + model = make_model() + a = make_agent(race="white") + + a.hiv.active = True + a.hiv.dx = True + a.hiv.dx_time = model.time - 1 # make duration 1, would pass if not reinit + a.haart.ever = True # uses reinit prob + a.haart.update_agent(model) + assert not a.haart.active diff --git a/tests/params/basic.yml b/tests/params/basic.yml index f3b6330b..edf9348d 100644 --- a/tests/params/basic.yml +++ b/tests/params/basic.yml @@ -50,6 +50,9 @@ demographics: enroll_prob: 1.0 start: 10 stop: 100 + reinit: &reinit_prob + allow: true + prob: 0.0 adherence: init: 0.1 prob: 0.1 @@ -73,6 +76,7 @@ demographics: haart: init: 0.1 prob: *haart_prob + reinit: *reinit_prob adherence: init: 0.1 prob: 0.1 @@ -232,13 +236,20 @@ partnership: versatile: 0.00745 duration: Sex: - white: &sex_dur + white: type: bins bins: 1: prob: 1.0 min: 1 max: 2 + black: + type: bins + bins: + 1: + prob: 1.0 + min: 3 + max: 4 Inj: white: &inj_dur type: distribution diff --git a/tests/partnering_test.py b/tests/partnering_test.py index d60500ec..8bee6c36 100644 --- a/tests/partnering_test.py +++ b/tests/partnering_test.py @@ -14,6 +14,8 @@ def test_partnership_duration(params): assert get_partnership_duration(params, FakeRandom(1.0), "Inj", "white") == 1 # test duration with bins assert get_partnership_duration(params, FakeRandom(0.1), "Sex", "white") == 1 + # test duration with second race + assert get_partnership_duration(params, FakeRandom(0.1), "Sex", "black") == 3 @pytest.mark.unit diff --git a/tests/population_test.py b/tests/population_test.py index a939a675..c7e1cdaa 100644 --- a/tests/population_test.py +++ b/tests/population_test.py @@ -248,7 +248,7 @@ def test_update_agent_partners_MSM_match(make_population, params): def test_update_agent_partners_NDU_PWID_match(make_population, params): pop = make_population(n=0) a = pop.create_agent(pop.geography.locations["world"], "white", 0, "MSM") - p = pop.create_agent(pop.geography.locations["world"], "white", 0, "MSM") + p = pop.create_agent(pop.geography.locations["world"], "black", 0, "MSM") # ensure random sex partner no assorting pop.pop_random = FakeRandom(1.1) a.drug_type = "None" @@ -265,6 +265,17 @@ def test_update_agent_partners_NDU_PWID_match(make_population, params): assert p in pop.graph.nodes() assert a.partners assert len(pop.graph.edges()) == 1 + for ( + rel + ) in ( + a.relationships + ): # check that duration uses "randomly selected" (first) partner + assert rel.duration == partnering.get_partnership_duration( + a.location.params, pop.np_random, "Sex", a.race + ) + assert rel.duration != partnering.get_partnership_duration( + a.location.params, pop.np_random, "Sex", p.race + ) @pytest.mark.unit From 56f56483a590fc7eb259e033d0c0e713ed48a801 Mon Sep 17 00:00:00 2001 From: sbessey Date: Mon, 11 Jan 2021 10:54:29 -0500 Subject: [PATCH 07/49] build(nyc-msm): establish testing probability by "location" Instead of every agent of a given race having the same testing probability, this creates three categories of test probability. --- settings/nyc-msm/location.yml | 55 +++++++++++++++++++++++++++++++++++ settings/nyc-msm/model.yml | 8 +++++ 2 files changed, 63 insertions(+) create mode 100644 settings/nyc-msm/location.yml diff --git a/settings/nyc-msm/location.yml b/settings/nyc-msm/location.yml new file mode 100644 index 00000000..8fc067b0 --- /dev/null +++ b/settings/nyc-msm/location.yml @@ -0,0 +1,55 @@ +location: + scaling: + high_test: + demographics|white|ppl: + field: override + override: 0.384 + demographics|white|sex_type|MSM|drug_type|None|hiv|dx|prob: &high_test_prob + field: override + override: 0.333 + demographics|black|ppl: + field: override + override: 0.278 + demographics|black|sex_type|MSM|drug_type|None|hiv|dx|prob: *high_test_prob + demographics|latino|ppl: + field: override + override: 0.338 + demographics|latino|sex_type|MSM|drug_type|None|hiv|dx|prob: *high_test_prob + + mid_test: + demographics|white|ppl: + field: override + override: 0.587 + demographics|white|sex_type|MSM|drug_type|None|hiv|dx|prob: &mid_test_prob + field: override + override: 0.167 + demographics|black|ppl: + field: override + override: 0.187 + demographics|black|sex_type|MSM|drug_type|None|hiv|dx|prob: *mid_test_prob + demographics|latino|ppl: + field: override + override: 0.226 + demographics|latino|sex_type|MSM|drug_type|None|hiv|dx|prob: *mid_test_prob + + low_test: + demographics|white|ppl: + field: override + override: 0.605 + demographics|white|sex_type|MSM|drug_type|None|hiv|dx|prob: &low_test_prob + field: override + override: 0.042 + demographics|black|ppl: + field: override + override: 0.082 + demographics|black|sex_type|MSM|drug_type|None|hiv|dx|prob: *low_test_prob + demographics|latino|ppl: + field: override + override: 0.313 + demographics|latino|sex_type|MSM|drug_type|None|hiv|dx|prob: *low_test_prob + + edges: + edge_default: + location_1: high_test + location_2: high_test + distance: 0 \ No newline at end of file diff --git a/settings/nyc-msm/model.yml b/settings/nyc-msm/model.yml index e2c0dd92..517c68f0 100644 --- a/settings/nyc-msm/model.yml +++ b/settings/nyc-msm/model.yml @@ -27,6 +27,14 @@ classes: - sex drug_types: - None + locations: + high_test: + ppl: .190 + mid_test: + ppl: .634 + low_test: + ppl: .176 + features: prep: true From 0b3e3a84137ada3821381972c3e33b18f796af04 Mon Sep 17 00:00:00 2001 From: sbessey Date: Mon, 11 Jan 2021 14:17:53 -0500 Subject: [PATCH 08/49] feat(haart): allow agents to lose haart adherence Agents may now become non-adherent while staying on haart. --- settings/mississippi/demographics.yml | 1 - settings/nyc-msm/demographics.yml | 6 +++--- settings/nyc-msm/hiv.yml | 1 + titan/features/haart.py | 16 +++++++++++----- titan/params/demographics.yml | 10 ++++++---- titan/params/hiv.yml | 4 ++++ 6 files changed, 25 insertions(+), 13 deletions(-) diff --git a/settings/mississippi/demographics.yml b/settings/mississippi/demographics.yml index 931cc51e..14e72b75 100644 --- a/settings/mississippi/demographics.yml +++ b/settings/mississippi/demographics.yml @@ -41,7 +41,6 @@ demographics: start: 12 stop: 9999 reinit: - allow: true prob: 0.01 adherence: init: 0.500 diff --git a/settings/nyc-msm/demographics.yml b/settings/nyc-msm/demographics.yml index d25dbfa5..c1cb7166 100644 --- a/settings/nyc-msm/demographics.yml +++ b/settings/nyc-msm/demographics.yml @@ -49,11 +49,11 @@ demographics: stop: 9999 enroll_prob: 0.0 reinit: - allow: true prob: 0.035 adherence: init: 0.489 prob: 0.489 + discontinue: 0.0689 discontinue: 0.0076 death_rate: base: 1.512 @@ -126,11 +126,11 @@ demographics: stop: 9999 enroll_prob: 0.0 reinit: - allow: true prob: 0.032 adherence: init: 0.719 prob: 0.719 + discontinue: 0.0718 discontinue: 0.0069 # calibration death_rate: base: 0.792 @@ -203,11 +203,11 @@ demographics: stop: 9999 enroll_prob: 0.0 reinit: - allow: true prob: 0.041 adherence: init: 0.707 prob: 0.707 + discontinue: 0.0745 discontinue: 0.0059 death_rate: base: 0.0696 diff --git a/settings/nyc-msm/hiv.yml b/settings/nyc-msm/hiv.yml index 53c0f32a..1cd0eec2 100644 --- a/settings/nyc-msm/hiv.yml +++ b/settings/nyc-msm/hiv.yml @@ -5,6 +5,7 @@ hiv: aids: prob: 0.004 start_time: -48 + reinit_haart: true prep: target_model: diff --git a/titan/features/haart.py b/titan/features/haart.py index b85e059b..ffcc7c3a 100644 --- a/titan/features/haart.py +++ b/titan/features/haart.py @@ -105,7 +105,7 @@ def update_agent(self, model: "model.TITAN"): if num_haart_agents < (haart_params.cap * num_dx_agents): self.initiate(model) else: - if self.ever and haart_params.reinit.allow: + if self.ever and self.agent.location.params.hiv.reinit_haart: if model.run_random.random() < haart_params.reinit.prob: self.initiate(model) else: @@ -121,10 +121,16 @@ def update_agent(self, model: "model.TITAN"): self.initiate(model) # Go off HAART - elif self.active and model.run_random.random() < haart_params.discontinue: - self.active = False - self.adherent = False - self.remove_agent(self.agent) + elif self.active: + if model.run_random.random() < haart_params.discontinue: + self.active = False + self.adherent = False + self.remove_agent(self.agent) + elif ( + self.adherent + and model.run_random.random() < haart_params.adherence.discontinue + ): + self.adherent = False @classmethod def add_agent(cls, agent: "agent.Agent"): diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index ebcf5d5f..e73aa01d 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -328,10 +328,6 @@ demographics: stop: 999 enroll_prob: 0.0 reinit: - allow: - type: boolean - description: "Whether or not agents who have previously been on prep have a separate haart probability" - default: false prob: type: float description: "Probability of reinitiating haart" @@ -349,6 +345,12 @@ demographics: type: float min: 0.0 max: 1.0 + discontinue: + default: 0.0 + description: "Probability that an adherent agent will become nonadherent" + type: float + min: 0.0 + max: 1.0 discontinue: default: 0.0 description: "Probability that an agent on HAART discontinues HAART in a given time step" diff --git a/titan/params/hiv.yml b/titan/params/hiv.yml index bb6f1a49..5e65a82f 100644 --- a/titan/params/hiv.yml +++ b/titan/params/hiv.yml @@ -53,6 +53,10 @@ hiv: default: false description: Whether "prob" defined in demographics.race.sex_type.haart is a percent of diagnosed, HIV+ agents instead of a per-time-step probability for HAART coverage type: boolean + reinit_haart: + type: boolean + default: false + description: Whether a separate probability should be used for haart initiation among agents who have previously discontinued haart start_time: default: 0 description: "What timestep to start agent interactions related to hiv transmission" From 6fdc944f0eb815919f0020a653f8c1860c686a14 Mon Sep 17 00:00:00 2001 From: sbessey Date: Mon, 11 Jan 2021 16:44:19 -0500 Subject: [PATCH 09/49] test(haart): add test for loss of adherence --- settings/nyc-msm/demographics.yml | 6 +++--- tests/features/haart_test.py | 10 +++++++++- tests/params/basic.yml | 4 +++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/settings/nyc-msm/demographics.yml b/settings/nyc-msm/demographics.yml index c1cb7166..5a537962 100644 --- a/settings/nyc-msm/demographics.yml +++ b/settings/nyc-msm/demographics.yml @@ -54,7 +54,7 @@ demographics: init: 0.489 prob: 0.489 discontinue: 0.0689 - discontinue: 0.0076 + discontinue: 0.014 death_rate: base: 1.512 num_partners: @@ -130,7 +130,7 @@ demographics: adherence: init: 0.719 prob: 0.719 - discontinue: 0.0718 + discontinue: 0.0115 discontinue: 0.0069 # calibration death_rate: base: 0.792 @@ -208,7 +208,7 @@ demographics: init: 0.707 prob: 0.707 discontinue: 0.0745 - discontinue: 0.0059 + discontinue: 0.00883 death_rate: base: 0.0696 num_partners: diff --git a/tests/features/haart_test.py b/tests/features/haart_test.py index ac735b4d..636b0827 100644 --- a/tests/features/haart_test.py +++ b/tests/features/haart_test.py @@ -54,6 +54,15 @@ def test_update_haart_t1(make_model, make_agent): assert a.haart.active assert a.haart.adherent is True + # falls off adherence + model.run_random = FakeRandom(1.0) + a.location.params.demographics[a.race].sex_type[a.sex_type].drug_type[ + a.drug_type + ].haart.adherence.discontinue = 5.0 + a.haart.update_agent(model) + assert a.haart.active + assert a.haart.adherent is False + def test_update_haart_dx_time(make_model, make_agent): model = make_model() @@ -76,7 +85,6 @@ def test_update_haart_reinit(make_model, make_agent): model = make_model() a = make_agent(race="white") - a.hiv.active = True a.hiv.dx = True a.hiv.dx_time = model.time - 1 # make duration 1, would pass if not reinit a.haart.ever = True # uses reinit prob diff --git a/tests/params/basic.yml b/tests/params/basic.yml index edf9348d..3a5beca0 100644 --- a/tests/params/basic.yml +++ b/tests/params/basic.yml @@ -51,7 +51,6 @@ demographics: start: 10 stop: 100 reinit: &reinit_prob - allow: true prob: 0.0 adherence: init: 0.1 @@ -314,6 +313,9 @@ exposures: hiv: true knowledge: true +hiv: + reinit_haart: true + features: incar: true prep: true From c3ff3c896978cba651cfc3aac55eee81184ef58a Mon Sep 17 00:00:00 2001 From: sbessey Date: Wed, 13 Jan 2021 11:15:32 -0500 Subject: [PATCH 10/49] feat(haart): allow agents already on haart to become adherent also includes some edits from PR --- settings/chicago/chicago_baseCase.yml | 9 --------- settings/scott/demographics.yml | 16 ++++++---------- titan/features/haart.py | 6 ++++++ titan/params/demographics.yml | 13 ++++++++++++- titan/params/haart.yml | 9 +++++++++ titan/params/hiv.yml | 8 -------- 6 files changed, 33 insertions(+), 28 deletions(-) create mode 100644 titan/params/haart.yml diff --git a/settings/chicago/chicago_baseCase.yml b/settings/chicago/chicago_baseCase.yml index c9a9b71e..bd93bd51 100644 --- a/settings/chicago/chicago_baseCase.yml +++ b/settings/chicago/chicago_baseCase.yml @@ -37,9 +37,6 @@ features: calibration: injection: act: 1.0 - sex: - partner: 1.0 - act: 1.0 acquisition: 1.0 test_frequency: 1.0 mortality: 1.0 @@ -163,12 +160,6 @@ partnership: prob: 1.0 min: 37 max: 48 - sex: - acquisition: - MSM: - versatile: 0.00745 - insertive: 0.0011 - receptive: 0.0138 pca: frequency: Social: diff --git a/settings/scott/demographics.yml b/settings/scott/demographics.yml index 205f85fa..69c781e6 100644 --- a/settings/scott/demographics.yml +++ b/settings/scott/demographics.yml @@ -25,12 +25,10 @@ demographics: init: insertive: 1.0 safe_sex: - Sex: - prob: 0.1 - Inj: - prob: 0.1 - SexInj: + Sex: &condom_use_HM prob: 0.1 + Inj: *condom_use_HM + SexInj: *condom_use_HM injection: unsafe_prob: 0.321 num_acts: 5 @@ -106,12 +104,10 @@ demographics: init: receptive: 1.0 safe_sex: - Sex: - prob: 0.2 - Inj: - prob: 0.2 - SexInj: + Sex: &condom_use prob: 0.2 + Inj: *condom_use_HF + SexInj: *condom_use_HF injection: unsafe_prob: 0.321 num_acts: 5 diff --git a/titan/features/haart.py b/titan/features/haart.py index ffcc7c3a..1912295f 100644 --- a/titan/features/haart.py +++ b/titan/features/haart.py @@ -109,6 +109,7 @@ def update_agent(self, model: "model.TITAN"): if model.run_random.random() < haart_params.reinit.prob: self.initiate(model) else: + enroll_prob = 0 # Find enroll probability based on time since diagnosis for i in haart_params.prob.values(): if i.start <= (model.time - self.agent.hiv.dx_time) < i.stop: # type: ignore[attr-defined] @@ -131,6 +132,11 @@ def update_agent(self, model: "model.TITAN"): and model.run_random.random() < haart_params.adherence.discontinue ): self.adherent = False + elif ( + not self.adherent + and model.run_random.random() < haart_params.adherence.prob + ): + self.adherent = True @classmethod def add_agent(cls, agent: "agent.Agent"): diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index e73aa01d..75141940 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -308,6 +308,14 @@ demographics: description: "Percent of agents on haart if haart cap is used" min: 0.0 max: 1.0 + haart_cap: + default: false + description: Whether "prob" defined in demographics.race.sex_type.haart is a percent of diagnosed, HIV+ agents instead of a per-time-step probability for HAART coverage + type: boolean + reinit_haart: + type: boolean + default: false + description: Whether a separate probability should be used for haart initiation among agents who have previously discontinued haart prob: type: bin description: "Probability that an agent in this class with HIV becomes enrolled in HAART at a given time step based on time since diagnosis (use 0 for haart cap)" @@ -347,10 +355,13 @@ demographics: max: 1.0 discontinue: default: 0.0 - description: "Probability that an adherent agent will become nonadherent" + description: "Probability that an adherent agent will become nonadherent at a given timestep" type: float min: 0.0 max: 1.0 + become: + default: 0.0 + description: "Probability that an agent already on haart will become adherent at a given timestep" discontinue: default: 0.0 description: "Probability that an agent on HAART discontinues HAART in a given time step" diff --git a/titan/params/haart.yml b/titan/params/haart.yml new file mode 100644 index 00000000..7df77d11 --- /dev/null +++ b/titan/params/haart.yml @@ -0,0 +1,9 @@ +haart: + cap: + default: false + description: Whether "prob" defined in demographics.race.sex_type.haart is a percent of diagnosed, HIV+ agents instead of a per-time-step probability for HAART coverage + type: boolean + reinit_haart: + type: boolean + default: false + description: Whether a separate probability should be used for haart initiation among agents who have previously discontinued haart diff --git a/titan/params/hiv.yml b/titan/params/hiv.yml index 5e65a82f..11e6321d 100644 --- a/titan/params/hiv.yml +++ b/titan/params/hiv.yml @@ -49,14 +49,6 @@ hiv: description: On creation, agents are randomly assigned an hiv.time, this is the maximum time type: int min: 2 - haart_cap: - default: false - description: Whether "prob" defined in demographics.race.sex_type.haart is a percent of diagnosed, HIV+ agents instead of a per-time-step probability for HAART coverage - type: boolean - reinit_haart: - type: boolean - default: false - description: Whether a separate probability should be used for haart initiation among agents who have previously discontinued haart start_time: default: 0 description: "What timestep to start agent interactions related to hiv transmission" From 8707ebfabf907e8f692e2c9396b4c0f27bef3229 Mon Sep 17 00:00:00 2001 From: sbessey Date: Wed, 13 Jan 2021 13:09:57 -0500 Subject: [PATCH 11/49] refactor: clarify param names and comment --- titan/features/haart.py | 6 +++--- titan/params/demographics.yml | 21 ++++++++------------- titan/params/haart.yml | 2 +- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/titan/features/haart.py b/titan/features/haart.py index 1912295f..e459c0c1 100644 --- a/titan/features/haart.py +++ b/titan/features/haart.py @@ -93,7 +93,7 @@ def update_agent(self, model: "model.TITAN"): ) # Go on HAART if not self.active: - if self.agent.location.params.hiv.haart_cap: + if self.agent.location.params.haart.use_cap: # if HAART is based on cap instead of prob, determine number of # HAART agents based on % of diagnosed agents num_dx_agents = self.agent.hiv.dx_counts[self.agent.race][ # type: ignore[attr-defined] @@ -111,9 +111,9 @@ def update_agent(self, model: "model.TITAN"): else: enroll_prob = 0 # Find enroll probability based on time since diagnosis - for i in haart_params.prob.values(): + for i in haart_params.enroll_prob.values(): if i.start <= (model.time - self.agent.hiv.dx_time) < i.stop: # type: ignore[attr-defined] - enroll_prob = i.enroll_prob + enroll_prob = i.prob break if model.run_random.random() < ( diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index 75141940..f8f09de1 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -190,7 +190,7 @@ demographics: max: 1.0 safe_sex: type: sub-dict - description: "Parameters controlling condom use (safe sex) given bond type" + description: "Parameters controlling condom use/safe sex per sex act given bond type" keys: - bond_types default: @@ -305,22 +305,14 @@ demographics: cap: type: float default: 0.0 - description: "Percent of agents on haart if haart cap is used" + description: "Percent of diagnosed agents on haart if haart cap is used" min: 0.0 max: 1.0 - haart_cap: - default: false - description: Whether "prob" defined in demographics.race.sex_type.haart is a percent of diagnosed, HIV+ agents instead of a per-time-step probability for HAART coverage - type: boolean - reinit_haart: - type: boolean - default: false - description: Whether a separate probability should be used for haart initiation among agents who have previously discontinued haart - prob: + enroll_prob: type: bin description: "Probability that an agent in this class with HIV becomes enrolled in HAART at a given time step based on time since diagnosis (use 0 for haart cap)" fields: - enroll_prob: + prob: type: float min: 0.0 max: 1.0 @@ -334,7 +326,7 @@ demographics: 0: start: 0 stop: 999 - enroll_prob: 0.0 + prob: 0.0 reinit: prob: type: float @@ -362,6 +354,9 @@ demographics: become: default: 0.0 description: "Probability that an agent already on haart will become adherent at a given timestep" + type: float + min: 0.0 + max: 1.0 discontinue: default: 0.0 description: "Probability that an agent on HAART discontinues HAART in a given time step" diff --git a/titan/params/haart.yml b/titan/params/haart.yml index 7df77d11..b48a3673 100644 --- a/titan/params/haart.yml +++ b/titan/params/haart.yml @@ -1,5 +1,5 @@ haart: - cap: + use_cap: default: false description: Whether "prob" defined in demographics.race.sex_type.haart is a percent of diagnosed, HIV+ agents instead of a per-time-step probability for HAART coverage type: boolean From b81ac4b32cd3fc33b3d4d4124cea9650b873e296 Mon Sep 17 00:00:00 2001 From: sbessey Date: Wed, 13 Jan 2021 14:27:54 -0500 Subject: [PATCH 12/49] refactor: change param names for consistency and correct param names in tests --- settings/mississippi/demographics.yml | 8 +++---- settings/nyc-msm/demographics.yml | 30 +++++++++++++-------------- settings/nyc-msm/hiv.yml | 4 +++- settings/scott/demographics.yml | 6 +++--- settings/scott/model.yml | 4 +++- tests/features/haart_test.py | 2 +- tests/params/basic.yml | 12 +++++------ tests/params/basic_seeded.yml | 4 ++-- tests/params/basic_seeded_error.yml | 4 ++-- tests/params/simple_integration.yml | 4 ++-- titan/features/haart.py | 6 +++--- titan/params/demographics.yml | 2 +- titan/params/haart.yml | 2 +- 13 files changed, 46 insertions(+), 42 deletions(-) diff --git a/settings/mississippi/demographics.yml b/settings/mississippi/demographics.yml index 14e72b75..d603935f 100644 --- a/settings/mississippi/demographics.yml +++ b/settings/mississippi/demographics.yml @@ -27,17 +27,17 @@ demographics: init: 0.46 haart: init: 0.690 - prob: + enroll: 0: - enroll_prob: 0.609 + prob: 0.609 start: 0 stop: 1 1: - enroll_prob: 0.074 + prob: 0.074 start: 1 stop: 12 2: - enroll_prob: 0.0 + prob: 0.0 start: 12 stop: 9999 reinit: diff --git a/settings/nyc-msm/demographics.yml b/settings/nyc-msm/demographics.yml index 5a537962..b41fe626 100644 --- a/settings/nyc-msm/demographics.yml +++ b/settings/nyc-msm/demographics.yml @@ -31,23 +31,23 @@ demographics: init: 0.500 haart: init: 0.764 - prob: + enroll: 0: start: 0 stop: 1 - enroll_prob: 0.709 + prob: 0.709 1: start: 1 stop: 3 - enroll_prob: 0.212 + prob: 0.212 2: start: 3 stop: 12 - enroll_prob: 0.051 + prob: 0.051 3: start: 12 stop: 9999 - enroll_prob: 0.0 + prob: 0.0 reinit: prob: 0.035 adherence: @@ -108,23 +108,23 @@ demographics: init: 0.487 haart: init: 0.764 - prob: + enroll: 0: - enroll_prob: 0.278 + prob: 0.278 start: 0 stop: 1 1: - enroll_prob: 0.193 + prob: 0.193 start: 1 stop: 3 2: - enroll_prob: 0.037 + prob: 0.037 start: 3 stop: 12 3: start: 12 stop: 9999 - enroll_prob: 0.0 + prob: 0.0 reinit: prob: 0.032 adherence: @@ -185,23 +185,23 @@ demographics: init: 0.471 haart: init: 0.791 - prob: + enroll: 0: start: 0 stop: 1 - enroll_prob: 0.742 + prob: 0.742 1: start: 1 stop: 3 - enroll_prob: 0.163 + prob: 0.163 2: start: 3 stop: 12 - enroll_prob: 0.029 + prob: 0.029 3: start: 12 stop: 9999 - enroll_prob: 0.0 + prob: 0.0 reinit: prob: 0.041 adherence: diff --git a/settings/nyc-msm/hiv.yml b/settings/nyc-msm/hiv.yml index 1cd0eec2..42212def 100644 --- a/settings/nyc-msm/hiv.yml +++ b/settings/nyc-msm/hiv.yml @@ -5,7 +5,6 @@ hiv: aids: prob: 0.004 start_time: -48 - reinit_haart: true prep: target_model: @@ -15,3 +14,6 @@ prep: efficacy: adherent: 0.96 non_adherent: 0.76 + +haart: + reinit: true diff --git a/settings/scott/demographics.yml b/settings/scott/demographics.yml index 69c781e6..06aa124a 100644 --- a/settings/scott/demographics.yml +++ b/settings/scott/demographics.yml @@ -42,9 +42,9 @@ demographics: init: 1.0 haart: &haart_vals init: 1.0 - prob: + enroll: 0: - enroll_prob: 0.679 + prob: 0.679 start: 0 stop: 999 adherence: @@ -104,7 +104,7 @@ demographics: init: receptive: 1.0 safe_sex: - Sex: &condom_use + Sex: &condom_use_HF prob: 0.2 Inj: *condom_use_HF SexInj: *condom_use_HF diff --git a/settings/scott/model.yml b/settings/scott/model.yml index a3ddf473..45ca3646 100644 --- a/settings/scott/model.yml +++ b/settings/scott/model.yml @@ -43,12 +43,14 @@ hiv: dx: risk_reduction: sex: 0.19 - haart_cap: true start_time: -48 init: -48 aids: prob: 0.0029 +haart: + use_cap: true + timeline_scaling: timeline: burn_scale: diff --git a/tests/features/haart_test.py b/tests/features/haart_test.py index 636b0827..f9febf98 100644 --- a/tests/features/haart_test.py +++ b/tests/features/haart_test.py @@ -36,7 +36,7 @@ def test_update_haart_t1(make_model, make_agent): assert a.haart.active is False # Try haart cap with low cap, one agent on haart and one diagnosed agent. Nothing happens - a.location.params.hiv.haart_cap = True + a.location.params.haart.use_cap = True a.haart.counts[a.race][a.sex_type] = 1 a.hiv.dx_counts[a.race][a.sex_type] = 5 a.location.params.demographics[a.race].sex_type[a.sex_type].drug_type[ diff --git a/tests/params/basic.yml b/tests/params/basic.yml index 3a5beca0..a6b876a6 100644 --- a/tests/params/basic.yml +++ b/tests/params/basic.yml @@ -41,13 +41,13 @@ demographics: init: 0.1 haart: init: 0.1 - prob: &haart_prob + enroll: &haart_prob 0: - enroll_prob: 0.1 + prob: 0.1 start: 0 stop: 10 1: - enroll_prob: 1.0 + prob: 1.0 start: 10 stop: 100 reinit: &reinit_prob @@ -74,7 +74,7 @@ demographics: value_type: float haart: init: 0.1 - prob: *haart_prob + enroll: *haart_prob reinit: *reinit_prob adherence: init: 0.1 @@ -313,8 +313,8 @@ exposures: hiv: true knowledge: true -hiv: - reinit_haart: true +haart: + reinit: true features: incar: true diff --git a/tests/params/basic_seeded.yml b/tests/params/basic_seeded.yml index 62e04d91..cc8577f3 100644 --- a/tests/params/basic_seeded.yml +++ b/tests/params/basic_seeded.yml @@ -43,7 +43,7 @@ demographics: init: 0.1 prob: 0: - enroll_prob: 0.1 + enroll: 0.1 start: 0 stop: 99 adherence: @@ -70,7 +70,7 @@ demographics: init: 0.1 prob: 0: - enroll_prob: 0.1 + enroll: 0.1 start: 0 stop: 99 adherence: diff --git a/tests/params/basic_seeded_error.yml b/tests/params/basic_seeded_error.yml index cc4fcb88..0a78f81b 100644 --- a/tests/params/basic_seeded_error.yml +++ b/tests/params/basic_seeded_error.yml @@ -43,7 +43,7 @@ demographics: init: 0.1 prob: 0: - enroll_prob: 0.1 + enroll: 0.1 start: 0 stop: 99 adherence: @@ -70,7 +70,7 @@ demographics: init: 0.1 prob: 0: - enroll_prob: 0.1 + enroll: 0.1 start: 0 stop: 99 adherence: diff --git a/tests/params/simple_integration.yml b/tests/params/simple_integration.yml index 18582b79..4bc57b3b 100644 --- a/tests/params/simple_integration.yml +++ b/tests/params/simple_integration.yml @@ -57,7 +57,7 @@ demographics: init: 0.1 prob: 1: - enroll_prob: 0.1 + enroll: 0.1 start: 0 stop: 999 adherence: @@ -102,7 +102,7 @@ demographics: init: 0.1 prob: 1: - enroll_prob: 0.1 + enroll: 0.1 start: 0 stop: 999 adherence: diff --git a/titan/features/haart.py b/titan/features/haart.py index e459c0c1..c3626eb5 100644 --- a/titan/features/haart.py +++ b/titan/features/haart.py @@ -105,13 +105,13 @@ def update_agent(self, model: "model.TITAN"): if num_haart_agents < (haart_params.cap * num_dx_agents): self.initiate(model) else: - if self.ever and self.agent.location.params.hiv.reinit_haart: + if self.ever and self.agent.location.params.haart.reinit: if model.run_random.random() < haart_params.reinit.prob: self.initiate(model) else: - enroll_prob = 0 + enroll = 0 # Find enroll probability based on time since diagnosis - for i in haart_params.enroll_prob.values(): + for i in haart_params.enroll.values(): if i.start <= (model.time - self.agent.hiv.dx_time) < i.stop: # type: ignore[attr-defined] enroll_prob = i.prob break diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index f8f09de1..3a42ad4e 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -308,7 +308,7 @@ demographics: description: "Percent of diagnosed agents on haart if haart cap is used" min: 0.0 max: 1.0 - enroll_prob: + enroll: type: bin description: "Probability that an agent in this class with HIV becomes enrolled in HAART at a given time step based on time since diagnosis (use 0 for haart cap)" fields: diff --git a/titan/params/haart.yml b/titan/params/haart.yml index b48a3673..d6c9589b 100644 --- a/titan/params/haart.yml +++ b/titan/params/haart.yml @@ -3,7 +3,7 @@ haart: default: false description: Whether "prob" defined in demographics.race.sex_type.haart is a percent of diagnosed, HIV+ agents instead of a per-time-step probability for HAART coverage type: boolean - reinit_haart: + reinit: type: boolean default: false description: Whether a separate probability should be used for haart initiation among agents who have previously discontinued haart From 8115c5991ccb63a6aa6c81cfc0ad9dd51f7fdb7f Mon Sep 17 00:00:00 2001 From: sbessey Date: Wed, 13 Jan 2021 15:40:16 -0500 Subject: [PATCH 13/49] refactor(output): add degree centrality to component report --- titan/output.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/titan/output.py b/titan/output.py index c86e9855..4d391e65 100644 --- a/titan/output.py +++ b/titan/output.py @@ -6,6 +6,7 @@ import os import networkx as nx # type: ignore +from numpy import mean # type: ignore from .parse_params import ObjMap from . import utils @@ -284,8 +285,8 @@ def print_components( if f.tell() == 0: f.write( "run_id\trunseed\tpopseed\tt\tcompID\ttotalN\tNhiv\tNprep\tNtrtHIV" - "\tComp_Status\tPCA\tOral\tInjectable\tAware\tnidu\tcentrality\tDensity" - "\tEffectiveSize" + header + "\n" + "\tComp_Status\tPCA\tOral\tInjectable\tAware\tnidu\tcentrality\tdensity" + "\tEffectiveSize" + header + "\tdeg_cent\n" ) comp_id = 0 @@ -341,6 +342,7 @@ def print_components( else: component_status = "treatment_no_eligible" + deg_cent = mean(list(nx.degree_centrality(comp).values())) race_str = "" for race in race_list: race_str += "\t" + str(race_count[race]) @@ -350,7 +352,7 @@ def print_components( f"{nhiv}\t" f"{nprep}\t{ntrthiv}\t{component_status}\t{pca}\t{oral}\t{injectable_prep}" f"\t{aware}\t{nidu}\t{comp_centrality:.4f}\t{comp_density:.4f}" - f"\t{average_size:.4f}{race_str}\n" + f"\t{average_size:.4f}{race_str}\t{deg_cent}\n" ) comp_id += 1 From 64c6776d836fa1ae370ce4f93aeb3b6565c7ed5d Mon Sep 17 00:00:00 2001 From: sbessey Date: Thu, 14 Jan 2021 12:45:17 -0500 Subject: [PATCH 14/49] test(haart): correct test for haart reinit --- tests/features/haart_test.py | 6 ++++++ titan/features/haart.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/features/haart_test.py b/tests/features/haart_test.py index f9febf98..d728b1f1 100644 --- a/tests/features/haart_test.py +++ b/tests/features/haart_test.py @@ -83,10 +83,16 @@ def test_update_haart_dx_time(make_model, make_agent): def test_update_haart_reinit(make_model, make_agent): model = make_model() + model.time = 1 a = make_agent(race="white") + a.hiv.active = True a.hiv.dx = True a.hiv.dx_time = model.time - 1 # make duration 1, would pass if not reinit a.haart.ever = True # uses reinit prob a.haart.update_agent(model) assert not a.haart.active + + model.run_random = FakeRandom(-1.1) + a.haart.update_agent(model) + assert a.haart.active diff --git a/titan/features/haart.py b/titan/features/haart.py index c3626eb5..de47bf79 100644 --- a/titan/features/haart.py +++ b/titan/features/haart.py @@ -109,7 +109,7 @@ def update_agent(self, model: "model.TITAN"): if model.run_random.random() < haart_params.reinit.prob: self.initiate(model) else: - enroll = 0 + enroll_prob = 0 # Find enroll probability based on time since diagnosis for i in haart_params.enroll.values(): if i.start <= (model.time - self.agent.hiv.dx_time) < i.stop: # type: ignore[attr-defined] From ef5f0a62be63b745a1e8f0b026d8d22cd0a44100 Mon Sep 17 00:00:00 2001 From: sbessey Date: Thu, 14 Jan 2021 13:02:15 -0500 Subject: [PATCH 15/49] refactor: fix typo in assertion --- titan/population_io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titan/population_io.py b/titan/population_io.py index 4a66d71a..c8a095c2 100644 --- a/titan/population_io.py +++ b/titan/population_io.py @@ -54,7 +54,7 @@ def write(pop: Population, dir: str, compress: bool = True) -> str: returns: path, or archive name if compress is true """ - assert len(pop.relationships) > 0, "can't write empty population" + assert len(pop.relationships) > 0, "Can't write empty population" # open agent file agent_file = os.path.join(dir, f"{pop.id}_agents.csv") From 09ad1ad9c57451f2215662204a3f94f4d847691c Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 12:12:24 -0500 Subject: [PATCH 16/49] build: add setup.py so titan can be installed with pip --- setup.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 setup.py diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..0f2b67fa --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +import setuptools + +with open("README.md", "r", encoding="utf-8") as fh: + long_description = fh.read() + +setuptools.setup( + name="titan", # Replace with your own username + version="2.0.0", + # author="Example Author", + # author_email="author@example.com", + description="TITAN Agent Based Model", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/marshall-lab/TITAN", + packages=setuptools.find_packages(), + classifiers=[ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + ], + python_requires='>=3.6', +) From dec1f22091864b689e48331fccf670bdbf383edf Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 12:26:36 -0500 Subject: [PATCH 17/49] fix: add params files to buil --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 0f2b67fa..8503159b 100644 --- a/setup.py +++ b/setup.py @@ -18,4 +18,5 @@ "Operating System :: OS Independent", ], python_requires='>=3.6', + package_data={'titan': 'params/*.yml'}, ) From 82e04d70fc2480b0054ceb9a6ee99bb9cd0e1877 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 12:30:35 -0500 Subject: [PATCH 18/49] build: add settings --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8503159b..91aa98fb 100644 --- a/setup.py +++ b/setup.py @@ -18,5 +18,6 @@ "Operating System :: OS Independent", ], python_requires='>=3.6', - package_data={'titan': 'params/*.yml'}, + package_data={'titan': ['params/*.yml']}, + data_files=[('settings', ['settings/*/*.yml', 'settings/*/*.yaml'])] ) From 0af42f1ab003bd4916ef84931eddea8e2ff0cdf2 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 12:35:49 -0500 Subject: [PATCH 19/49] refactor: move settings into titan --- run_titan.py | 5 ----- setup.py | 3 +-- titan/parse_params.py | 4 ++-- {settings => titan/settings}/atlanta/assort_mix.yml | 0 {settings => titan/settings}/atlanta/calibration.yml | 0 {settings => titan/settings}/atlanta/demographics.yml | 0 {settings => titan/settings}/atlanta/model.yml | 0 {settings => titan/settings}/atlanta/outputs.yml | 0 {settings => titan/settings}/atlanta/partnership.yml | 0 {settings => titan/settings}/chicago/chicago_baseCase.yml | 0 {settings => titan/settings}/mississippi/demographics.yml | 0 {settings => titan/settings}/mississippi/model.yml | 0 {settings => titan/settings}/nyc-msm/demographics.yml | 0 {settings => titan/settings}/nyc-msm/hiv.yml | 0 {settings => titan/settings}/nyc-msm/model.yml | 0 {settings => titan/settings}/nyc-msm/partnership.yml | 0 {settings => titan/settings}/scott/demographics.yml | 0 {settings => titan/settings}/scott/model.yml | 0 18 files changed, 3 insertions(+), 9 deletions(-) rename {settings => titan/settings}/atlanta/assort_mix.yml (100%) rename {settings => titan/settings}/atlanta/calibration.yml (100%) rename {settings => titan/settings}/atlanta/demographics.yml (100%) rename {settings => titan/settings}/atlanta/model.yml (100%) rename {settings => titan/settings}/atlanta/outputs.yml (100%) rename {settings => titan/settings}/atlanta/partnership.yml (100%) rename {settings => titan/settings}/chicago/chicago_baseCase.yml (100%) rename {settings => titan/settings}/mississippi/demographics.yml (100%) rename {settings => titan/settings}/mississippi/model.yml (100%) rename {settings => titan/settings}/nyc-msm/demographics.yml (100%) rename {settings => titan/settings}/nyc-msm/hiv.yml (100%) rename {settings => titan/settings}/nyc-msm/model.yml (100%) rename {settings => titan/settings}/nyc-msm/partnership.yml (100%) rename {settings => titan/settings}/scott/demographics.yml (100%) rename {settings => titan/settings}/scott/model.yml (100%) diff --git a/run_titan.py b/run_titan.py index 8950c45d..999d0d09 100755 --- a/run_titan.py +++ b/run_titan.py @@ -341,11 +341,6 @@ def main( setting = setting.lower() if setting == "custom": setting = None - else: - setting = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "settings", setting - ) - assert os.path.isdir(setting), f"{setting} is not a directory" params = create_params( setting, diff --git a/setup.py b/setup.py index 91aa98fb..1fee8cfd 100644 --- a/setup.py +++ b/setup.py @@ -18,6 +18,5 @@ "Operating System :: OS Independent", ], python_requires='>=3.6', - package_data={'titan': ['params/*.yml']}, - data_files=[('settings', ['settings/*/*.yml', 'settings/*/*.yaml'])] + package_data={'titan': ['params/*.yml', 'settings/*/*.yml']} ) diff --git a/titan/parse_params.py b/titan/parse_params.py index 6a7dfc2a..119edaeb 100644 --- a/titan/parse_params.py +++ b/titan/parse_params.py @@ -86,7 +86,7 @@ def check_params(params: ObjMap): def create_params( - setting_path: Optional[str], + setting_name: Optional[str], param_path: str, outdir: str, error_on_unused: bool = False, @@ -117,7 +117,7 @@ def create_params( # merge setting and params if setting_path is not None: - param_paths.append(setting_path) + param_paths.append(os.path.join(parent, "settings", setting_name)) param_paths.append(param_path) diff --git a/settings/atlanta/assort_mix.yml b/titan/settings/atlanta/assort_mix.yml similarity index 100% rename from settings/atlanta/assort_mix.yml rename to titan/settings/atlanta/assort_mix.yml diff --git a/settings/atlanta/calibration.yml b/titan/settings/atlanta/calibration.yml similarity index 100% rename from settings/atlanta/calibration.yml rename to titan/settings/atlanta/calibration.yml diff --git a/settings/atlanta/demographics.yml b/titan/settings/atlanta/demographics.yml similarity index 100% rename from settings/atlanta/demographics.yml rename to titan/settings/atlanta/demographics.yml diff --git a/settings/atlanta/model.yml b/titan/settings/atlanta/model.yml similarity index 100% rename from settings/atlanta/model.yml rename to titan/settings/atlanta/model.yml diff --git a/settings/atlanta/outputs.yml b/titan/settings/atlanta/outputs.yml similarity index 100% rename from settings/atlanta/outputs.yml rename to titan/settings/atlanta/outputs.yml diff --git a/settings/atlanta/partnership.yml b/titan/settings/atlanta/partnership.yml similarity index 100% rename from settings/atlanta/partnership.yml rename to titan/settings/atlanta/partnership.yml diff --git a/settings/chicago/chicago_baseCase.yml b/titan/settings/chicago/chicago_baseCase.yml similarity index 100% rename from settings/chicago/chicago_baseCase.yml rename to titan/settings/chicago/chicago_baseCase.yml diff --git a/settings/mississippi/demographics.yml b/titan/settings/mississippi/demographics.yml similarity index 100% rename from settings/mississippi/demographics.yml rename to titan/settings/mississippi/demographics.yml diff --git a/settings/mississippi/model.yml b/titan/settings/mississippi/model.yml similarity index 100% rename from settings/mississippi/model.yml rename to titan/settings/mississippi/model.yml diff --git a/settings/nyc-msm/demographics.yml b/titan/settings/nyc-msm/demographics.yml similarity index 100% rename from settings/nyc-msm/demographics.yml rename to titan/settings/nyc-msm/demographics.yml diff --git a/settings/nyc-msm/hiv.yml b/titan/settings/nyc-msm/hiv.yml similarity index 100% rename from settings/nyc-msm/hiv.yml rename to titan/settings/nyc-msm/hiv.yml diff --git a/settings/nyc-msm/model.yml b/titan/settings/nyc-msm/model.yml similarity index 100% rename from settings/nyc-msm/model.yml rename to titan/settings/nyc-msm/model.yml diff --git a/settings/nyc-msm/partnership.yml b/titan/settings/nyc-msm/partnership.yml similarity index 100% rename from settings/nyc-msm/partnership.yml rename to titan/settings/nyc-msm/partnership.yml diff --git a/settings/scott/demographics.yml b/titan/settings/scott/demographics.yml similarity index 100% rename from settings/scott/demographics.yml rename to titan/settings/scott/demographics.yml diff --git a/settings/scott/model.yml b/titan/settings/scott/model.yml similarity index 100% rename from settings/scott/model.yml rename to titan/settings/scott/model.yml From 471df757f1c8f1294de18de4945841776b531927 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 12:37:15 -0500 Subject: [PATCH 20/49] fix: change all var references --- titan/parse_params.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/titan/parse_params.py b/titan/parse_params.py index 119edaeb..fdbdd3cd 100644 --- a/titan/parse_params.py +++ b/titan/parse_params.py @@ -96,7 +96,7 @@ def create_params( or not to use the base setting. Parse and create a params (ObjMap) object. args: - setting_path: path to a settings file or directory or `None` + setting_name: path to a settings file or directory or `None` param_path: path to parameter file or directory outdir: path to directory where computed params will be saved error_on_unused: throw a hard error if there are unused parameters, otherwise warnings are only printed @@ -116,7 +116,7 @@ def create_params( param_paths = [] # merge setting and params - if setting_path is not None: + if setting_name is not None: param_paths.append(os.path.join(parent, "settings", setting_name)) param_paths.append(param_path) From 210a4172b45a5cc7e335974b6491975fed5bd433 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 12:55:52 -0500 Subject: [PATCH 21/49] build: make run_titan installed executable --- setup.py | 5 ++++- run_titan.py => titan/run_titan.py | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) rename run_titan.py => titan/run_titan.py (99%) diff --git a/setup.py b/setup.py index 1fee8cfd..1c783c6a 100644 --- a/setup.py +++ b/setup.py @@ -18,5 +18,8 @@ "Operating System :: OS Independent", ], python_requires='>=3.6', - package_data={'titan': ['params/*.yml', 'settings/*/*.yml']} + package_data={'titan': ['params/*.yml', 'settings/*/*.yml']}, + entry_scripts={ + 'console_scripts': ['run_titan'='titan:run_titan:main'] + } ) diff --git a/run_titan.py b/titan/run_titan.py similarity index 99% rename from run_titan.py rename to titan/run_titan.py index 999d0d09..a6d18641 100755 --- a/run_titan.py +++ b/titan/run_titan.py @@ -405,8 +405,7 @@ def mean(seq): print(("all tasks - sum: %8.4f seconds" % sum(wct))) print(f"all tasks - total: {toc} seconds") - -if __name__ == "__main__": +def main(): args = parser.parse_args() rows = args.rows.strip() if args.rows is not None else None sweepfile = args.sweepfile.strip() if args.sweepfile is not None else None @@ -424,3 +423,6 @@ def mean(seq): save_pop=args.savepop, pop_path=poppath, ) + +if __name__ == "__main__": + main() From 636f1af453499c9d907a212d18cde37062ec5157 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 12:56:59 -0500 Subject: [PATCH 22/49] fix: quotes --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1c783c6a..d7b0c6f5 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,6 @@ python_requires='>=3.6', package_data={'titan': ['params/*.yml', 'settings/*/*.yml']}, entry_scripts={ - 'console_scripts': ['run_titan'='titan:run_titan:main'] + 'console_scripts': ['run_titan=titan:run_titan:main'] } ) From 1c615655ff4c02ebe635d8c4fb114c9f4b7d0ffd Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 12:58:41 -0500 Subject: [PATCH 23/49] fix: no plural --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d7b0c6f5..4bbf3493 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,6 @@ python_requires='>=3.6', package_data={'titan': ['params/*.yml', 'settings/*/*.yml']}, entry_scripts={ - 'console_scripts': ['run_titan=titan:run_titan:main'] + 'console_script': ['run_titan=titan:run_titan:main'] } ) From eb9e7a25bcb151766bfd27982505f4c4d838ef33 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 12:59:53 -0500 Subject: [PATCH 24/49] fix: use a real kwarg --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4bbf3493..74578e63 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ ], python_requires='>=3.6', package_data={'titan': ['params/*.yml', 'settings/*/*.yml']}, - entry_scripts={ + entry_points={ 'console_script': ['run_titan=titan:run_titan:main'] } ) From b96107e6e2b714c35b9bff6f5ad4810216ab5098 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 13:03:47 -0500 Subject: [PATCH 25/49] build: make main top level --- setup.py | 2 +- titan/__init__.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 74578e63..646b6eab 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,6 @@ python_requires='>=3.6', package_data={'titan': ['params/*.yml', 'settings/*/*.yml']}, entry_points={ - 'console_script': ['run_titan=titan:run_titan:main'] + 'console_script': ['run_titan=titan:main'] } ) diff --git a/titan/__init__.py b/titan/__init__.py index 00adb5e6..e712b2f4 100644 --- a/titan/__init__.py +++ b/titan/__init__.py @@ -1 +1,2 @@ # makes src a package +from .run_titan import main From a15a83a462d177554b76b871760f778d7d1c34cf Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 13:05:41 -0500 Subject: [PATCH 26/49] fix: plural again --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 646b6eab..65469241 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,6 @@ python_requires='>=3.6', package_data={'titan': ['params/*.yml', 'settings/*/*.yml']}, entry_points={ - 'console_script': ['run_titan=titan:main'] + 'console_scripts': ['run_titan=titan:main'] } ) From ac2f2d257d944d88d47c7c45f9d29259f6a65ee0 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Fri, 15 Jan 2021 13:09:09 -0500 Subject: [PATCH 27/49] fix: only one main --- setup.py | 2 +- titan/__init__.py | 2 +- titan/run_titan.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 65469241..e5ef9e0a 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,6 @@ python_requires='>=3.6', package_data={'titan': ['params/*.yml', 'settings/*/*.yml']}, entry_points={ - 'console_scripts': ['run_titan=titan:main'] + 'console_scripts': ['run_titan=titan:script_init'] } ) diff --git a/titan/__init__.py b/titan/__init__.py index e712b2f4..6e01ee0c 100644 --- a/titan/__init__.py +++ b/titan/__init__.py @@ -1,2 +1,2 @@ # makes src a package -from .run_titan import main +from .run_titan import script_init diff --git a/titan/run_titan.py b/titan/run_titan.py index a6d18641..2261a4d3 100755 --- a/titan/run_titan.py +++ b/titan/run_titan.py @@ -405,7 +405,7 @@ def mean(seq): print(("all tasks - sum: %8.4f seconds" % sum(wct))) print(f"all tasks - total: {toc} seconds") -def main(): +def script_init(): args = parser.parse_args() rows = args.rows.strip() if args.rows is not None else None sweepfile = args.sweepfile.strip() if args.sweepfile is not None else None @@ -425,4 +425,4 @@ def main(): ) if __name__ == "__main__": - main() + init() From 754b0f976c29d6b204beb3152c2e5b2e503b346d Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 09:13:00 -0500 Subject: [PATCH 28/49] fix: add dependencies to setup.py --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index e5ef9e0a..bc9d79a6 100644 --- a/setup.py +++ b/setup.py @@ -3,6 +3,9 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() +with open('requirements.txt') as f: + required = f.read().splitlines() + setuptools.setup( name="titan", # Replace with your own username version="2.0.0", @@ -13,6 +16,7 @@ long_description_content_type="text/markdown", url="https://github.com/marshall-lab/TITAN", packages=setuptools.find_packages(), + install_requires=required, classifiers=[ "Programming Language :: Python :: 3", "Operating System :: OS Independent", From 55ff6a5a7cc2d6bbfec825b2cd36936c84fc26c9 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 09:27:53 -0500 Subject: [PATCH 29/49] build: use git branch/ref as setup version --- setup.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index bc9d79a6..0e04ce03 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,13 @@ import setuptools +from pathlib import Path + +def get_active_branch_name(): + head_dir = Path(".") / ".git" / "HEAD" + with head_dir.open("r") as f: content = f.read().splitlines() + + for line in content: + if line[0:4] == "ref:": + return line.partition("refs/heads/")[2] with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() @@ -8,7 +17,7 @@ setuptools.setup( name="titan", # Replace with your own username - version="2.0.0", + version=get_active_branch_name(), # author="Example Author", # author_email="author@example.com", description="TITAN Agent Based Model", From 32e5c390a42b79f7fd4990a2bea470efa92beab7 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 09:30:11 -0500 Subject: [PATCH 30/49] build: try prefixing version with 0.0.0 --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 0e04ce03..a6a97c5a 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ def get_active_branch_name(): setuptools.setup( name="titan", # Replace with your own username - version=get_active_branch_name(), + version=f"0.0.0-{get_active_branch_name()}", # author="Example Author", # author_email="author@example.com", description="TITAN Agent Based Model", From cd204ca04f8e70713009cb25b0d94b8bcc12b806 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 09:53:08 -0500 Subject: [PATCH 31/49] build: debugging print --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a6a97c5a..ab7a65bb 100644 --- a/setup.py +++ b/setup.py @@ -4,8 +4,8 @@ def get_active_branch_name(): head_dir = Path(".") / ".git" / "HEAD" with head_dir.open("r") as f: content = f.read().splitlines() - for line in content: + print(line) if line[0:4] == "ref:": return line.partition("refs/heads/")[2] From 1da03139aedf6c25ac21c19e53ee21624e088812 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 10:00:27 -0500 Subject: [PATCH 32/49] build: more printing --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index ab7a65bb..70dd1702 100644 --- a/setup.py +++ b/setup.py @@ -2,6 +2,7 @@ from pathlib import Path def get_active_branch_name(): + print("getting active branch name...") head_dir = Path(".") / ".git" / "HEAD" with head_dir.open("r") as f: content = f.read().splitlines() for line in content: From 0b60c1737f451ab82c507e6f0181f70bd1cdb219 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 10:06:31 -0500 Subject: [PATCH 33/49] build: force error --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 70dd1702..ae8a264f 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,7 @@ def get_active_branch_name(): for line in content: print(line) if line[0:4] == "ref:": + assert False return line.partition("refs/heads/")[2] with open("README.md", "r", encoding="utf-8") as fh: From f4af0ce27868ec0a8289715f45b4066fe464be7c Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 10:11:15 -0500 Subject: [PATCH 34/49] build: try using setuptools-git-version --- setup.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/setup.py b/setup.py index ae8a264f..0b9ec82e 100644 --- a/setup.py +++ b/setup.py @@ -1,15 +1,4 @@ import setuptools -from pathlib import Path - -def get_active_branch_name(): - print("getting active branch name...") - head_dir = Path(".") / ".git" / "HEAD" - with head_dir.open("r") as f: content = f.read().splitlines() - for line in content: - print(line) - if line[0:4] == "ref:": - assert False - return line.partition("refs/heads/")[2] with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() @@ -18,8 +7,12 @@ def get_active_branch_name(): required = f.read().splitlines() setuptools.setup( - name="titan", # Replace with your own username - version=f"0.0.0-{get_active_branch_name()}", + name="titan", # Replace with your own username, + version_config={ + "version_format": "{tag}.dev{sha}", + "starting_version": "0.1.0" + }, + setup_requires=['better-setuptools-git-version'], # author="Example Author", # author_email="author@example.com", description="TITAN Agent Based Model", From b7261ce941e6bd5812ed4e86c6c220508dc42eb3 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 10:30:49 -0500 Subject: [PATCH 35/49] build: lift/edit get-git-vesion --- setup.py | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 0b9ec82e..83adb717 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,41 @@ import setuptools +import subprocess + +# ========= FROM https://github.com/vivin/better-setuptools-git-version/blob/master/better_setuptools_git_version.py with edits ========= +def get_latest_tag(): + subprocess.getoutput("git describe --tags `git rev-list --tags --max-count=1`") + +def get_tag_commit_sha(tag): + """Return the commit that the tag is pointing to.""" + return subprocess.getoutput("git rev-list -n 1 {tag}".format(tag=tag)) + +def get_head_sha(): + """Return the sha key of HEAD.""" + return subprocess.getoutput('git rev-parse HEAD') + +def is_head_at_tag(tag): + """Return True or False depending on whether the given tag is pointing to HEAD""" + return get_head_sha() == get_tag_commit_sha(tag) + +def get_version(): + """ + Return the full git version using the given template. If there are no annotated tags, the version specified by + starting_version will be used. If HEAD is at the tag, the version will be the tag itself. If there are commits ahead + of the tag, the first 8 characters of the sha of the HEAD commit will be included. + In all of the above cases, if the working tree is also dirty or contains untracked files, a "+dirty" suffix will be + appended to the version. + Returns: + the formatted version based on tags in the git repository. + """ + template="{tag}.dev{sha}" + tag = get_latest_tag() + if is_head_at_tag(tag): + version = tag + else: + sha = get_head_sha()[:8] + version = template.format(tag=tag, sha=sha) + + return version with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() @@ -8,11 +45,7 @@ setuptools.setup( name="titan", # Replace with your own username, - version_config={ - "version_format": "{tag}.dev{sha}", - "starting_version": "0.1.0" - }, - setup_requires=['better-setuptools-git-version'], + version=get_version(), # author="Example Author", # author_email="author@example.com", description="TITAN Agent Based Model", From 3d80b63599c89a600a1313f7a26e9b65b31b1597 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 10:32:02 -0500 Subject: [PATCH 36/49] build: return the tag --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 83adb717..c7792306 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ # ========= FROM https://github.com/vivin/better-setuptools-git-version/blob/master/better_setuptools_git_version.py with edits ========= def get_latest_tag(): - subprocess.getoutput("git describe --tags `git rev-list --tags --max-count=1`") + return subprocess.getoutput("git describe --tags `git rev-list --tags --max-count=1`") def get_tag_commit_sha(tag): """Return the commit that the tag is pointing to.""" From ab5df07247f315d676676fb83eb9ca3ec4a10b8b Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 11:04:36 -0500 Subject: [PATCH 37/49] test: fix tests --- scripts/bs_Core.sh | 54 ----------- setup.py | 29 +++--- subTitan.sh | 189 -------------------------------------- tests/integration_test.py | 20 +++- titan/parse_params.py | 6 +- titan/run_titan.py | 14 +-- 6 files changed, 45 insertions(+), 267 deletions(-) delete mode 100755 scripts/bs_Core.sh delete mode 100755 subTitan.sh diff --git a/scripts/bs_Core.sh b/scripts/bs_Core.sh deleted file mode 100755 index fd156520..00000000 --- a/scripts/bs_Core.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash -#SBATCH -J MODEL_NAME -#SBATCH -c NCORES -#SBATCH --time=WALL_TIME -#SBATCH --mail-type=FAIL -#SBATCH --mail-type=END -#SBATCH --mail-user=NoMail -#SBATCH --mem=MEMORYGB - -if [ -z "$SLURM_NPROCS" ] ; then - if [ -z "$SLURM_NTASKS_PER_NODE" ] ; then - SLURM_NTASKS_PER_NODE=1 - fi - SLURM_NPROCS=$(( $SLURM_JOB_NUM_NODES * $SLURM_NTASKS_PER_NODE )) -fi - -#!/bin/bash -module load graphviz - -setting="" -paramPath="" -nMC="" -forceFlag="" -sweepDefs="" -sweepfile="" -rows="" - -while getopts S:n:w:W:r:Fa:p:P: option -do - case "${option}" - in - S) setting=${OPTARG};; - n) nMC=${OPTARG};; - w) sweepDefs+="-w ${OPTARG} ";; - W) sweepfile="-W ${OPTARG}";; - r) rows="-r ${OPTARG}";; - F) forceFlag="-F";; - a) paramPath=${OPTARG};; - p) savePop="--savepop";; - P) popPath="--poppath ${OPTARG}";; - esac -done - -cd $PWD - -echo Master process running on `hostname` -echo Directory is `pwd` -echo PBS has allocated the following nodes: -echo `cat $PBS_NODEFILE` -echo Starting execution at `date` -NCPU=`wc -l < $PBS_NODEFILE` -echo This job has allocated $NCPU CPUs - -./run_titan.py -S $setting -n $nMC -p $paramPath $forceFlag $sweepDefs $sweepfile $rows $savePop $popPath diff --git a/setup.py b/setup.py index c7792306..c237da2e 100644 --- a/setup.py +++ b/setup.py @@ -3,20 +3,26 @@ # ========= FROM https://github.com/vivin/better-setuptools-git-version/blob/master/better_setuptools_git_version.py with edits ========= def get_latest_tag(): - return subprocess.getoutput("git describe --tags `git rev-list --tags --max-count=1`") + return subprocess.getoutput( + "git describe --tags `git rev-list --tags --max-count=1`" + ) + def get_tag_commit_sha(tag): """Return the commit that the tag is pointing to.""" return subprocess.getoutput("git rev-list -n 1 {tag}".format(tag=tag)) + def get_head_sha(): """Return the sha key of HEAD.""" - return subprocess.getoutput('git rev-parse HEAD') + return subprocess.getoutput("git rev-parse HEAD") + def is_head_at_tag(tag): """Return True or False depending on whether the given tag is pointing to HEAD""" return get_head_sha() == get_tag_commit_sha(tag) + def get_version(): """ Return the full git version using the given template. If there are no annotated tags, the version specified by @@ -27,7 +33,7 @@ def get_version(): Returns: the formatted version based on tags in the git repository. """ - template="{tag}.dev{sha}" + template = "{tag}.dev{sha}" tag = get_latest_tag() if is_head_at_tag(tag): version = tag @@ -35,16 +41,19 @@ def get_version(): sha = get_head_sha()[:8] version = template.format(tag=tag, sha=sha) - return version + return versio + +# add the readme as the description with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() -with open('requirements.txt') as f: +# make the requirements.txt be installed (not good practice for general packaging, good for current oscar set up) +with open("requirements.txt") as f: required = f.read().splitlines() setuptools.setup( - name="titan", # Replace with your own username, + name="titan", # Replace with your own username, version=get_version(), # author="Example Author", # author_email="author@example.com", @@ -58,9 +67,7 @@ def get_version(): "Programming Language :: Python :: 3", "Operating System :: OS Independent", ], - python_requires='>=3.6', - package_data={'titan': ['params/*.yml', 'settings/*/*.yml']}, - entry_points={ - 'console_scripts': ['run_titan=titan:script_init'] - } + python_requires=">=3.6", + package_data={"titan": ["params/*.yml", "settings/*/*.yml"]}, + entry_points={"console_scripts": ["run_titan=titan:script_init"]}, ) diff --git a/subTitan.sh b/subTitan.sh deleted file mode 100755 index e49eb126..00000000 --- a/subTitan.sh +++ /dev/null @@ -1,189 +0,0 @@ -#!/bin/bash - -#Read in source code path, then shift for optargs -titanPath=$PWD -paramPath="$1" -shift - -setting="custom" -date=`date +%Y-%m-%d-T%H-%M-%S` -user=${USER} -walltime=12:00:00 -memory=12g -outfile="Jobname.o" -repeats=1 -nMC=1 -model=${PWD##*/} -basePath=$PWD -jobname="" -folderName="" -sweepDefs="" -sweepfile="" -rows="" -num_cores=1 -forceFlag="" -savePop="" -popPath="" - -while getopts m:j:T:S:r:n:f:w:W:R:Fc:t:p:P: option -do - case "${option}" - in - m) memory=${OPTARG};; - j) jobname=${OPTARG};; - T) walltime=${OPTARG};; - S) setting=${OPTARG};; - r) repeats=${OPTARG};; - n) nMC=${OPTARG};; - f) folderName=${OPTARG};; - w) sweepDefs+="-w ${OPTARG} ";; - W) sweepfile="-W ${OPTARG}";; - R) rows="-r ${OPTARG}";; - F) forceFlag="-F";; - c) num_cores=${OPTARG};; - t) titanPath=${OPTARG};; - p) savePop="-p";; - P) popPath="-P ${OPTARG}";; - esac -done - -if [[ $jobname == "" ]]; then - jobname="Analysis_$setting_$date" -fi - -if [[ $folderName == "" ]]; then - folderName="$setting/" -fi - -srcCode="${titanPath}/titan/" -outPath="$HOME/scratch/$folderName" - -usage() { -echo " -usage: subtitan {Parameter file or directory}[-T walltime] [-m memory] [-S setting] [-j jobname] [-r repeats] [-n iterations] [-b use_base] [-f folder_name] [-w sweep_defs] [-F force] [-c num_cores ] [-t titanPath ] - -Starts a TITAN simulation in ~/scratch/{SourceFolder}/{jobname} - -options: - -j jobname name of analysis for organization (default: {SourceFolder}_date) - -T walltime as hh:mm:ss, max compute time (default: $walltime) - -m memory per node, as #[k|m|g] (default: $memory) - -S setting name of setting for this model - -r repeats number of times to repeat the analysis (default: $repeats) - -n iterations number of mode iterations per job (default: $nMC) - -f folder_name What the parent folder for the model run outputs should be called (default: ) - -w sweep_defs Optionally, definitions of sweep parameters in the format param:start:stop[:step] - -W sweepfile Optionally, a csv file with sweep definitions (if used, -w flag is ignored) - -R rows Optionally, which data rows of the sweep file to use in format start:stop - -F force If the number of sweep combinations exceeds 100, run anyway - -c num_cores How many cores to request and run the job on (default: $num_cores) - -t titanPath where the code is - -p savePop save population, either 'all' or 'core' (default none) - -P popPath load population from the provide path (by default, creates a new population) -" -exit 0 -} - -updateParams() { -echo " - - Updating params: - savePath $PWD - sourceCode $srcCode - jobname $jobname - walltime $walltime - memory $memory - num_cores $num_cores -" - -#Submit script params -sed -i "s/MODEL_NAME/$jobname/g" scripts/bs_Core.sh -sed -i "s/WALL_TIME/$walltime/g" scripts/bs_Core.sh -sed -i "s/MEMORY/$memory/g" scripts/bs_Core.sh -sed -i "s/NCORES/$num_cores/g" scripts/bs_Core.sh - -} - -prepSubmit() { - #Copy source code into parent path - echo -e "\n\tCopying $srcCode to $finalPath" - mkdir -p $finalPath - cp $titanPath/run_titan.py $finalPath - cp -rT $titanPath/titan $finalPath/titan - cp -rT $titanPath/scripts $finalPath/scripts - cp -rT $titanPath/settings $finalPath/settings - #Move into new source code folder - echo -e "\n\tMoving to model folder directory" - cd $finalPath - echo -e "\t$PWD" - updateParams; - - #Submit job to cluster - sbatch scripts/bs_Core.sh -S $setting -a $paramPath -n $nMC $forceFlag $sweepDefs $sweepfile $rows $savePop $popPath - - #Move back to base directory - cd $basePath -} - - -# User and Date will be ignored if job ID is specified - -if [ ! $paramPath ]; then - usage; -fi - -if [[ ${paramPath:0:1} != "/" ]] || [[ ${paramPath:0:1} == "~" ]]; then - paramPath="${pwd}/$paramPath" -fi - -if [ ! -d $srcCode ]; then - echo -e "\n\n$srcCode is not a directory! Source code must be provided as a directory\n" - exit 0 -fi - -if [ -d $outPath$jobname ]; then - echo -e "\n\n!! WARNING !!\nThe folder $jobname already exists and will be OVERWRITTEN!\n" - read -p "Continue (y/n)?" choice - case "$choice" in - y|Y ) echo "Proceeding";; - n|N|* ) echo "Aborting" - exit 0;; - esac -fi - -if [ $srcCode ]; then - - echo " - jobname $jobname - outPath $outPath - paramPath $paramPath - user $user - date $date - walltime $walltime - memory $memory - outfile $outfile - model $model" - echo -e "\n" - - echo -e "\tMaking parent directory in scratch" - mkdir -p $outPath - echo -e "\t $outPath" - - if [ $repeats -gt 1 ]; then - # mkdir -p $outPath$jobname - basejobname=$jobname - for ((i=1; i<=repeats; i++)); do - echo -e "\n\nWorking on repeat $i" - jobname=$basejobname"_"$i - finalPath=$outPath"/"$basejobname"/"$jobname - prepSubmit; - done - else - finalPath=$outPath"/"$jobname - prepSubmit; - fi - -else - echo -e "\nSOMETHING WENT WRONG!!! Abort" - exit 1; -fi diff --git a/tests/integration_test.py b/tests/integration_test.py index 5f9431a4..9b54e2f1 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -30,7 +30,9 @@ def _make_model_integration(): @pytest.mark.integration_deterministic def test_model_runs(): - f = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "run_titan.py") + f = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "..", "titan", "run_titan.py" + ) param_file = os.path.join( os.path.dirname(os.path.abspath(__file__)), "params", "basic.yml" ) @@ -43,7 +45,9 @@ def test_model_runs(): def test_model_reproducible(tmpdir): path_a = tmpdir.mkdir("result_a") path_b = tmpdir.mkdir("result_b") - f = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "run_titan.py") + f = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "..", "titan", "run_titan.py" + ) param_file = os.path.join( os.path.dirname(os.path.abspath(__file__)), "params", "basic_seeded.yml" ) @@ -80,7 +84,9 @@ def test_model_reproducible(tmpdir): @pytest.mark.integration_deterministic def test_model_pop_write_read(tmpdir): path_a = tmpdir.mkdir("a") - f = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "run_titan.py") + f = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "..", "titan", "run_titan.py" + ) param_file = os.path.join( os.path.dirname(os.path.abspath(__file__)), "params", "basic.yml" ) @@ -99,13 +105,17 @@ def test_model_pop_write_read(tmpdir): @pytest.mark.integration_deterministic def test_model_settings_run(tmpdir): - f = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "run_titan.py") + f = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "..", "titan", "run_titan.py" + ) param_file = os.path.join( os.path.dirname(os.path.abspath(__file__)), "params", "integration_base.yml" ) for item in os.listdir( - os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "settings") + os.path.join( + os.path.dirname(os.path.abspath(__file__)), "..", "titan", "settings" + ) ): if "__" not in item and item != "base": path = tmpdir.mkdir(item) diff --git a/titan/parse_params.py b/titan/parse_params.py index fdbdd3cd..9b7de6ab 100644 --- a/titan/parse_params.py +++ b/titan/parse_params.py @@ -117,7 +117,11 @@ def create_params( # merge setting and params if setting_name is not None: - param_paths.append(os.path.join(parent, "settings", setting_name)) + # check if it's a known setting or pass it through as a path + if setting_name in os.listdir(os.path.join(parent, "settings")): + param_paths.append(os.path.join(parent, "settings", setting_name)) + else: + param_paths.append(setting_name) param_paths.append(param_path) diff --git a/titan/run_titan.py b/titan/run_titan.py index 2261a4d3..5926cda1 100755 --- a/titan/run_titan.py +++ b/titan/run_titan.py @@ -21,9 +21,8 @@ from titan.parse_params import create_params from titan import utils -# how many cores can we use -NCORES = os.environ.get("SLURM_CPUS_PER_TASK", cpu_count()) -NCORES = int(NCORES) # environment variable returns string +# how many cores can we use, environment variable returns string +NCORES = int(os.environ.get("SLURM_CPUS_PER_TASK", cpu_count())) # set up args parsing parser = argparse.ArgumentParser(description="Run TITAN model") @@ -339,11 +338,10 @@ def main( # generate params - if no setting, set to none setting = setting.lower() - if setting == "custom": - setting = None + setting_parsed = None if setting == "custom" else setting params = create_params( - setting, + setting_parsed, params_path, outfile_dir, error_on_unused=error_on_unused, @@ -405,6 +403,7 @@ def mean(seq): print(("all tasks - sum: %8.4f seconds" % sum(wct))) print(f"all tasks - total: {toc} seconds") + def script_init(): args = parser.parse_args() rows = args.rows.strip() if args.rows is not None else None @@ -424,5 +423,6 @@ def script_init(): pop_path=poppath, ) + if __name__ == "__main__": - init() + script_init() From ec35b39ea40faac095106361ce9bed6f398942e2 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 11:47:16 -0500 Subject: [PATCH 38/49] test: allow run_titan to work as local script still --- titan/run_titan.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/titan/run_titan.py b/titan/run_titan.py index 5926cda1..62a4fadd 100755 --- a/titan/run_titan.py +++ b/titan/run_titan.py @@ -5,6 +5,7 @@ from copy import copy import sys import os +from inspect import getsourcefile import shutil import argparse import itertools @@ -15,6 +16,14 @@ from typing import List, Optional import subprocess +# allow imports to work if running it as a script for development locally +if __name__ == "__main__": + PACKAGE_PARENT = ".." + SCRIPT_DIR = os.path.dirname( + os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))) + ) + sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT))) + from titan.model import TITAN from titan.population import Population import titan.population_io as pop_io From b9738c8ee5052c9701a545e0adedb483e00d8e55 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 11:48:18 -0500 Subject: [PATCH 39/49] fix: typo versio => version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c237da2e..972740e0 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ def get_version(): sha = get_head_sha()[:8] version = template.format(tag=tag, sha=sha) - return versio + return version # add the readme as the description From b40b9d915dd7a6e9d564414322e60bbb3a216640 Mon Sep 17 00:00:00 2001 From: mcmcgrath13 Date: Tue, 19 Jan 2021 12:31:28 -0500 Subject: [PATCH 40/49] test: add run_titan tests --- tests/integration_test.py | 13 ++++++++ tests/run_titan_test.py | 65 +++++++++++++++++++++++++++++++++++++++ titan/run_titan.py | 2 +- 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 tests/run_titan_test.py diff --git a/tests/integration_test.py b/tests/integration_test.py index 9b54e2f1..1bf99f93 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -41,6 +41,19 @@ def test_model_runs(): assert True +@pytest.mark.integration_deterministic +def test_model_runs_sweep(): + f = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "..", "titan", "run_titan.py" + ) + param_file = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "params", "basic.yml" + ) + + subprocess.check_call([f, f"-p {param_file}", "-w model.seed.run:1:3"]) + assert True + + @pytest.mark.integration_deterministic def test_model_reproducible(tmpdir): path_a = tmpdir.mkdir("result_a") diff --git a/tests/run_titan_test.py b/tests/run_titan_test.py new file mode 100644 index 00000000..2445e589 --- /dev/null +++ b/tests/run_titan_test.py @@ -0,0 +1,65 @@ +import pytest + +from titan.run_titan import * + + +@pytest.mark.unit +def test_sweep_range(): + assert sweep_range("param:1:2") == { + "start": 1, + "stop": 2, + "step": 1, + "param": "param", + } + assert sweep_range("param:1.5:2:0.5") == { + "start": 1.5, + "stop": 2.0, + "step": 0.5, + "param": "param", + } + + with pytest.raises(ValueError): + sweep_range("param:a:b:c") + + +@pytest.mark.unit +def test_drange(): + start = 1 + step = 0.1 + stop = 2 + i = 0 + for val in drange(start, stop, step): + i += 1 + assert val >= start and val < stop + + assert i == 10 + + +@pytest.mark.unit +def test_setup_sweeps(): + sweeps = [ + {"start": 1.5, "stop": 2.3, "step": 0.5, "param": "param1"}, + {"start": 1, "stop": 4, "step": 1, "param": "param2"}, + ] + + defs = setup_sweeps(sweeps) + + assert len(defs) == 6 + + +@pytest.mark.unit +def test_setup_seepfile(): + sweepfile = os.path.join( + os.path.dirname(os.path.abspath(__file__)), "params", "sweepfile.csv" + ) + rows = None + defs = setup_sweeps_file(sweepfile, rows) + + assert len(defs) == 7 + assert defs[0] == {"model.seed.run": 0, "model.seed.ppl": 0} + + rows = "2:3" + defs = setup_sweeps_file(sweepfile, rows) + + assert len(defs) == 2 + assert defs[0] == {"model.seed.run": 1, "model.seed.ppl": 1} diff --git a/titan/run_titan.py b/titan/run_titan.py index 62a4fadd..e5575f6c 100755 --- a/titan/run_titan.py +++ b/titan/run_titan.py @@ -98,7 +98,7 @@ def sweep_range(string): except ValueError: raise ValueError("start, stop, and step must have same type (int or float)") - return {"param": parts[0], "start": start, "stop": stop, "step": step} + return {"param": parts[0].strip(), "start": start, "stop": stop, "step": step} parser.add_argument( From 2141428c0935696adba6be2660320e0ed453ac8b Mon Sep 17 00:00:00 2001 From: sbessey Date: Tue, 19 Jan 2021 13:05:22 -0500 Subject: [PATCH 41/49] refactor(aids): move haart scale on aids progression to haart.py HAART is a feature, so all related methods should live in it (and all related params in haart.yml) BREAKING CHANGE: hiv.aids.haart_scale is now haart.aids_scale --- titan/exposures/hiv.py | 6 +----- titan/features/haart.py | 9 +++++++++ titan/params/haart.yml | 13 +++++++++++++ titan/params/hiv.yml | 13 ------------- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/titan/exposures/hiv.py b/titan/exposures/hiv.py index 82004c77..40dc2240 100644 --- a/titan/exposures/hiv.py +++ b/titan/exposures/hiv.py @@ -326,11 +326,7 @@ def progress_to_aids(self, model: "model.TITAN"): """ aids_params = self.agent.location.params.hiv.aids p = 1.0 - if self.agent.haart.active: # type: ignore[attr-defined] - if self.agent.haart.adherent: # type: ignore[attr-defined] - p = aids_params.haart_scale.adherent - else: - p = aids_params.haart_scale.non_adherent + self.agent.haart.aids_scale() if model.run_random.random() < p * aids_params.prob: self.aids = True diff --git a/titan/features/haart.py b/titan/features/haart.py index de47bf79..fad961d7 100644 --- a/titan/features/haart.py +++ b/titan/features/haart.py @@ -194,6 +194,15 @@ def get_transmission_risk_multiplier(self, time: int, interaction_type: str): return prob + def aids_scale(self): + prob = 1.0 + if self.active: + params = self.agent.location.params + adherence = "adherent" if self.adherent else "non_adherent" + prob = params.haart.aids_scale[adherence] + + return prob + # =========== HELPER METHODS ============ def initiate(self, model: "model.TITAN"): diff --git a/titan/params/haart.yml b/titan/params/haart.yml index d6c9589b..e62883f8 100644 --- a/titan/params/haart.yml +++ b/titan/params/haart.yml @@ -7,3 +7,16 @@ haart: type: boolean default: false description: Whether a separate probability should be used for haart initiation among agents who have previously discontinued haart + aids_scale: + non_adherent: + description: Scalar for aids progression for agents on haart but non-adherent + default: 0.00368 + type: float + min: 0. + max: 1. + adherent: + description: Scalar for aids progression for agents adherent to haart + default: 0.0008 + type: float + min: 0. + max: 1. diff --git a/titan/params/hiv.yml b/titan/params/hiv.yml index 11e6321d..108ef18b 100644 --- a/titan/params/hiv.yml +++ b/titan/params/hiv.yml @@ -31,19 +31,6 @@ hiv: type: float min: 0 max: 1 - haart_scale: - non_adherent: - description: Scalar for aids progression for agents on haart but non-adherent - default: 0.00368 - type: float - min: 0. - max: 1. - adherent: - description: Scalar for aids progression for agents adherent to haart - default: 0.0008 - type: float - min: 0. - max: 1. max_init_time: default: 42 description: On creation, agents are randomly assigned an hiv.time, this is the maximum time From e6187b610b95ea62db3b616ee094bad77874ade3 Mon Sep 17 00:00:00 2001 From: sbessey Date: Tue, 19 Jan 2021 13:12:43 -0500 Subject: [PATCH 42/49] refactor(prep): change prep enrollment to def instead of bin --- settings/mississippi/demographics.yml | 6 +++--- settings/nyc-msm/demographics.yml | 8 ++++---- settings/scott/demographics.yml | 2 +- titan/params/demographics.yml | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/settings/mississippi/demographics.yml b/settings/mississippi/demographics.yml index d603935f..4d478e44 100644 --- a/settings/mississippi/demographics.yml +++ b/settings/mississippi/demographics.yml @@ -28,15 +28,15 @@ demographics: haart: init: 0.690 enroll: - 0: + enroll_0: prob: 0.609 start: 0 stop: 1 - 1: + enroll_1: prob: 0.074 start: 1 stop: 12 - 2: + enroll_2: prob: 0.0 start: 12 stop: 9999 diff --git a/settings/nyc-msm/demographics.yml b/settings/nyc-msm/demographics.yml index b41fe626..0f5528cd 100644 --- a/settings/nyc-msm/demographics.yml +++ b/settings/nyc-msm/demographics.yml @@ -32,19 +32,19 @@ demographics: haart: init: 0.764 enroll: - 0: + enroll_0: start: 0 stop: 1 prob: 0.709 - 1: + enroll_1: start: 1 stop: 3 prob: 0.212 - 2: + enroll_2: start: 3 stop: 12 prob: 0.051 - 3: + enroll_3: start: 12 stop: 9999 prob: 0.0 diff --git a/settings/scott/demographics.yml b/settings/scott/demographics.yml index 06aa124a..045142a7 100644 --- a/settings/scott/demographics.yml +++ b/settings/scott/demographics.yml @@ -43,7 +43,7 @@ demographics: haart: &haart_vals init: 1.0 enroll: - 0: + enroll_0: prob: 0.679 start: 0 stop: 999 diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index 3a42ad4e..6bd1d15b 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -309,7 +309,7 @@ demographics: min: 0.0 max: 1.0 enroll: - type: bin + type: definition description: "Probability that an agent in this class with HIV becomes enrolled in HAART at a given time step based on time since diagnosis (use 0 for haart cap)" fields: prob: @@ -323,7 +323,7 @@ demographics: type: int min: 1 default: - 0: + enroll_0: start: 0 stop: 999 prob: 0.0 From 5ca9c58288772a968a6ded8bc7cae14a0b5e23f5 Mon Sep 17 00:00:00 2001 From: sbessey Date: Tue, 19 Jan 2021 13:26:43 -0500 Subject: [PATCH 43/49] refactor(prep): change target to cap Use of "target" and "cap" were inconsistent between haart and prep. "Cap" is more accurate, as number of agents on prep/haart cannot go over this threshold BREAKING CHANGE: params naming change --- settings/atlanta/demographics.yml | 4 ++-- settings/chicago/chicago_baseCase.yml | 4 ++-- settings/mississippi/model.yml | 4 ++-- settings/nyc-msm/demographics.yml | 6 +++--- settings/nyc-msm/hiv.yml | 2 +- tests/features/prep_test.py | 8 ++++---- tests/features/random_trial_test.py | 6 +++--- tests/integration_test.py | 4 ++-- tests/model_test.py | 12 ++++++------ tests/params/basic.yml | 8 ++++---- tests/params/basic_seeded.yml | 8 ++++---- tests/params/basic_seeded_error.yml | 8 ++++---- tests/params/simple_integration.yml | 4 ++-- tests/population_io_test.py | 2 +- titan/features/prep.py | 10 +++++----- titan/features/random_trial.py | 2 +- titan/params/demographics.yml | 4 ++-- titan/params/location.yml | 2 +- titan/params/prep.yml | 8 ++++---- titan/utils.py | 4 ++-- 20 files changed, 55 insertions(+), 55 deletions(-) diff --git a/settings/atlanta/demographics.yml b/settings/atlanta/demographics.yml index 76b88d57..84238956 100644 --- a/settings/atlanta/demographics.yml +++ b/settings/atlanta/demographics.yml @@ -17,7 +17,7 @@ demographics: prep: discontinue: 0.0 adherence: 0.568 - target: 0.258 + cap: 0.258 init: 0.258 drug_type: NonInj: @@ -63,7 +63,7 @@ demographics: prob: 0.528 prep: adherence: 0.911 - target: 0.415 + cap: 0.415 discontinue: 0.0 init: 0.415 drug_type: diff --git a/settings/chicago/chicago_baseCase.yml b/settings/chicago/chicago_baseCase.yml index bd93bd51..dc37cf19 100644 --- a/settings/chicago/chicago_baseCase.yml +++ b/settings/chicago/chicago_baseCase.yml @@ -81,7 +81,7 @@ prep: type: - Oral - Inj - target: 0.088 + cap: 0.088 init: 0.088 start_time: 0 efficacy: @@ -102,7 +102,7 @@ demographics: prep: discontinue: 0.0 adherence: 0.568 - target: 0.5 + cap: 0.5 init: 0.5 high_risk: init: 0.0 diff --git a/settings/mississippi/model.yml b/settings/mississippi/model.yml index 4017c65c..9a9f2201 100644 --- a/settings/mississippi/model.yml +++ b/settings/mississippi/model.yml @@ -73,9 +73,9 @@ partnership: prep: type: - Oral - target: 0.049 + cap: 0.049 init: 0.25 - target_as_prob: true + cap_as_prob: true efficacy: adherent: 0.96 non_adherent: 0.83 diff --git a/settings/nyc-msm/demographics.yml b/settings/nyc-msm/demographics.yml index 0f5528cd..b866c00b 100644 --- a/settings/nyc-msm/demographics.yml +++ b/settings/nyc-msm/demographics.yml @@ -17,7 +17,7 @@ demographics: prep: discontinue: 0.034 # calibration adherence: 0.565 - target: 0.028 + cap: 0.028 init: 0.252 drug_type: None: @@ -94,7 +94,7 @@ demographics: prep: discontinue: 0.025 # calibration adherence: 0.719 - target: 0.026 + cap: 0.026 init: 0.234 drug_type: None: @@ -171,7 +171,7 @@ demographics: prep: discontinue: 0.014 adherence: 0.738 - target: 0.031 + cap: 0.031 init: 0.279 drug_type: None: diff --git a/settings/nyc-msm/hiv.yml b/settings/nyc-msm/hiv.yml index 42212def..89cfd555 100644 --- a/settings/nyc-msm/hiv.yml +++ b/settings/nyc-msm/hiv.yml @@ -9,7 +9,7 @@ hiv: prep: target_model: - Racial - target_as_prob: true + cap_as_prob: true start_time: -48 efficacy: adherent: 0.96 diff --git a/tests/features/prep_test.py b/tests/features/prep_test.py index aa1e632e..034b4bfb 100644 --- a/tests/features/prep_test.py +++ b/tests/features/prep_test.py @@ -199,7 +199,7 @@ def test_initiate_prep_eligible(make_model, make_agent): p.hiv.dx = True p.external_exposure.active = True model.time = 10 - a.location.params.prep.target = 1.0 + a.location.params.prep.cap = 1.0 a.location.params.prep.target_model = ["cdc_women"] rel = Relationship(a, p, 10, bond_type="Sex") # non-forcing, adherant, inj @@ -223,7 +223,7 @@ def test_initiate_prep_eligible_racial(make_model, make_agent): p.hiv.dx = True p.external_exposure.active = True model.time = 10 - a.location.params.prep.target = 1.0 + a.location.params.prep.cap = 1.0 a.location.params.prep.target_model = ["Racial"] rel = Relationship(a, p, 10, bond_type="Sex") # non-forcing, adherant, inj @@ -383,10 +383,10 @@ def test_progress_inj_prep(make_agent, params, make_model): @pytest.mark.unit -def test_target_as_prob(make_agent, make_model, params): +def test_cap_as_prob(make_agent, make_model, params): model = make_model(params) a = make_agent() - a.location.params.prep.target_as_prob = True + a.location.params.prep.cap_as_prob = True model.run_random = FakeRandom(0.2) a.prep.initiate(model) diff --git a/tests/features/random_trial_test.py b/tests/features/random_trial_test.py index 28a41e81..0bc2ffe5 100644 --- a/tests/features/random_trial_test.py +++ b/tests/features/random_trial_test.py @@ -10,7 +10,7 @@ def test_initialize_random_trial_prep_all(make_model, params): params.features.prep = True params.vaccine.on_init = False - params.prep.target = 0 + params.prep.cap = 0 params.random_trial.choice = "all" model = make_model(params) model.run_random = FakeRandom(-0.1) @@ -27,7 +27,7 @@ def test_initialize_random_trial_prep_all(make_model, params): def test_initialize_random_trial_prep_eigenvector(make_model, params): params.features.prep = True params.vaccine.on_init = False - params.prep.target = 0 + params.prep.cap = 0 params.hiv.start_time = 5 params.random_trial.choice = "eigenvector" model = make_model(params) @@ -53,7 +53,7 @@ def test_initialize_random_trial_prep_eigenvector(make_model, params): def test_initialize_random_trial_prep_random(make_model, params): params.features.prep = True params.vaccine.on_init = False - params.prep.target = 0 + params.prep.cap = 0 params.hiv.start_time = 5 params.random_trial.choice = "random" model = make_model(params) diff --git a/tests/integration_test.py b/tests/integration_test.py index 5bd6fe2f..9a857d0a 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -189,7 +189,7 @@ def test_prep_coverage(make_model_integration, tmpdir): model_a.run(path_a) # change the coverage upward for creating model b, use same seeds - model_a.params.prep.target = 0.9 + model_a.params.prep.cap = 0.9 model_a.params.prep.init = 0.9 model_a.params.model.seed.run = model_a.run_seed model_a.params.model.seed.ppl = model_a.pop.pop_seed @@ -197,7 +197,7 @@ def test_prep_coverage(make_model_integration, tmpdir): model_b = TITAN(model_a.params) model_b.run(path_b) print( - f"model b prep world target: {model_b.pop.geography.locations['world'].params.prep.target}" + f"model b prep world target: {model_b.pop.geography.locations['world'].params.prep.cap}" ) result_file_a = os.path.join(path_a, "basicReport.txt") diff --git a/tests/model_test.py b/tests/model_test.py index 36b4bbf7..e9ba2e52 100644 --- a/tests/model_test.py +++ b/tests/model_test.py @@ -141,21 +141,21 @@ def test_timeline_scaling_prep_def(make_model): model.params.timeline_scaling.timeline = ObjMap( { "scale": { - "parameter": "prep|target", + "parameter": "prep|cap", "start_time": 1, "stop_time": 3, "scalar": scalar, } } ) - original_prep_target = model.params.prep.target + original_prep_target = model.params.prep.cap # scale the param model.time = 1 model.timeline_scaling() assert math.isclose( - original_prep_target * scalar, model.params.prep.target, abs_tol=0.001 + original_prep_target * scalar, model.params.prep.cap, abs_tol=0.001 ) # param still scaled @@ -163,17 +163,17 @@ def test_timeline_scaling_prep_def(make_model): model.timeline_scaling() assert math.isclose( - original_prep_target * scalar, model.params.prep.target, abs_tol=0.001 + original_prep_target * scalar, model.params.prep.cap, abs_tol=0.001 ) # revert to original model.time = 3 model.timeline_scaling() - assert math.isclose(original_prep_target, model.params.prep.target, abs_tol=0.001) + assert math.isclose(original_prep_target, model.params.prep.cap, abs_tol=0.001) # still original model.time = 4 model.timeline_scaling() - assert math.isclose(original_prep_target, model.params.prep.target, abs_tol=0.001) + assert math.isclose(original_prep_target, model.params.prep.cap, abs_tol=0.001) diff --git a/tests/params/basic.yml b/tests/params/basic.yml index a6b876a6..b13719fe 100644 --- a/tests/params/basic.yml +++ b/tests/params/basic.yml @@ -97,7 +97,7 @@ demographics: prep: &base_prep adherence: 0.1 discontinue: 0.1 - target: 0.3 + cap: 0.3 init: 0.3 high_risk: &base_high_risk init: 0.1 @@ -116,7 +116,7 @@ demographics: prep: adherence: 0.1 discontinue: 0.1 - target: 0.3 + cap: 0.3 init: 0.5 high_risk: *base_high_risk vaccine: *base_vaccine @@ -279,9 +279,9 @@ partnership: black: *poisson_one prep: - target: 0.1 + cap: 0.1 init: 0.7 - target_as_prob: false + cap_as_prob: false knowledge: prob: 0.3 diff --git a/tests/params/basic_seeded.yml b/tests/params/basic_seeded.yml index cc8577f3..a6dc0cea 100644 --- a/tests/params/basic_seeded.yml +++ b/tests/params/basic_seeded.yml @@ -96,7 +96,7 @@ demographics: prep: &base_prep adherence: 0.1 discontinue: 0.1 - target: 0.3 + cap: 0.3 init: 0.3 high_risk: &base_high_risk init: 0.1 @@ -115,7 +115,7 @@ demographics: prep: adherence: 0.1 discontinue: 0.1 - target: 0.3 + cap: 0.3 init: 0.5 high_risk: *base_high_risk vaccine: *base_vaccine @@ -264,9 +264,9 @@ partnership: Social: *poisson_one prep: - target: 0.1 + cap: 0.1 init: 0.7 - target_as_prob: false + cap_as_prob: false knowledge: prob: 0.3 diff --git a/tests/params/basic_seeded_error.yml b/tests/params/basic_seeded_error.yml index 0a78f81b..373ec902 100644 --- a/tests/params/basic_seeded_error.yml +++ b/tests/params/basic_seeded_error.yml @@ -96,7 +96,7 @@ demographics: prep: &base_prep adherence: 0.1 discontinue: 0.1 - target: 0.3 + cap: 0.3 init: 0.3 high_risk: &base_high_risk init: 0.1 @@ -115,7 +115,7 @@ demographics: prep: adherence: 0.1 discontinue: 0.1 - target: 0.3 + cap: 0.3 init: 0.5 high_risk: *base_high_risk vaccine: *base_vaccine @@ -264,9 +264,9 @@ partnership: Social: *poisson_one prep: - target: 0.1 + cap: 0.1 init: 0.7 - target_as_prob: false + cap_as_prob: false knowledge: prob: 0.3 diff --git a/tests/params/simple_integration.yml b/tests/params/simple_integration.yml index 4bc57b3b..508af68f 100644 --- a/tests/params/simple_integration.yml +++ b/tests/params/simple_integration.yml @@ -33,7 +33,7 @@ demographics: prep: adherence: 0.9 discontinue: 0.1 - target: 0.3 + cap: 0.3 init: 0.3 high_risk: init: 0.1 @@ -176,7 +176,7 @@ partner_tracing: prob: 1 prep: - target: 0.1 + cap: 0.1 init: 0.1 start_time: -6 diff --git a/tests/population_io_test.py b/tests/population_io_test.py index 3c60b4b4..38a20894 100644 --- a/tests/population_io_test.py +++ b/tests/population_io_test.py @@ -96,7 +96,7 @@ def test_read_pop(tmpdir, make_population, params): @pytest.mark.unit def test_write_read_pop_compressed(tmpdir, make_population, params): - params.prep.target = 0.5 + params.prep.cap = 0.5 pop = make_population(n=10) prep_counts = deepcopy(Prep.counts) diff --git a/titan/features/prep.py b/titan/features/prep.py index 1e30e48e..6ff569c7 100644 --- a/titan/features/prep.py +++ b/titan/features/prep.py @@ -168,17 +168,17 @@ def initiate(self, model: "model.TITAN", force: bool = False): if force: self.enroll(model.run_random, model.time) - elif params.prep.target_as_prob: + 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.target + .prep.cap ): self.enroll(model.run_random, model.time) else: - if model.run_random.random() <= params.prep.target: + if model.run_random.random() <= params.prep.cap: self.enroll(model.run_random, model.time) else: if "Racial" in params.prep.target_model: @@ -191,12 +191,12 @@ def initiate(self, model: "model.TITAN", force: bool = False): 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.target + ].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.target + * params.prep.cap ) if num_prep_agents < target_prep: diff --git a/titan/features/random_trial.py b/titan/features/random_trial.py index 8ea8985a..72f420b6 100644 --- a/titan/features/random_trial.py +++ b/titan/features/random_trial.py @@ -152,7 +152,7 @@ def suitable_prep(agent, model) -> bool: if ( not agent.hiv.active and not agent.prep.active - and model.run_random.random() < agent.location.params.prep.target + and model.run_random.random() < agent.location.params.prep.cap and not agent.vaccine.active ): return True diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index 6bd1d15b..4e3cab8e 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -212,9 +212,9 @@ demographics: type: float min: 0.0 max: 1.0 - target: + cap: default: 0.0 - description: "Target percentage of PrEP coverage for this population class, or probability of going on PrEP if target_as_prob is true" + description: "Ceiling/target percentage of PrEP coverage for this population class, or probability of going on PrEP if cap_as_prob is true" type: float min: 0.0 max: 1.0 diff --git a/titan/params/location.yml b/titan/params/location.yml index cfb920e1..036675c2 100644 --- a/titan/params/location.yml +++ b/titan/params/location.yml @@ -6,7 +6,7 @@ location: - locations default: type: definition - description: "Definition of applying a scalar to a parameter. The parameter should be the definition name with pipes instead of dots (e.g. `prep.target` would be `prep|target`). Some parameter categories can't be scaled, these include: model, classes, features, calibration, outputs, agent_zero, syringe_services, location. Additionally, some fields within otherwise scalable categories cannot be changed by location if their use is not tied to a specific agent (e.g. prep.pca.choice, prep.random_trial). When a parameter is related to the interaction of an agent and a partner, the params of the location where the agent lives is typically used unless the parameter use clearly relates to the partner." + description: "Definition of applying a scalar to a parameter. The parameter should be the definition name with pipes instead of dots (e.g. `prep.cap` would be `prep|cap`). Some parameter categories can't be scaled, these include: model, classes, features, calibration, outputs, agent_zero, syringe_services, location. Additionally, some fields within otherwise scalable categories cannot be changed by location if their use is not tied to a specific agent (e.g. prep.pca.choice, prep.random_trial). When a parameter is related to the interaction of an agent and a partner, the params of the location where the agent lives is typically used unless the parameter use clearly relates to the partner." fields: field: type: enum diff --git a/titan/params/prep.yml b/titan/params/prep.yml index c1fc26d6..609a5f0d 100644 --- a/titan/params/prep.yml +++ b/titan/params/prep.yml @@ -7,9 +7,9 @@ prep: - Inj description: Type of PrEP used in model type: array - target: + cap: default: 0.0 - description: Target coverage for PrEP coverage, or probability of initiating prep at a timestep if target_as_prob is true + description: Ceiling/target coverage for PrEP coverage, or probability of initiating prep at a timestep if cap_as_prob is true type: float min: 0 max: 1 @@ -19,9 +19,9 @@ prep: type: float min: 0. max: 1. - target_as_prob: + cap_as_prob: default: false - description: If prep.target should be treated as a probability instead of a target + description: If prep.cap should be treated as a probability instead of a cap type: boolean start_time: default: 0 diff --git a/titan/utils.py b/titan/utils.py index 6de0d151..b218df56 100644 --- a/titan/utils.py +++ b/titan/utils.py @@ -176,7 +176,7 @@ def get_param_from_path(params, param_path, delimiter): def scale_param(params, param_path, scalar, delimiter="|"): """ - Given the params and a parameter path in the format prep|target, scale the + Given the params and a parameter path in the format prep|cap, scale the current value by the scalar """ scaling_item, last_key = get_param_from_path(params, param_path, delimiter) @@ -188,7 +188,7 @@ def scale_param(params, param_path, scalar, delimiter="|"): def override_param(params, param_path, value, delimiter="|"): """ - Given the params and a parameter path in the format prep|target, change the + Given the params and a parameter path in the format prep|cap, change the current value to new value """ override_item, last_key = get_param_from_path(params, param_path, delimiter) From 5d8b7ada50523baca268d1236430daa6ceb1aa7b Mon Sep 17 00:00:00 2001 From: sbessey Date: Tue, 19 Jan 2021 14:19:37 -0500 Subject: [PATCH 44/49] fix: type ignore in hiv --- titan/exposures/hiv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titan/exposures/hiv.py b/titan/exposures/hiv.py index 40dc2240..8f5a13e8 100644 --- a/titan/exposures/hiv.py +++ b/titan/exposures/hiv.py @@ -326,7 +326,7 @@ def progress_to_aids(self, model: "model.TITAN"): """ aids_params = self.agent.location.params.hiv.aids p = 1.0 - self.agent.haart.aids_scale() + self.agent.haart.aids_scale() # type: ignore[attr-defined] if model.run_random.random() < p * aids_params.prob: self.aids = True From f25a285ebb49a4294a21497a7ad9566b76800458 Mon Sep 17 00:00:00 2001 From: sbessey Date: Tue, 19 Jan 2021 16:29:40 -0500 Subject: [PATCH 45/49] refactor: change names/descriptions and remove unnecessary intermediate variable --- settings/nyc-msm/hiv.yml | 2 +- tests/features/haart_test.py | 6 ++++-- tests/integration_test.py | 3 ++- tests/params/basic.yml | 2 +- titan/exposures/hiv.py | 2 +- titan/features/haart.py | 19 ++++++++++--------- titan/params/demographics.yml | 2 +- titan/params/haart.yml | 2 +- 8 files changed, 21 insertions(+), 17 deletions(-) diff --git a/settings/nyc-msm/hiv.yml b/settings/nyc-msm/hiv.yml index 89cfd555..73445206 100644 --- a/settings/nyc-msm/hiv.yml +++ b/settings/nyc-msm/hiv.yml @@ -16,4 +16,4 @@ prep: non_adherent: 0.76 haart: - reinit: true + use_reinit: true diff --git a/tests/features/haart_test.py b/tests/features/haart_test.py index d728b1f1..a9694dfd 100644 --- a/tests/features/haart_test.py +++ b/tests/features/haart_test.py @@ -72,7 +72,7 @@ def test_update_haart_dx_time(make_model, make_agent): a.hiv.active = True a.hiv.dx = True - a.hiv.dx_time = 1 # agent dx duration is 0 + a.hiv.dx_time = model.time # agent dx duration is 0 a.haart.update_agent(model) assert not a.haart.active @@ -93,6 +93,8 @@ def test_update_haart_reinit(make_model, make_agent): a.haart.update_agent(model) assert not a.haart.active - model.run_random = FakeRandom(-1.1) + a.location.params.demographics[a.race].sex_type[a.sex_type].drug_type[ + a.drug_type + ].haart.reinit.prob = 1.0 a.haart.update_agent(model) assert a.haart.active diff --git a/tests/integration_test.py b/tests/integration_test.py index 9a857d0a..4c1f6b02 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -197,7 +197,7 @@ def test_prep_coverage(make_model_integration, tmpdir): model_b = TITAN(model_a.params) model_b.run(path_b) print( - f"model b prep world target: {model_b.pop.geography.locations['world'].params.prep.cap}" + f"model b prep world cap: {model_b.pop.geography.locations['world'].params.prep.cap}" ) result_file_a = os.path.join(path_a, "basicReport.txt") @@ -243,6 +243,7 @@ def test_syringe_services(params_integration, tmpdir): """ params_integration.demographics.black.sex_type.MSM.drug_type.Inj.ppl = 1.0 params_integration.demographics.black.sex_type.MSM.drug_type["None"].ppl = 0.0 + params_integration.model.num_pop = 500 model_a = TITAN(params_integration) model_a.params.partnership.sex.frequency.Sex = ( ObjMap( # turn off sex to show only injection effects diff --git a/tests/params/basic.yml b/tests/params/basic.yml index b13719fe..de5970c0 100644 --- a/tests/params/basic.yml +++ b/tests/params/basic.yml @@ -314,7 +314,7 @@ exposures: knowledge: true haart: - reinit: true + use_reinit: true features: incar: true diff --git a/titan/exposures/hiv.py b/titan/exposures/hiv.py index 8f5a13e8..db125cd5 100644 --- a/titan/exposures/hiv.py +++ b/titan/exposures/hiv.py @@ -326,7 +326,7 @@ def progress_to_aids(self, model: "model.TITAN"): """ aids_params = self.agent.location.params.hiv.aids p = 1.0 - self.agent.haart.aids_scale() # type: ignore[attr-defined] + p = self.agent.haart.aids_scale() # type: ignore[attr-defined] if model.run_random.random() < p * aids_params.prob: self.aids = True diff --git a/titan/features/haart.py b/titan/features/haart.py index fad961d7..22191854 100644 --- a/titan/features/haart.py +++ b/titan/features/haart.py @@ -105,14 +105,15 @@ def update_agent(self, model: "model.TITAN"): if num_haart_agents < (haart_params.cap * num_dx_agents): self.initiate(model) else: - if self.ever and self.agent.location.params.haart.reinit: + if self.ever and self.agent.location.params.haart.use_reinit: if model.run_random.random() < haart_params.reinit.prob: self.initiate(model) else: enroll_prob = 0 # Find enroll probability based on time since diagnosis + haart_duration = model.time - self.agent.hiv.dx_time # type: ignore[attr-defined] for i in haart_params.enroll.values(): - if i.start <= (model.time - self.agent.hiv.dx_time) < i.stop: # type: ignore[attr-defined] + if i.start <= haart_duration < i.stop: # type: ignore[attr-defined] enroll_prob = i.prob break @@ -121,20 +122,21 @@ def update_agent(self, model: "model.TITAN"): ): self.initiate(model) - # Go off HAART - elif self.active: + # Update agents on HAART + else: + # Go off HAART if model.run_random.random() < haart_params.discontinue: self.active = False self.adherent = False self.remove_agent(self.agent) - elif ( + elif ( # Become non-adherent self.adherent and model.run_random.random() < haart_params.adherence.discontinue ): self.adherent = False - elif ( + elif ( # Become adherent not self.adherent - and model.run_random.random() < haart_params.adherence.prob + and model.run_random.random() < haart_params.adherence.become ): self.adherent = True @@ -197,9 +199,8 @@ def get_transmission_risk_multiplier(self, time: int, interaction_type: str): def aids_scale(self): prob = 1.0 if self.active: - params = self.agent.location.params adherence = "adherent" if self.adherent else "non_adherent" - prob = params.haart.aids_scale[adherence] + prob = self.agent.location.params.haart.aids_scale[adherence] return prob diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index 4e3cab8e..3e564d28 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -310,7 +310,7 @@ demographics: max: 1.0 enroll: type: definition - description: "Probability that an agent in this class with HIV becomes enrolled in HAART at a given time step based on time since diagnosis (use 0 for haart cap)" + description: "Probability that an agent in this class with HIV becomes enrolled in HAART given the agent's time since diagnosis (unused if use_cap is true)" fields: prob: type: float diff --git a/titan/params/haart.yml b/titan/params/haart.yml index e62883f8..036719ee 100644 --- a/titan/params/haart.yml +++ b/titan/params/haart.yml @@ -3,7 +3,7 @@ haart: default: false description: Whether "prob" defined in demographics.race.sex_type.haart is a percent of diagnosed, HIV+ agents instead of a per-time-step probability for HAART coverage type: boolean - reinit: + use_reinit: type: boolean default: false description: Whether a separate probability should be used for haart initiation among agents who have previously discontinued haart From 74a947b2377e6a93613bcdfd13d4d9feeef9d510 Mon Sep 17 00:00:00 2001 From: sbessey Date: Wed, 20 Jan 2021 10:06:17 -0500 Subject: [PATCH 46/49] style: fix comments and remove unnecessary type ignore --- titan/exposures/hiv.py | 1 - titan/features/haart.py | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/titan/exposures/hiv.py b/titan/exposures/hiv.py index db125cd5..19097c93 100644 --- a/titan/exposures/hiv.py +++ b/titan/exposures/hiv.py @@ -325,7 +325,6 @@ def progress_to_aids(self, model: "model.TITAN"): model: the running model """ aids_params = self.agent.location.params.hiv.aids - p = 1.0 p = self.agent.haart.aids_scale() # type: ignore[attr-defined] if model.run_random.random() < p * aids_params.prob: diff --git a/titan/features/haart.py b/titan/features/haart.py index 22191854..a1a7acc7 100644 --- a/titan/features/haart.py +++ b/titan/features/haart.py @@ -113,7 +113,7 @@ def update_agent(self, model: "model.TITAN"): # Find enroll probability based on time since diagnosis haart_duration = model.time - self.agent.hiv.dx_time # type: ignore[attr-defined] for i in haart_params.enroll.values(): - if i.start <= haart_duration < i.stop: # type: ignore[attr-defined] + if i.start <= haart_duration < i.stop: enroll_prob = i.prob break @@ -129,12 +129,14 @@ def update_agent(self, model: "model.TITAN"): self.active = False self.adherent = False self.remove_agent(self.agent) - elif ( # Become non-adherent + # Become non-adherent + elif ( self.adherent and model.run_random.random() < haart_params.adherence.discontinue ): self.adherent = False - elif ( # Become adherent + # Become adherent + elif ( not self.adherent and model.run_random.random() < haart_params.adherence.become ): From 825b5d947ff28a9e8c097d27b7430918c756391c Mon Sep 17 00:00:00 2001 From: sbessey Date: Wed, 20 Jan 2021 10:13:07 -0500 Subject: [PATCH 47/49] docs(demographics): correct description for haart.adherence.become --- titan/params/demographics.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index 3e564d28..30300723 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -353,7 +353,7 @@ demographics: max: 1.0 become: default: 0.0 - description: "Probability that an agent already on haart will become adherent at a given timestep" + description: "Probability that a non-adherent agent will become adherent at a given timestep" type: float min: 0.0 max: 1.0 From f821d8553111f23789b63bc60ebb68469d6bf585 Mon Sep 17 00:00:00 2001 From: sbessey Date: Wed, 20 Jan 2021 10:20:38 -0500 Subject: [PATCH 48/49] docs: correct description of haart.use_cap --- titan/params/haart.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titan/params/haart.yml b/titan/params/haart.yml index 036719ee..02a4c8c3 100644 --- a/titan/params/haart.yml +++ b/titan/params/haart.yml @@ -1,7 +1,7 @@ haart: use_cap: default: false - description: Whether "prob" defined in demographics.race.sex_type.haart is a percent of diagnosed, HIV+ agents instead of a per-time-step probability for HAART coverage + description: Whether "cap" defined in demographics.race.sex_type.haart is used. Otherwise, haart probability is based on time since diagnosis. type: boolean use_reinit: type: boolean From 5f53044b92e24a7dedcbec49549920eb945a733e Mon Sep 17 00:00:00 2001 From: sbessey Date: Wed, 20 Jan 2021 10:26:16 -0500 Subject: [PATCH 49/49] docs: further clarify haart.use_cap desc --- titan/params/haart.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/titan/params/haart.yml b/titan/params/haart.yml index 02a4c8c3..4aec7ba3 100644 --- a/titan/params/haart.yml +++ b/titan/params/haart.yml @@ -1,7 +1,7 @@ haart: use_cap: default: false - description: Whether "cap" defined in demographics.race.sex_type.haart is used. Otherwise, haart probability is based on time since diagnosis. + description: Whether "cap" defined in demographics.race.sex_type.haart.cap is used. Otherwise, haart probability is based on time since diagnosis (demographics.race.sex_type.haart.enroll). type: boolean use_reinit: type: boolean