Skip to content

Syringe Services

name: str

Name of feature in the params file. Also used to name the attribute in Agent

init_class(params) classmethod

Initialize enrolled risk to 0.

Parameters:

Name Type Description Default
params

the population params

required
Source code in titan/features/syringe_services.py
@classmethod
def init_class(cls, params):
    """
    Initialize enrolled risk to 0.

    args:
        params: the population params
    """
    cls.enrolled_risk = 0.0

update_pop(model) classmethod

Update the feature for the entire population (class method). This is useful for initializing class level trackers that need to be reset each time step, or if enabling a feature for agents needs to be evaluated within the context of the full population (limited slots, or similar).

Enroll PWID agents in syringe services according to the syring_services timeline and params.

Parameters:

Name Type Description Default
model hiv_model.TITAN

the instance of TITAN currently being run

required
Source code in titan/features/syringe_services.py
@classmethod
def update_pop(cls, model: "hiv_model.TITAN"):
    """
    Update the feature for the entire population (class method).  This is useful for initializing class level trackers that need to be reset each time step, or if enabling a feature for agents needs to be evaluated within the context of the full population (limited slots, or similar).

    Enroll PWID agents in syringe services according to the syring_services timeline and params.

    args:
        model: the instance of TITAN currently being run
    """
    logging.info(("\n\n!!!!Engaging syringe services program"))
    ssp_num_slots = 0
    ssp_agents = {
        agent for agent in model.pop.pwid_agents if agent.syringe_services.active  # type: ignore[attr-defined]
    }

    for item in model.params.syringe_services.timeline.values():
        if item.start_time <= model.time < item.stop_time:
            cls.enrolled_risk = item.risk

            # linearly interpolate slots between start and stop
            ssp_num_slots = (item.num_slots_stop - item.num_slots_start) / (
                item.stop_time - item.start_time
            ) * (model.time - item.start_time) + item.num_slots_start

            # If cap indicates all or no agents, do not change
            # otherwise, find true number of slots through distribution
            num_pwid_agents = model.pop.pwid_agents.num_members()
            if 0 < ssp_num_slots < num_pwid_agents:
                ssp_num_slots = round(
                    model.run_random.betavariate(
                        ssp_num_slots,
                        num_pwid_agents - ssp_num_slots,
                    )
                    * num_pwid_agents
                )
            break

    target_set = utils.safe_shuffle(
        (model.pop.pwid_agents.members - ssp_agents), model.run_random
    )

    # unenroll agents if above cap
    for agent in ssp_agents.copy():
        if len(ssp_agents) > ssp_num_slots:
            agent.syringe_services.active = False  # type: ignore[attr-defined]
            ssp_agents.remove(agent)
        else:
            break

    # enroll agents if below cap
    for agent in target_set:
        if len(ssp_agents) < ssp_num_slots:
            agent.syringe_services.active = True  # type: ignore[attr-defined]
            ssp_agents.add(agent)
        else:
            break

    logging.info(
        f"SSP has {ssp_num_slots} target slots with "
        f"{len(ssp_agents)} slots filled"
    )