Skip to content

Commit 357afde

Browse files
committed
Migrate HIV example to scenarios API; drop module.order
The make_param() helper is replaced by a base param.net() plus a flat scenarios data frame fed through create_scenario_list() / use_scenario(). module.order was unnecessary: with the snapshot pattern in progress and cascade, the default ordering produces equivalent dynamics.
1 parent 0ccf2e4 commit 357afde

7 files changed

Lines changed: 123 additions & 160 deletions

File tree

_freeze/examples/hiv/index/execute-results/html.json

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.
-3.24 KB
Loading
-2.23 KB
Loading
-383 Bytes
Loading
-1.55 KB
Loading

examples/hiv/index.qmd

Lines changed: 55 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -597,58 +597,50 @@ afunc <- function(dat, at) {
597597

598598
## Epidemic Simulation
599599

600-
### Parameters
600+
### Base Parameters
601601

602-
Stage and ART-status multipliers reflect viral-load biology and the U=U evidence base; PrEP efficacy reflects the high end of demonstrated effectiveness with consistent adherence (oral TDF/FTC, injectable CAB-LA, lenacapavir).
602+
Stage and ART-status multipliers reflect viral-load biology and the U=U evidence base; PrEP efficacy reflects the high end of demonstrated effectiveness with consistent adherence (oral TDF/FTC, injectable CAB-LA, lenacapavir). The base parameter object holds every parameter the modules read; cascade and PrEP rates are defaulted to zero so the "no intervention" scenario inherits an all-off baseline directly from this set.
603603

604604
```{r}
605-
#| label: param-builder
606-
make_param <- function(test.rate = 0, aids.dx.rate = 0,
607-
linkage.rate = 0, art.reinit.rate = 0,
608-
suppression.rate = 0, art.disc.rate = 0,
609-
prep.init.cov = 0, prep.start.rate = 0,
610-
prep.stop.rate = 0, prep.indic.deg = 2) {
611-
param.net(
612-
# Transmission biology
613-
inf.prob.act = 0.0025,
614-
rel.inf.acute = 5,
615-
rel.inf.aids = 2,
616-
rel.inf.art.unsupp = 0.30,
617-
rel.inf.art.supp = 0.01,
618-
prep.efficacy = 0.95,
619-
acts.main = 3,
620-
acts.casual = 1,
621-
# Disease progression (per week)
622-
acute.to.chronic.rate = 1 / 12,
623-
chronic.to.aids.rate = 1 / 520,
624-
aids.depart.rate = 1 / 104,
625-
art.prog.mult = 0.5,
626-
art.aids.surv.mult = 0.1,
627-
# Care cascade
628-
test.rate = test.rate,
629-
aids.dx.rate = aids.dx.rate,
630-
linkage.rate = linkage.rate,
631-
art.reinit.rate = art.reinit.rate,
632-
suppression.rate = suppression.rate,
633-
art.disc.rate = art.disc.rate,
634-
# PrEP
635-
prep.init.cov = prep.init.cov,
636-
prep.start.rate = prep.start.rate,
637-
prep.stop.rate = prep.stop.rate,
638-
prep.indic.deg = prep.indic.deg,
639-
# Vital dynamics
640-
departure.rate = departure_rate,
641-
arrival.rate = 0.00065
642-
)
643-
}
605+
#| label: param-base
606+
param_base <- param.net(
607+
# Transmission biology
608+
inf.prob.act = 0.0025,
609+
rel.inf.acute = 5,
610+
rel.inf.aids = 2,
611+
rel.inf.art.unsupp = 0.30,
612+
rel.inf.art.supp = 0.01,
613+
prep.efficacy = 0.95,
614+
acts.main = 3,
615+
acts.casual = 1,
616+
# Disease progression (per week)
617+
acute.to.chronic.rate = 1 / 12,
618+
chronic.to.aids.rate = 1 / 520,
619+
aids.depart.rate = 1 / 104,
620+
art.prog.mult = 0.5,
621+
art.aids.surv.mult = 0.1,
622+
# Care cascade (off by default)
623+
test.rate = 0,
624+
aids.dx.rate = 0,
625+
linkage.rate = 0,
626+
art.reinit.rate = 0,
627+
suppression.rate = 0,
628+
art.disc.rate = 0,
629+
# PrEP (off by default; indication threshold set up front)
630+
prep.init.cov = 0,
631+
prep.start.rate = 0,
632+
prep.stop.rate = 0,
633+
prep.indic.deg = 2,
634+
# Vital dynamics
635+
departure.rate = departure_rate,
636+
arrival.rate = 0.00065
637+
)
644638
645639
init <- init.net(i.num = round(0.08 * N))
646640
```
647641

648642
### Control Settings
649643

650-
`progress()` and `cascade()` run before `infect()` so transmission probabilities reflect the current step's stage and ART status. `prep()` updates PrEP status among susceptibles just before `infect()` so newly-PrEPed people benefit immediately.
651-
652644
```{r}
653645
#| label: control-settings
654646
control <- control.net(
@@ -664,48 +656,41 @@ control <- control.net(
664656
prep.FUN = prep,
665657
departures.FUN = dfunc,
666658
arrivals.FUN = afunc,
667-
verbose = FALSE,
668-
module.order = c("resim_nets.FUN",
669-
"progress.FUN",
670-
"cascade.FUN",
671-
"prep.FUN",
672-
"infection.FUN",
673-
"departures.FUN",
674-
"arrivals.FUN",
675-
"nwupdate.FUN",
676-
"summary_nets.FUN",
677-
"prevalence.FUN")
659+
verbose = FALSE
678660
)
679661
```
680662

681663
### Scenarios
682664

665+
Scenarios are defined as a flat data frame (one row per scenario, columns matching parameter names in `param_base`) and converted to a scenario list with `create_scenario_list()`. Inside the loop, `use_scenario(param_base, scn)` returns a parameter object with the scenario's row applied to the base defaults. The `.scenario.id` column labels each scenario; `.at = 0` applies the override before step 1.
666+
683667
```{r}
684668
#| label: scenarios
685-
scns <- list(
686-
none = list(),
687-
cascade = list(test.rate = 0.015, aids.dx.rate = 0.050,
688-
linkage.rate = 0.100, art.reinit.rate = 0.030,
689-
suppression.rate = 1 / 12, art.disc.rate = 0.002),
690-
prep = list(prep.init.cov = 0.50,
691-
prep.start.rate = 0.015, prep.stop.rate = 0.015),
692-
both = list(test.rate = 0.015, aids.dx.rate = 0.050,
693-
linkage.rate = 0.100, art.reinit.rate = 0.030,
694-
suppression.rate = 1 / 12, art.disc.rate = 0.002,
695-
prep.init.cov = 0.50,
696-
prep.start.rate = 0.015, prep.stop.rate = 0.015)
669+
scenarios.df <- data.frame(
670+
.scenario.id = c("none", "cascade", "prep", "both"),
671+
.at = 0,
672+
test.rate = c(0, 0.015, 0, 0.015),
673+
aids.dx.rate = c(0, 0.050, 0, 0.050),
674+
linkage.rate = c(0, 0.100, 0, 0.100),
675+
art.reinit.rate = c(0, 0.030, 0, 0.030),
676+
suppression.rate = c(0, 1 / 12, 0, 1 / 12),
677+
art.disc.rate = c(0, 0.002, 0, 0.002),
678+
prep.init.cov = c(0, 0, 0.50, 0.50),
679+
prep.start.rate = c(0, 0, 0.015, 0.015),
680+
prep.stop.rate = c(0, 0, 0.015, 0.015)
697681
)
682+
scenarios.list <- create_scenario_list(scenarios.df)
698683
699684
labels <- c(none = "No intervention",
700685
cascade = "Cascade (95-95-95)",
701686
prep = "PrEP (50% coverage)",
702687
both = "Cascade + PrEP")
703688
704689
sims <- list()
705-
for (s in names(scns)) {
706-
sims[[s]] <- netsim(list(est_main, est_cas),
707-
do.call(make_param, scns[[s]]),
708-
init, control)
690+
for (scn in scenarios.list) {
691+
sims[[scn$id]] <- netsim(list(est_main, est_cas),
692+
use_scenario(param_base, scn),
693+
init, control)
709694
}
710695
```
711696

examples/hiv/model.R

Lines changed: 66 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -99,53 +99,48 @@ source("examples/hiv/module-fx.R")
9999
# PrEP efficacy (95%) reflects the high end of demonstrated effectiveness
100100
# with consistent adherence (oral TDF/FTC and injectable cabotegravir).
101101

102-
make_param <- function(test.rate = 0, aids.dx.rate = 0,
103-
linkage.rate = 0, art.reinit.rate = 0,
104-
suppression.rate = 0, art.disc.rate = 0,
105-
prep.init.cov = 0, prep.start.rate = 0,
106-
prep.stop.rate = 0, prep.indic.deg = 2) {
107-
param.net(
108-
# --- Transmission biology ---
109-
inf.prob.act = 0.0025,
110-
rel.inf.acute = 5,
111-
rel.inf.aids = 2,
112-
rel.inf.art.unsupp = 0.30,
113-
rel.inf.art.supp = 0.01,
114-
prep.efficacy = 0.95,
115-
acts.main = 3,
116-
acts.casual = 1,
117-
# --- Disease progression (per week) ---
118-
acute.to.chronic.rate = 1 / 12, # 12-week acute window
119-
chronic.to.aids.rate = 1 / 520, # ~10-year chronic stage
120-
aids.depart.rate = 1 / 104, # ~2-year AIDS survival untreated
121-
art.prog.mult = 0.5, # suppressive ART halves progression
122-
art.aids.surv.mult = 0.1, # ART extends AIDS survival 10x
123-
# --- Care cascade ---
124-
test.rate = test.rate,
125-
aids.dx.rate = aids.dx.rate,
126-
linkage.rate = linkage.rate,
127-
art.reinit.rate = art.reinit.rate, # re-engagement after prior ART
128-
suppression.rate = suppression.rate,
129-
art.disc.rate = art.disc.rate,
130-
# --- PrEP ---
131-
prep.init.cov = prep.init.cov,
132-
prep.start.rate = prep.start.rate,
133-
prep.stop.rate = prep.stop.rate,
134-
prep.indic.deg = prep.indic.deg, # total-degree threshold for indication
135-
# --- Vital dynamics ---
136-
departure.rate = departure_rate,
137-
arrival.rate = 0.00065 # offsets background + AIDS mortality
138-
)
139-
}
102+
# Base parameter set holds the defaults for every parameter the modules
103+
# read. Per-scenario overrides are applied via the EpiModel scenarios
104+
# API (create_scenario_list + use_scenario) further below. Cascade and
105+
# PrEP rates default to zero so that the "none" scenario inherits an
106+
# all-off baseline directly from this base set.
107+
param_base <- param.net(
108+
# --- Transmission biology ---
109+
inf.prob.act = 0.0025,
110+
rel.inf.acute = 5,
111+
rel.inf.aids = 2,
112+
rel.inf.art.unsupp = 0.30,
113+
rel.inf.art.supp = 0.01,
114+
prep.efficacy = 0.95,
115+
acts.main = 3,
116+
acts.casual = 1,
117+
# --- Disease progression (per week) ---
118+
acute.to.chronic.rate = 1 / 12, # 12-week acute window
119+
chronic.to.aids.rate = 1 / 520, # ~10-year chronic stage
120+
aids.depart.rate = 1 / 104, # ~2-year AIDS survival untreated
121+
art.prog.mult = 0.5, # suppressive ART halves progression
122+
art.aids.surv.mult = 0.1, # ART extends AIDS survival 10x
123+
# --- Care cascade (off by default) ---
124+
test.rate = 0,
125+
aids.dx.rate = 0,
126+
linkage.rate = 0,
127+
art.reinit.rate = 0,
128+
suppression.rate = 0,
129+
art.disc.rate = 0,
130+
# --- PrEP (off by default; indication threshold set up front) ---
131+
prep.init.cov = 0,
132+
prep.start.rate = 0,
133+
prep.stop.rate = 0,
134+
prep.indic.deg = 2,
135+
# --- Vital dynamics ---
136+
departure.rate = departure_rate,
137+
arrival.rate = 0.00065 # offsets background + AIDS mortality
138+
)
140139

141140
# Initial conditions: 8% seroprevalence, with the seed cohort distributed
142141
# across stages by mean stage duration (handled in init_attrs).
143142
init <- init.net(i.num = round(0.08 * N))
144143

145-
# Module set. progress() and cascade() both run before infect() so that
146-
# transmission is computed against the current step's disease stage and
147-
# ART status. prep() updates PrEP status among susceptibles just before
148-
# infect() so newly-PrEPed people benefit immediately.
149144
control <- control.net(
150145
type = NULL,
151146
nsims = nsims,
@@ -159,66 +154,49 @@ control <- control.net(
159154
prep.FUN = prep,
160155
departures.FUN = dfunc,
161156
arrivals.FUN = afunc,
162-
verbose = FALSE,
163-
module.order = c("resim_nets.FUN",
164-
"progress.FUN",
165-
"cascade.FUN",
166-
"prep.FUN",
167-
"infection.FUN",
168-
"departures.FUN",
169-
"arrivals.FUN",
170-
"nwupdate.FUN",
171-
"summary_nets.FUN",
172-
"prevalence.FUN")
157+
verbose = FALSE
173158
)
174159

175160

176161
# 3. Scenarios --------------------------------------------------------------
177162

178-
# Four scenarios isolate each intervention mechanism and combine them.
179-
# Cascade rates are tuned so the "cascade" scenario approximates the
180-
# UNAIDS 95-95-95 targets at equilibrium (95% diagnosed, 95% of those on
181-
# ART, 95% of those on ART suppressed -- 86% overall viral suppression
182-
# among PLHIV).
183-
184-
# Cascade rates calibrated so the cascade scenario lands near the UNAIDS
185-
# 95-95-95 attainment (95% of PLHIV diagnosed, 95% of those on ART,
186-
# 95% of those on ART suppressed). PrEP scenario uses risk-based
187-
# eligibility (total degree >= 2, OR a current HIV+ partner) with 50%
163+
# Scenarios are defined as a flat data frame (one row per scenario) and
164+
# converted to a list via EpiModel's create_scenario_list(). At sim time,
165+
# use_scenario(param_base, scn) returns a parameter object with the
166+
# scenario's row applied to the base defaults. .scenario.id labels the
167+
# scenario; .at = 0 means the override applies before step 1.
168+
#
169+
# Cascade rates are calibrated so the cascade scenario lands near the
170+
# UNAIDS 95-95-95 attainment (95% of PLHIV diagnosed, 95% of those on
171+
# ART, 95% of those on ART suppressed). PrEP scenarios use risk-based
172+
# eligibility set in param_base (prep.indic.deg = 2) and target ~50%
188173
# coverage among indicated susceptibles at equilibrium.
189-
scns <- list(
190-
none = list(), # pre-treatment-era counterfactual
191-
cascade = list(test.rate = 0.015,
192-
aids.dx.rate = 0.050,
193-
linkage.rate = 0.100,
194-
art.reinit.rate = 0.030,
195-
suppression.rate = 1 / 12,
196-
art.disc.rate = 0.002),
197-
prep = list(prep.init.cov = 0.50,
198-
prep.start.rate = 0.015,
199-
prep.stop.rate = 0.015),
200-
both = list(test.rate = 0.015,
201-
aids.dx.rate = 0.050,
202-
linkage.rate = 0.100,
203-
art.reinit.rate = 0.030,
204-
suppression.rate = 1 / 12,
205-
art.disc.rate = 0.002,
206-
prep.init.cov = 0.50,
207-
prep.start.rate = 0.015,
208-
prep.stop.rate = 0.015)
174+
scenarios.df <- data.frame(
175+
.scenario.id = c("none", "cascade", "prep", "both"),
176+
.at = 0,
177+
test.rate = c(0, 0.015, 0, 0.015),
178+
aids.dx.rate = c(0, 0.050, 0, 0.050),
179+
linkage.rate = c(0, 0.100, 0, 0.100),
180+
art.reinit.rate = c(0, 0.030, 0, 0.030),
181+
suppression.rate = c(0, 1 / 12, 0, 1 / 12),
182+
art.disc.rate = c(0, 0.002, 0, 0.002),
183+
prep.init.cov = c(0, 0, 0.50, 0.50),
184+
prep.start.rate = c(0, 0, 0.015, 0.015),
185+
prep.stop.rate = c(0, 0, 0.015, 0.015)
209186
)
187+
scenarios.list <- create_scenario_list(scenarios.df)
210188

211189
labels <- c(none = "No intervention",
212190
cascade = "Cascade (95-95-95)",
213191
prep = "PrEP (50% coverage)",
214192
both = "Cascade + PrEP")
215193

216194
sims <- list()
217-
for (s in names(scns)) {
218-
cat(sprintf("Scenario: %s\n", s))
219-
sims[[s]] <- netsim(list(est_main, est_cas),
220-
do.call(make_param, scns[[s]]),
221-
init, control)
195+
for (scn in scenarios.list) {
196+
cat(sprintf("Scenario: %s\n", scn$id))
197+
sims[[scn$id]] <- netsim(list(est_main, est_cas),
198+
use_scenario(param_base, scn),
199+
init, control)
222200
}
223201

224202

0 commit comments

Comments
 (0)