diff --git a/pyproject.toml b/pyproject.toml index d4cd32e4..49192653 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "titan-model" -version = "3.1.0" +version = "3.2.0" description = "TITAN Agent Based Model" license = "GPL-3.0-only" authors = ["Sam Bessey ", "Mary McGrath "] diff --git a/tests/exposures/monkeypox_test.py b/tests/exposures/monkeypox_test.py new file mode 100644 index 00000000..e16bca97 --- /dev/null +++ b/tests/exposures/monkeypox_test.py @@ -0,0 +1,135 @@ +import pytest + +from conftest import FakeRandom + +from titan.exposures import MonkeyPox +from titan.agent import Relationship + + +@pytest.mark.unit +def test_monkeypox_expose(make_model, make_agent): + model = make_model() + model.run_random = FakeRandom(0.0) # always less than param + a = make_agent() + p = make_agent() + a.partners["Sex"] = set() + p.partners["Sex"] = set() + rel = Relationship(a, p, 10, bond_type="Sex") + + MonkeyPox.expose(model, "sex", rel, 1) + + assert a.monkeypox.active is False + assert p.monkeypox.active is False + + a.monkeypox.active = True + a.monkeypox.time = model.time + p.monkeypox.active = True + a.monkeypox.time = model.time + + # test nothing happens + MonkeyPox.expose(model, "sex", rel, 10) + + assert a.monkeypox.active + assert p.monkeypox.active + + p.monkeypox.active = False + + # test conversion happens + MonkeyPox.expose(model, "sex", rel, 10) + + assert a.monkeypox.active + assert p.monkeypox.active + + +@pytest.mark.unit +def test_monkeypox_init(make_population, make_agent): + pop = make_population() + pop.pop_random = FakeRandom(0.0) + a = make_agent() + + time = pop.params.monkeypox.start_time - 1 + + a.monkeypox.init_agent(pop, time) + assert a.monkeypox.active is False + + time = pop.params.monkeypox.start_time + + a.monkeypox.init_agent(pop, time) + + assert a.monkeypox.active + assert a.monkeypox.time == time - pop.params.monkeypox.max_init_time + assert a.monkeypox.dx + assert a.monkeypox.dx_time == a.monkeypox.time + assert a in MonkeyPox.agents + assert MonkeyPox.dx_counts[a.race][a.sex_type] == 1 + + +@pytest.mark.unit +def test_monkeypox_convert(make_model, make_agent): + model = make_model() + a = make_agent() + + model.run_random = FakeRandom(-0.1) + + a.monkeypox.convert(model) + + assert a.monkeypox.active + assert a.monkeypox.time == model.time + assert a in MonkeyPox.agents + + +# TODO talk with Ellen about why this is failing? +@pytest.mark.unit +def test_diagnose_monkeypox(make_model, make_agent): + model = make_model() + model.params.partner_tracing.prob = 1.0 + model.time = 1 + a = make_agent() + p = make_agent() + p.monkeypox.active = True + a.partners["Sex"].add(p) + + model.run_random = FakeRandom(-0.1) # always less than param + a.monkeypox.diagnose(model) + + assert a.monkeypox.dx + assert a.monkeypox.dx_time == model.time + + +@pytest.mark.unit +def test_get_transmission_probability(make_model, make_agent): + model = make_model() + a = make_agent(race="white", SO="MSM") + a.sex_role = "versatile" + + p = make_agent(race="white", SO="MSM") + p.sex_role = "versatile" + + # test versatile-versatile relationship + p_sex = model.params.partnership.sex.acquisition.MSM.versatile + scale = model.params.calibration.acquisition + + # must be in acute phase + a.monkeypox.active = True + a.monkeypox.time = model.time + + assert a.monkeypox.get_transmission_probability(model, "sex", p, 1) == p_sex * scale + # Not transmissable with injection + assert a.monkeypox.get_transmission_probability(model, "inj", p, 1) == 0.0 + + # test one vers, one receptive + a.sex_role = "receptive" + p_sex_ins = model.params.partnership.sex.acquisition.MSM.insertive + p_sex_rec = model.params.partnership.sex.acquisition.MSM.receptive + + assert a.hiv.get_transmission_probability(model, "sex", p, 1) == p_sex_ins * scale + assert p.hiv.get_transmission_probability(model, "sex", a, 1) == p_sex_rec * scale + + +@pytest.mark.unit +def test_get_acute_status(make_agent, make_model): + model = make_model() + a = make_agent() + assert a.monkeypox.get_acute_status(model.time + 2) is False + a.monkeypox.convert(model) + assert a.monkeypox.get_acute_status(model.time + 2) is True diff --git a/tests/features/vaccine_test.py b/tests/features/vaccine_test.py index db8c1162..fcba300a 100644 --- a/tests/features/vaccine_test.py +++ b/tests/features/vaccine_test.py @@ -64,3 +64,29 @@ def test_vaccine_cquisition_risk_multiplier(make_agent): a.vaccine.vaccinate(0) assert a.vaccine.get_acquisition_risk_multiplier(1, "sex") < 1.0 + + +@pytest.mark.unit +def test_vaccine_HVTN(make_agent): + a = make_agent() + print(a.location.params.vaccine.type) + a.location.params.vaccine.type = "HVTN702" + assert a.vaccine.get_acquisition_risk_multiplier(0, "sex") == 1.0 + + a.vaccine.vaccinate(0) + assert a.vaccine.get_acquisition_risk_multiplier(1, "sex") < 1.0 + + +@pytest.mark.unit +def test_vaccine_other(make_agent): + a = make_agent() + print(a.location.params.vaccine.type) + a.location.params.vaccine.type = "other" + assert a.vaccine.get_acquisition_risk_multiplier(0, "sex") == 1.0 + + a.vaccine.vaccinate(0) + # no risk multiplier + assert a.vaccine.get_acquisition_risk_multiplier(1, "sex") == 1.0 + + a.location.params.vaccine.efficacy = 0.5 + assert a.vaccine.get_acquisition_risk_multiplier(1, "sex") == 0.5 diff --git a/tests/integration_test.py b/tests/integration_test.py index edf54a39..760f65c8 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -103,6 +103,7 @@ def test_model_reproducible(tmpdir): assert res_a[i]["pseed"] == res_b[i]["pseed"] assert res_a[i]["agents"] == res_b[i]["agents"] assert res_a[i]["hiv"] == res_b[i]["hiv"] + assert res_a[i]["monkeypox"] == res_b[i]["monkeypox"] assert res_a[i]["prep"] == res_b[i]["prep"] assert res_a[i]["death"] == res_b[i]["death"] assert res_a[i]["hiv_aids"] == res_b[i]["hiv_aids"] diff --git a/tests/params/basic.yml b/tests/params/basic.yml index 60443957..586b8141 100644 --- a/tests/params/basic.yml +++ b/tests/params/basic.yml @@ -44,6 +44,11 @@ demographics: prob: 0.1 aids: init: 0.1 + monkeypox: + init: 0.1 + dx: + init: 0.1 + prob: 0.1 haart: init: 0.1 enroll: &haart_prob @@ -75,6 +80,11 @@ demographics: prob: 0.1 aids: init: 0.1 + monkeypox: + init: 0.1 + dx: + init: 0.1 + prob: 0.1 num_partners: Sex: dist_type: poisson @@ -324,6 +334,7 @@ outputs: exposures: hiv: true knowledge: true + monkeypox: true haart: use_reinit: true diff --git a/tests/params/basic_seeded.yml b/tests/params/basic_seeded.yml index ff1c4066..17830e70 100644 --- a/tests/params/basic_seeded.yml +++ b/tests/params/basic_seeded.yml @@ -39,6 +39,11 @@ demographics: prob: 0.1 aids: init: 0.1 + monkeypox: + init: 0.1 + dx: + init: 0.1 + prob: 0.1 haart: init: 0.1 enroll: @@ -59,6 +64,11 @@ demographics: prob: 0.1 aids: init: 0.1 + monkeypox: + init: 0.1 + dx: + init: 0.1 + prob: 0.1 num_partners: Sex: dist_type: poisson @@ -296,6 +306,7 @@ outputs: exposures: hiv: true + monkeypox: true knowledge: true features: diff --git a/tests/population_test.py b/tests/population_test.py index dab36d8c..745e2bac 100644 --- a/tests/population_test.py +++ b/tests/population_test.py @@ -94,6 +94,9 @@ def test_add_remove_agent_to_pop(make_population): agent.hiv.aids = True agent.hiv.dx = True agent.hiv.add_agent(agent) + agent.monkeypox.active = True + agent.monkeypox.dx = True + agent.monkeypox.add_agent(agent) pop.add_agent(agent) diff --git a/titan/agent.py b/titan/agent.py index 7b8db7e4..10e38250 100644 --- a/titan/agent.py +++ b/titan/agent.py @@ -88,7 +88,7 @@ def __str__(self) -> str: """ return ( f"\t{self.id}\t{self.age}\t{self.sex_type}\t{self.drug_type}\t" # type: ignore[attr-defined] - f"{self.race}\t{self.hiv.active}" # type: ignore[attr-defined] + f"{self.race}\t{self.hiv.active}\t{self.prep.active}" # type: ignore[attr-defined] ) def __repr__(self) -> str: diff --git a/titan/exposures/__init__.py b/titan/exposures/__init__.py index 4b8e687b..0f7718e4 100644 --- a/titan/exposures/__init__.py +++ b/titan/exposures/__init__.py @@ -2,3 +2,4 @@ from .base_exposure import * from .hiv import * from .knowledge import * +from .monkeypox import MonkeyPox diff --git a/titan/exposures/monkeypox.py b/titan/exposures/monkeypox.py new file mode 100644 index 00000000..230fd843 --- /dev/null +++ b/titan/exposures/monkeypox.py @@ -0,0 +1,298 @@ +from typing import List, Dict, Optional, Set + +from . import base_exposure +from .. import agent +from .. import population +from .. import model +from .. import utils + + +class MonkeyPox(base_exposure.BaseExposure): + + name: str = "monkeypox" + stats: List[str] = [ + "monkeypox", + "monkeypox_dx", + "monkeypox_new", + "monkeypox_dx_new", + ] + """ + MonkeyPox collects the following stats: + + * monkeypox - number of agents with hx of monkeypox + * monkeypox_dx - number of agents ever diagnosed with monkeypox + * monkeypox_new - number of agents converted to monkeypox this timestep + * monkeypox_dx_new - number of agents with diagnosed with monkeypox this timestep + """ + + dx_counts: Dict[str, Dict[str, int]] = {} + """Counts of diagnosed agents by race and sex_type""" + + agents: Set["agent.Agent"] = set() + """Agents who have ever had monkeypox""" + + def __init__(self, agent: "agent.Agent"): + super().__init__(agent) + + self.active = False + self.time: Optional[int] = None + self.dx = False + self.dx_time: Optional[int] = None + + @classmethod + def init_class(cls, params): + """ + Initialize any diagnosis counts and the agents set. + + args: + params: parameters for this population + """ + cls.dx_counts = { + race: {so: 0 for so in params.classes.sex_types} + for race in params.classes.races + } + cls.agents = set() + + def init_agent(self, pop: "population.Population", time: int): + """ + Initialize the agent for this exposure during population initialization (`Population.create_agent`). Called on only exposures that are enabled per the params. + + Based on demographic params for the agent, stochastically determine if monkeypox is active, and if active, at what past time point was the agent converted, if the agent is diagnosed, and if the agent has aids. + + args: + pop: the population this agent is a part of + time: the current time step + """ + agent_params = ( + self.agent.location.params.demographics[self.agent.race] + .sex_type[self.agent.sex_type] + .drug_type[self.agent.drug_type] + ) + + # Monkeypox + if ( + pop.pop_random.random() < agent_params.monkeypox.init + and time >= pop.params.monkeypox.start_time + ): + self.active = True + + # if monkeypox, when did the agent convert? Random sample + self.time = utils.safe_random_int( + time - self.agent.location.params.monkeypox.max_init_time, + time, + pop.pop_random, + ) + + if pop.pop_random.random() < agent_params.monkeypox.dx.init: + self.dx = True + # agent was diagnosed at a random time between conversion and now + self.dx_time = utils.safe_random_int(self.time, time, pop.pop_random) + + # add agent to class + self.add_agent(self.agent) + + def update_agent(self, model: "model.TITAN"): + """ + Update the agent for this exposure for a time step. Called once per time step in `TITAN.update_all_agents`. Agent level updates are done after population level updates. Called on only exposures that are enabled per the params. + + If the agent is monkeypox+ and the model time is past the monkeypox start_time, determine if the agent becomes diagnosed if not yet diagnosed. + + args: + model: the instance of TITAN currently being run + """ + if self.active and model.time >= model.params.monkeypox.start_time: + if not self.dx: + test_prob = ( + self.agent.location.params.demographics[self.agent.race] + .sex_type[self.agent.sex_type] + .drug_type[self.agent.drug_type] + .monkeypox.dx.prob + ) + + # Rescale based on calibration param + test_prob *= model.calibration.test_frequency + + if model.run_random.random() < test_prob: + self.diagnose(model) + + @classmethod + def add_agent(cls, agent: "agent.Agent"): + """ + Add an agent to the class (not instance). This can be useful if tracking population level statistics or groups, such as counts or newly active agents. + + Add the agent to the `agents` set and if the agent is diagnosed, updated the `dx_counts` + + args: + agent: the agent to add to the class attributes + """ + cls.agents.add(agent) + + if agent.monkeypox.dx: # type: ignore[attr-defined] + cls.dx_counts[agent.race][agent.sex_type] += 1 + + @classmethod + def remove_agent(cls, agent: "agent.Agent"): + """ + Remove an agent from the class (not instance). This can be useful if tracking population level statistics or groups, such as counts. + + Remove the agent from the `agents` set and decrement the `dx_counts` if the agent was diagnosed. + + args: + agent: the agent to remove from the class attributes + """ + cls.agents.remove(agent) + + if agent.monkeypox.dx: # type: ignore[attr-defined] + cls.dx_counts[agent.race][agent.sex_type] -= 1 + + def set_stats(self, stats: Dict[str, int], time: int): + if self.active: + stats["monkeypox"] += 1 + if self.time == time: + stats["monkeypox_new"] += 1 + if self.dx: + stats["monkeypox_dx"] += 1 + if self.dx_time == time: + stats["monkeypox_dx_new"] += 1 + + @staticmethod + def expose( + model: "model.TITAN", + interaction: str, + rel: "agent.Relationship", + num_acts: int, + ): + """ + Expose a relationship to the exposure for a number of acts of a specific interaction type. Typically, this is determining if the exposure can cause conversion/change in one of the agents, then if so determining the probability of that and then converting the succeptible agent. + + For monkeypox, one agent must be active and the other not for an exposure to cause conversion. + + args: + model: The running model + interaction: The type of interaction (e.g. sex, injection) + rel: The relationship where the interaction is occuring + num_acts: The number of acts of that interaction + """ + # Agent 1 is monkeypox+, Agent 2 is not, Agent 2 is succept + if rel.agent1.monkeypox.active and not rel.agent2.monkeypox.active: # type: ignore[attr-defined] + agent = rel.agent1 + partner = rel.agent2 + # If Agent 2 is monkeypox+ and Agent 1 is not, Agent 1 is succept + elif not rel.agent1.monkeypox.active and rel.agent2.monkeypox.active: # type: ignore[attr-defined] + agent = rel.agent2 + partner = rel.agent1 + else: # neither agent is monkeypox+ or both are + return + + p = agent.monkeypox.get_transmission_probability( # type: ignore[attr-defined] + model, interaction, partner, num_acts + ) + + if model.run_random.random() < p: + # if agent monkeypox+ partner becomes monkeypox+ + partner.monkeypox.convert(model) # type: ignore[attr-defined] + + def get_transmission_probability( + self, + model: "model.TITAN", + interaction: str, + partner: "agent.Agent", + num_acts: int, + ) -> float: + """ + Determines the probability of an monkeypox transmission event from agent to partner based on + interaction type and numer of acts. For sex acts, transmission probability is a + function of the acquisition probability of the monkeypox- agent's sex role + and the monkeypox+ agent's haart adherence, acute status, and dx risk reduction + + args: + model: The running model + interaction : "injection" or "sex" + partner: monkeypox- Agent + num_acts: The number of exposure interactions the agents had this time step + + returns: + probability of transmission from agent to partner + """ + # if this isn't an interaction where monkeypox can transmit, return 0% prob + if interaction not in ("sex") or not self.get_acute_status(model.time): + return 0.0 + + # Logic for if needle or sex type interaction + p: float + + # get partner's sex role during acts + partner_sex_role = "versatile" + + # get probability of sex acquisition given monkeypox- partner's position + p = partner.location.params.partnership.sex.acquisition[partner.sex_type][ + partner_sex_role + ] + + # feature specific risk adjustment + for feature in model.features: + agent_feature = getattr(self.agent, feature.name) + p *= agent_feature.get_transmission_risk_multiplier(self.time, interaction) + + partner_feature = getattr(partner, feature.name) + p *= partner_feature.get_acquisition_risk_multiplier(self.time, interaction) + + # Scaling parameter for positively identified monkeypox agents + if self.dx: + p *= 1 - self.agent.location.params.monkeypox.dx.risk_reduction[interaction] + + # Racial calibration parameter to attain proper race incidence disparity + p *= partner.location.params.demographics[partner.race].monkeypox.transmission + + # Scaling parameter for per act transmission. + p *= model.calibration.acquisition + + return utils.total_probability(p, num_acts) + + def convert(self, model: "model.TITAN"): + """ + Agent becomes monkeypox agent. Update all appropriate attributes, sets and dictionaries. + + args: + model: The model being run + """ + if not self.active: + self.active = True + self.time = model.time + self.agent.vaccine.active = False # type: ignore[attr-defined] + self.add_agent(self.agent) + + def diagnose(self, model: "model.TITAN"): + """ + Mark the agent as diagnosed. + + args: + model: the running model + """ + self.dx = True + self.dx_time = model.time + self.add_agent(self.agent) + + # ============================ HELPER METHODS ============================== + + def get_acute_status(self, time: int) -> bool: + """ + Get acute status of agent at time + + args: + time: The current time step + + returns: + whether an agent is acute + """ + if self.active and self.time is not None: + monkeypox_duration = time - self.time + + if ( + self.agent.location.params.monkeypox.acute.duration + >= monkeypox_duration + >= 0 + ): + return True + + return False diff --git a/titan/features/vaccine.py b/titan/features/vaccine.py index c641c4f2..0a41c6a3 100644 --- a/titan/features/vaccine.py +++ b/titan/features/vaccine.py @@ -102,6 +102,8 @@ def get_acquisition_risk_multiplier(self, time: int, interaction_type: str): ) elif self.type == "RV144": return np.exp(-2.40 + 0.76 * (np.log(vaccine_time_months))) + else: + return 1 - self.agent.location.params.vaccine.efficacy return 1.0 diff --git a/titan/model.py b/titan/model.py index 7f7aeac7..57aa2c0d 100644 --- a/titan/model.py +++ b/titan/model.py @@ -387,6 +387,7 @@ def exit(self): # agent ages out of model if agent.age > exit.age: self.exits[strategy.exit_class].append(agent) + break elif case == "death": p = ( prob.get_death_rate( @@ -406,6 +407,7 @@ def exit(self): if self.run_random.random() < p: # agent dies self.exits[strategy.exit_class].append(agent) + break elif case == "drop_out": p = ( agent.location.params.demographics[agent.race] @@ -417,6 +419,7 @@ def exit(self): if self.run_random.random() < p: # agent leaves study pop self.exits[strategy.exit_class].append(agent) + break for exit_list in self.exits.values(): for agent in exit_list: diff --git a/titan/output.py b/titan/output.py index c0f4bf87..15f876a9 100644 --- a/titan/output.py +++ b/titan/output.py @@ -296,7 +296,8 @@ def print_components( # if this is a new file, write the header info if f.tell() == 0: f.write( - "run_id\trunseed\tpopseed\tt\tcomponent" + "run_id\trunseed\tpopseed\tt\tcomponent\tagents" + "\tinfected\ttrt\ttrtinfected" "\tdensity\tEffectiveSize\tdeg_cent\n" ) @@ -307,9 +308,19 @@ def print_components( comp_density = nx.density(comp) deg_cent = mean(list(nx.degree_centrality(comp).values())) + nhiv = ntrthiv = ntrt = 0 + for agent in comp.nodes(): + if agent.hiv.active: + nhiv += 1 + if agent.random_trial.treated: + ntrthiv += 1 + + if agent.random_trial.treated: + ntrt += 1 f.write( - f"{run_id}\t{runseed}\t{popseed}\t{t}\t{id}\t" + f"{run_id}\t{runseed}\t{popseed}\t{t}\t{id}\t{num_nodes}" + f"\t{nhiv}\t{ntrt}\t{ntrthiv}" f"\t{comp_density:.4f}" f"\t{average_size:.4f}\t{deg_cent}\n" ) diff --git a/titan/params/agent_zero.yml b/titan/params/agent_zero.yml index 0b00d478..545be139 100644 --- a/titan/params/agent_zero.yml +++ b/titan/params/agent_zero.yml @@ -26,3 +26,4 @@ agent_zero: values: - hiv - knowledge + - monkeypox diff --git a/titan/params/demographics.yml b/titan/params/demographics.yml index 65dc2d10..ab1de5b5 100644 --- a/titan/params/demographics.yml +++ b/titan/params/demographics.yml @@ -16,6 +16,12 @@ demographics: description: Scaling factor for transmission probability type: float min: 0 + monkeypox: + transmission: + default: 1.0 + description: Scaling factor for transmission probability + type: float + min: 0 age: type: bin description: Binned probabilities of ages by race, last bin should be 1 to have default @@ -295,6 +301,26 @@ demographics: type: float min: 0.0 max: 1.0 + monkeypox: + init: + default: 0.0 + description: "Probability that an agent in this class initially has monkeypox" + type: float + min: 0.0 + max: 1.0 + dx: + init: + default: 0.0 + description: "Probability that an agent with monkeypox in the initial ppl is initially diagnosed" + type: float + min: 0.0 + max: 1.0 + prob: + default: 0.0 + description: "Probability that an agent with HIV becomes diagnosed at a given time step and 'knows' their monkeypox status, therefore enabling enrollment in treatment" + type: float + min: 0.0 + max: 1.0 haart: init: default: 0.0 diff --git a/titan/params/exposures.yml b/titan/params/exposures.yml index 4924cc58..d79217e9 100644 --- a/titan/params/exposures.yml +++ b/titan/params/exposures.yml @@ -7,3 +7,7 @@ exposures: default: false description: Whether knowledge will be used as an exposure in the model type: boolean + monkeypox: + default: false + description: Whether monkeypox will be used as an exposure in the model + type: boolean diff --git a/titan/params/hiv.yml b/titan/params/hiv.yml index 4a358d14..de3f139a 100644 --- a/titan/params/hiv.yml +++ b/titan/params/hiv.yml @@ -3,7 +3,7 @@ hiv: risk_reduction: sex: default: 0 - description: risk redection in sex transmission probability for diagnosed agents + description: risk reduction in sex transmission probability for diagnosed agents type: float min: 0 max: 1 diff --git a/titan/params/monkeypox.yml b/titan/params/monkeypox.yml new file mode 100644 index 00000000..95e17b64 --- /dev/null +++ b/titan/params/monkeypox.yml @@ -0,0 +1,29 @@ +monkeypox: + dx: + risk_reduction: + sex: + default: 0 + description: risk reduction in sex transmission probability for diagnosed agents + type: float + min: 0 + max: 1 + acute: + duration: + default: 30 + description: number of timesteps agent is acutely infected and contagious + type: int + min: 0 + infectivity: + default: 1.0 + description: Infectivity multiplier ratio for acute status infections + type: float + min: 0 + start_time: + default: -999 + description: "what timesteps to allow agents to init with monkeypox" + type: int + max_init_time: + default: 1 + description: On creation, agents are randomly assigned a monkeypox.time. this is the maximum time + type: int + min: 1 \ No newline at end of file diff --git a/titan/params/vaccine.yml b/titan/params/vaccine.yml index b8293a4e..8f718f88 100644 --- a/titan/params/vaccine.yml +++ b/titan/params/vaccine.yml @@ -6,6 +6,13 @@ vaccine: values: - RV144 - HVTN702 + - other + efficacy: + default: 0.0 + description: for "other" vaccine, how efficacious is the vaccine + type: float + min: 0.0 + max: 1.0 booster: default: true description: Whether to use booster vaccines diff --git a/titan/population.py b/titan/population.py index a392e244..b0cbeed0 100644 --- a/titan/population.py +++ b/titan/population.py @@ -503,7 +503,7 @@ def trim_component(component, max_size): comp.number_of_nodes() > self.params.model.network.component_size.max ): - logging.info("TOO BIG", comp, comp.number_of_nodes()) + logging.info(f"TOO BIG {comp} {comp.number_of_nodes()}") trim_component(comp, self.params.model.network.component_size.max) logging.info(f" Total agents in graph: {self.graph.number_of_nodes()}") diff --git a/titan/settings/atlanta/assort_mix.yml b/titan/settings/atlanta/assort_mix.yml index 81e0dd10..cf1edd83 100644 --- a/titan/settings/atlanta/assort_mix.yml +++ b/titan/settings/atlanta/assort_mix.yml @@ -23,4 +23,10 @@ assort_mix: partner_values: insertive: 0.0 __other__: 1.00 + assort_age: + attribute: age_bin + agent_value: __any__ + partner_values: + __same__: 0.7 + __other__: 0.3 diff --git a/titan/settings/atlanta/calibration.yml b/titan/settings/atlanta/calibration.yml index 1da3504d..9c452331 100644 --- a/titan/settings/atlanta/calibration.yml +++ b/titan/settings/atlanta/calibration.yml @@ -1,8 +1,9 @@ calibration: sex: - act: 1.0 + act: 0.705 partner: 1.0 test_frequency: 1.00 haart: - coverage: 0.50 - acquisition: 0.50 + coverage: 0.700 + acquisition: 0.525 + diff --git a/titan/settings/atlanta/demographics.yml b/titan/settings/atlanta/demographics.yml index 2e7a5bf0..eae2a93f 100644 --- a/titan/settings/atlanta/demographics.yml +++ b/titan/settings/atlanta/demographics.yml @@ -3,6 +3,23 @@ demographics: ppl: 0.389 hiv: transmission: 3.75 + age: &agent_age + 1: + prob: .25 + min: 18 + max: 24 + 2: + prob: .5 + min: 25 + max: 29 + 3: + prob: .75 + min: 30 + max: 34 + 4: + prob: 1. + min: 35 + max: 39 sex_type: MSM: ppl: 1.0 @@ -20,36 +37,45 @@ demographics: cap: 0.258 init: 0.258 drug_type: - NonInj: - ppl: 0.30 - num_partners: &black_num_partners - Sex: - dist_type: poisson - vars: - 1: - value: 7.0 - value_type: float - hiv: &black_hiv - init: 0.434 - dx: - init: 0.655 - prob: 0.02 - aids: - init: 0.232 - haart: &black_haart - init: 0.625 - cap: 0.35 - adherence: - init: 0.885 - prob: 0.885 - discontinue: 0.10 - None: - ppl: 0.70 - num_partners: *black_num_partners - hiv: *black_hiv - haart: *black_haart + NonInj: + ppl: 0.30 + num_partners: &black_num_partners + Sex: + dist_type: poisson + vars: + 1: + value: 7.0 + value_type: float + hiv: &black_hiv + init: 0.434 + dx: + init: 0.655 + prob: 0.02 + aids: + init: 0.232 + exit: &black_exit_rate + age_out: + prob: 1.0 + death: + base: 8.6 + hiv: 17.2 + aids: 34.4 + haart: &black_haart + init: 0.625 + cap: 0.35 + adherence: + init: 0.885 + prob: 0.885 + discontinue: 0.10 + None: + ppl: 0.70 + num_partners: *black_num_partners + exit: *black_exit_rate + hiv: *black_hiv + haart: *black_haart white: ppl: 0.611 + age: *agent_age sex_type: MSM: ppl: 1.0 @@ -77,6 +103,8 @@ demographics: aids: init: 0.07 exit: &white_exit_rate + age_out: + prob: 1.0 death: base: 8.6 hiv: 17.2 @@ -93,7 +121,7 @@ demographics: dist_type: poisson vars: 1: - value: 7.0 + value: 3.0 value_type: float None: ppl: 0.52 diff --git a/titan/settings/atlanta/model.yml b/titan/settings/atlanta/model.yml index 07735cfe..26c4ed88 100644 --- a/titan/settings/atlanta/model.yml +++ b/titan/settings/atlanta/model.yml @@ -4,11 +4,12 @@ model: run: 0 num_pop: 17440 time: - num_steps: 120 + num_steps: 12 steps_per_year: 12 - burn_steps: 24 + burn_steps: 12 network: - type: scale_free + type: comp_size + enable: true features: incar: false @@ -45,3 +46,41 @@ classes: drug_types: - NonInj - None + exit: + age_out: + exit_type: age_out + ignore_incar: false + age: 39 + death: + exit_type: death + ignore_incar: false + enter: + age_in: + enter_type: replace + age_in: true + age: 18 + prob: 1.0 + replace: + enter_type: replace + prob: 1.0 + age_bins: + 0: + min_age: 18 + max_age: 24 + 1: + min_age: 25 + max_age: 29 + 2: + min_age: 30 + max_age: 34 + 3: + min_age: 35 + max_age: 39 + +exit_enter: + age_out_in: + exit_class: age_out + entry_class: age_in + die_and_replace: + exit_class: death + entry_class: replace diff --git a/titan/settings/atlanta/outputs.yml b/titan/settings/atlanta/outputs.yml index bdb4564d..45bd95a9 100644 --- a/titan/settings/atlanta/outputs.yml +++ b/titan/settings/atlanta/outputs.yml @@ -1,3 +1,6 @@ outputs: reports: - - basicReport \ No newline at end of file + - basicReport + network: + calc_network_stats: true + calc_component_stats: true \ No newline at end of file diff --git a/titan/settings/atlanta/partnership.yml b/titan/settings/atlanta/partnership.yml index 5a479b6d..4eb78ba7 100644 --- a/titan/settings/atlanta/partnership.yml +++ b/titan/settings/atlanta/partnership.yml @@ -42,3 +42,6 @@ partnership: insertive: 0.0011 receptive: 0.0138 versatile: 0.00745 + network: + same_component: + prob: 0.7 diff --git a/titan/settings/nyc-monkeypox/agent_zero.yml b/titan/settings/nyc-monkeypox/agent_zero.yml new file mode 100644 index 00000000..e54041af --- /dev/null +++ b/titan/settings/nyc-monkeypox/agent_zero.yml @@ -0,0 +1,6 @@ +agent_zero: + num_partners: 4 + fallback: false + interaction_type: sex + exposure: monkeypox + start_time: 1 diff --git a/titan/settings/nyc-monkeypox/assort_mix.yml b/titan/settings/nyc-monkeypox/assort_mix.yml new file mode 100644 index 00000000..3c7c4b68 --- /dev/null +++ b/titan/settings/nyc-monkeypox/assort_mix.yml @@ -0,0 +1,23 @@ +assort_mix: + assort_white: + attribute: race + agent_value: white + partner_values: + white: 0.649 + black: 0.099 + latino: 0.252 + assort_black: + attribute: race + agent_value: black + partner_values: + black: 0.588 + latino: 0.227 + white: 0.185 + assort_latino: + attribute: race + agent_value: latino + partner_values: + black: 0.227 + latino: 0.460 + white: 0.313 + \ No newline at end of file diff --git a/titan/settings/nyc-monkeypox/classes.yml b/titan/settings/nyc-monkeypox/classes.yml new file mode 100644 index 00000000..e36bb9d7 --- /dev/null +++ b/titan/settings/nyc-monkeypox/classes.yml @@ -0,0 +1,23 @@ +classes: + races: + white: + hispanic: false + black: + hispanic: false + latino: + hispanic: true + sex_types: + MSM: + gender: M + cis_trans: cis + sleeps_with: + - MSM + bond_types: + Main: + acts_allowed: + - sex + Casual: + acts_allowed: + - sex + drug_types: + - None \ No newline at end of file diff --git a/titan/settings/nyc-monkeypox/demographics.yml b/titan/settings/nyc-monkeypox/demographics.yml new file mode 100644 index 00000000..8e03e6df --- /dev/null +++ b/titan/settings/nyc-monkeypox/demographics.yml @@ -0,0 +1,146 @@ +demographics: + black: + ppl: 0.182 + sex_type: + MSM: + ppl: 1.0 + sex_role: + init: + versatile: 0.200 + insertive: 0.480 + receptive: 0.320 + safe_sex: + Main: + prob: 0.275 + Casual: + prob: 0.409 + drug_type: + None: + ppl: 1.0 + monkeypox: + init: 0.0 + dx: + init: 0.0 + prob: 0.198 + exit: + death: + base: 1.512 + num_partners: + Main: + dist_type: negative_binomial + vars: + 1: + value_type: float + value: 1.56 + 2: + value_type: float + value: 0.767 + Casual: + dist_type: negative_binomial + vars: + 1: + value_type: float + value: 0.67 + 2: + value_type: float + value: 0.39 + latino: + ppl: 0.261 + sex_type: + MSM: + ppl: 1.0 + sex_role: # calibration + init: + insertive: 0.220 + receptive: 0.195 + versatile: 0.585 + safe_sex: + Main: + prob: 0.183 + Casual: + prob: 0.341 + prep: + discontinue: 0.025 # calibration + adherence: 0.719 + cap: 0.026 + init: 0.234 + drug_type: + None: + ppl: 1.0 + monkeypox: + init: 0.0006818 + dx: + init: 0.920 + prob: 0.055 + exit: + death: + base: 0.792 + num_partners: + Main: + dist_type: negative_binomial + vars: + 1: + value_type: float + value: 1.37 + 2: + value_type: float + value: 0.754 + Casual: + dist_type: negative_binomial + vars: + 1: + value_type: float + value: 0.46 + 2: + value_type: float + value: 0.39 + white: + ppl: 0.557 + sex_type: + MSM: + ppl: 1.0 + safe_sex: + Main: + prob: 0.126 + Casual: + prob: 0.277 + sex_role: + init: + insertive: 0.439 + receptive: 0.286 + versatile: 0.275 + prep: + discontinue: 0.014 + adherence: 0.738 + cap: 0.031 + init: 0.279 + drug_type: + None: + ppl: 1.0 + monkeypox: + init: 0.0 + dx: + init: 0.920 + prob: 0.155 + exit: + death: + base: 0.1 + num_partners: + Main: + dist_type: negative_binomial + vars: + 1: + value_type: float + value: 1.02 + 2: + value_type: float + value: 0.624 + Casual: + dist_type: negative_binomial + vars: + 1: + value_type: float + value: 0.57 + 2: + value_type: float + value: 0.39 diff --git a/titan/settings/nyc-monkeypox/exposures.yml b/titan/settings/nyc-monkeypox/exposures.yml new file mode 100644 index 00000000..95635275 --- /dev/null +++ b/titan/settings/nyc-monkeypox/exposures.yml @@ -0,0 +1,3 @@ +exposures: + hiv: false + monkeypox: true \ No newline at end of file diff --git a/titan/settings/nyc-monkeypox/model.yml b/titan/settings/nyc-monkeypox/model.yml new file mode 100644 index 00000000..fb0a7af0 --- /dev/null +++ b/titan/settings/nyc-monkeypox/model.yml @@ -0,0 +1,16 @@ +features: + exit_enter: true + agent_zero: false + assort_mix: true + vaccine: true + +outputs: + classes: + - races + +model: + num_pop: 171990 + time: + num_steps: 365 + steps_per_year: 365 + burn_steps: 0 diff --git a/titan/settings/nyc-monkeypox/monkeypox.yml b/titan/settings/nyc-monkeypox/monkeypox.yml new file mode 100644 index 00000000..501a6b65 --- /dev/null +++ b/titan/settings/nyc-monkeypox/monkeypox.yml @@ -0,0 +1,10 @@ +monkeypox: + dx: + risk_reduction: + sex: 0.0 + start_time: 0 + max_init_time: 2 + acute: + infectivity: 10000 + duration: 30 + \ No newline at end of file diff --git a/titan/settings/nyc-monkeypox/partnership.yml b/titan/settings/nyc-monkeypox/partnership.yml new file mode 100644 index 00000000..a15d359e --- /dev/null +++ b/titan/settings/nyc-monkeypox/partnership.yml @@ -0,0 +1,106 @@ +partnership: + sex: + acquisition: + MSM: + versatile: .00001005 + insertive: .00001005 + receptive: .00001005 + frequency: + Main: + type: distribution + distribution: + dist_type: gamma + vars: + 1: + value: 0.51 + value_type: float + 2: + value: .439 + value_type: float + Casual: + type: distribution + distribution: + dist_type: gamma + vars: + 1: + value: 0.55 + value_type: float + 2: + value: 0.131 + value_type: float + + duration: + Main: + white: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 2113.2 + value_type: float + 2: + value: 201.6 + value_type: float + mean: 2113.2 + black: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 626.4 + value_type: float + 2: + value: 82.8 + value_type: float + mean: 626.4 + latino: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 1065.6 + value_type: float + 2: + value: 115.2 + value_type: float + mean: 1065.6 + Casual: + white: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 352.8 + value_type: float + 2: + value: 39.6 + value_type: float + mean: 352.8 + black: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 518.4 + value_type: float + 2: + value: 46.8 + value_type: float + mean: 518.4 + latino: + type: distribution + distribution: + dist_type: wald + vars: + 1: + value: 489.6 + value_type: float + 2: + value: 36.0 + value_type: float + mean: 489.6 diff --git a/titan/settings/nyc-monkeypox/vaccine.yml b/titan/settings/nyc-monkeypox/vaccine.yml new file mode 100644 index 00000000..2ffc233f --- /dev/null +++ b/titan/settings/nyc-monkeypox/vaccine.yml @@ -0,0 +1,6 @@ +vaccine: + type: other + efficacy: 0.193 + booster: false + start_time: 1 + on_init: false \ No newline at end of file