Skip to content

Commit e934408

Browse files
committed
Fix calls in RSV and time varying vax examples
1 parent fc4884b commit e934408

6 files changed

Lines changed: 56 additions & 46 deletions

File tree

examples/rsv/index.qmd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ Three custom modules: `init_attrs` (one-shot setup of per-node intervention stat
223223

224224
```r
225225
for (k in 1:2) { # <1>
226-
el <- dat$run$el[[k]]
226+
el <- get_edgelist(dat, network = k)
227227
if (is.null(el) || nrow(el) == 0) next
228228

229229
head <- el[, 1]; tail <- el[, 2]
@@ -251,7 +251,7 @@ for (k in 1:2) { # <1>
251251

252252
Per-step transmission probability per discordant edge is built up from:
253253

254-
1. **Per-layer iteration.** With `tergmLite = TRUE`, each network layer's current edgelist is in `dat$run$el[[k]]`. We walk each layer separately rather than calling `discord_edgelist()`, because we want different transmission rates on the family vs community layer.
254+
1. **Per-layer iteration.** `get_edgelist(dat, network = k)` returns the current edgelist for layer `k`. We walk each layer separately rather than calling `discord_edgelist()`, because we want different transmission rates on the family vs community layer.
255255
2. **NPI community-edge thinning.** When NPIs are active, we drop a fraction of community edges on the fly (representing reduced contact intensity).
256256
3. **Asymptomatic infectiousness multiplier.** Each row's transmission probability is halved if the infectious partner is in state `"ia"`.
257257
4. **Vaccine / prophylaxis efficacy on the susceptible side.** Looking up the susceptible's `vax_status` lets us apply leaky reductions for the two age-targeted interventions.

examples/rsv/module-fx.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ infect <- function(dat, at) {
107107
all_new <- integer(0)
108108

109109
for (k in 1:2) {
110-
el <- dat$run$el[[k]]
110+
el <- get_edgelist(dat, network = k)
111111
if (is.null(el) || nrow(el) == 0) next
112112

113113
head <- el[, 1]

examples/sir-time-varying-vaccination/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ The same pattern generalizes to any time-varying or state-dependent intervention
6262

6363
### Infection Module (`infect`)
6464

65-
Standard S to I transmission along discordant edges, plus an optional `import.rate` parameter for exogenous (external) introductions. Vaccine-immune individuals are excluded automatically because `discord_edgelist()` only pairs `"s"` with `"i"`. Records `si.flow` and `im.flow`.
65+
Standard S to I transmission along discordant edges, plus an optional `exog.inf.prob` parameter that adds an external (exogenous) force of infection: each active susceptible has that per-timestep probability of being infected from outside the modeled network (spillover from an unmodeled reservoir, environmental exposure, off-network contacts). This is distinct from importation, which describes already-infected individuals arriving in the population and is properly modeled as an arrivals process in a vital-dynamics module. Vaccine-immune individuals are excluded automatically because `discord_edgelist()` only pairs `"s"` with `"i"`. Records `si.flow` and `exog.flow`.
6666

6767
### Recovery Module (`recov`)
6868

@@ -88,7 +88,7 @@ Records `vax.active` (0/1 indicator), `vax.flow`, `sv.flow`, `vs.flow`, and `v.n
8888
| `act.rate` | Acts per partnership per timestep | 1 |
8989
| `rec.rate` | Per-timestep recovery rate | 0.04 |
9090
| `rs.rate` | R-to-S waning rate (`0` = SIR; `> 0` = SIRS) | 0 |
91-
| `import.rate` | Per-S probability of exogenous import per step | 0 |
91+
| `exog.inf.prob` | Per-S probability of exogenous infection per step | 0 |
9292

9393
### Vaccination
9494

examples/sir-time-varying-vaccination/index.qmd

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ nsteps_endemic <- 1000
8282

8383
### Infection Module
8484

85-
Standard S to I transmission along discordant edges, plus an optional `import.rate` parameter that lets the user model exogenous (external) introductions of the disease. Vaccine-immune individuals (`status == "v"`) are excluded automatically because `discord_edgelist()` only pairs `"s"` with `"i"`.
85+
Standard S to I transmission along discordant edges, plus an optional `exog.inf.prob` parameter that adds an **external (exogenous) force of infection**. At each timestep every active susceptible has probability `exog.inf.prob` of being infected by a source outside the modeled network: spillover from an unmodeled reservoir, environmental exposure, or contacts with individuals not represented in the simulated network. This is distinct from *importation*, which describes already-infected individuals arriving in the population and is properly modeled as an arrivals process in a vital-dynamics module. Here the population is closed and existing susceptibles transition to infectious at a constant background hazard, independent of in-population prevalence. Vaccine-immune individuals (`status == "v"`) are excluded automatically because `discord_edgelist()` only pairs `"s"` with `"i"`.
8686

8787
```{r}
8888
#| label: infection-module
@@ -94,13 +94,13 @@ infect <- function(dat, at) {
9494
9595
inf.prob <- get_param(dat, "inf.prob")
9696
act.rate <- get_param(dat, "act.rate")
97-
import.rate <- get_param(dat, "import.rate")
97+
exog.inf.prob <- get_param(dat, "exog.inf.prob")
9898
9999
idsInf <- which(active == 1 & status == "i")
100100
nActive <- sum(active == 1)
101101
nElig <- length(idsInf)
102102
nInf <- 0
103-
nImp <- 0
103+
nExog <- 0
104104
105105
## Endogenous network transmission ##
106106
if (nElig > 0 && nElig < nActive) {
@@ -120,32 +120,32 @@ infect <- function(dat, at) {
120120
}
121121
}
122122
123-
## Exogenous (imported) introductions ##
124-
if (import.rate > 0) { # <2>
123+
## Exogenous force of infection ##
124+
if (exog.inf.prob > 0) { # <2>
125125
idsSus <- which(active == 1 & status == "s")
126126
if (length(idsSus) > 0) {
127-
vecImp <- which(rbinom(length(idsSus), 1, import.rate) == 1)
128-
nImp <- length(vecImp)
129-
if (nImp > 0) {
130-
idsImp <- idsSus[vecImp]
131-
status[idsImp] <- "i"
132-
infTime[idsImp] <- at
127+
vecExog <- which(rbinom(length(idsSus), 1, exog.inf.prob) == 1)
128+
nExog <- length(vecExog)
129+
if (nExog > 0) {
130+
idsExog <- idsSus[vecExog]
131+
status[idsExog] <- "i"
132+
infTime[idsExog] <- at
133133
}
134134
}
135135
}
136136
137-
if (nInf + nImp > 0) {
137+
if (nInf + nExog > 0) {
138138
dat <- set_attr(dat, "status", status)
139139
dat <- set_attr(dat, "infTime", infTime)
140140
}
141141
142142
dat <- set_epi(dat, "si.flow", at, nInf)
143-
dat <- set_epi(dat, "im.flow", at, nImp)
143+
dat <- set_epi(dat, "exog.flow", at, nExog)
144144
return(dat)
145145
}
146146
```
147147
1. `discord_edgelist()` only considers nodes with status `"s"` or `"i"`. Vaccine-immune individuals (`"v"`) and recovered individuals (`"r"`) never appear and are therefore protected without any explicit exclusion logic.
148-
2. Imports model exogenous introductions of the disease (travel-related cases, spillover events, etc.). Setting `import.rate = 0` gives a purely closed population.
148+
2. The exogenous force of infection adds a constant per-step Bernoulli hazard to every susceptible (e.g. spillover from an unmodeled reservoir, environmental exposure, off-network contacts). Setting `exog.inf.prob = 0` gives a purely network-driven model. This is not importation, which would describe already-infected individuals arriving in the population and is properly handled by an arrivals module.
149149

150150
### Recovery Module
151151

@@ -245,9 +245,10 @@ vaccinate <- function(dat, at) {
245245
sum(active == 1 & status == "i") / nAct
246246
} else 0
247247
prev.active <- 0
248-
if (!is.null(dat$epi$vax.active) && at > 2) {
249-
val <- dat$epi$vax.active[at - 1]
250-
if (!is.na(val)) prev.active <- val
248+
if (at > 2) {
249+
val <- get_epi(dat, "vax.active", at = at - 1,
250+
override.null.error = TRUE)
251+
if (!is.null(val) && !is.na(val)) prev.active <- val
251252
}
252253
if (prev.active == 1) {
253254
vax.active <- as.numeric(current.prev > vax.prev.off) # <4>
@@ -351,13 +352,13 @@ All scenarios share the same disease parameters and differ only in schedule and
351352
make_param <- function(vax.starts = -1, vax.ends = -1,
352353
vax.prev.on = -1, vax.prev.off = -1,
353354
vax.rate = 0.05, vax.wane = 0, rs.rate = 0,
354-
import.rate = 0) {
355+
exog.inf.prob = 0) {
355356
param.net(
356357
inf.prob = 0.10,
357358
act.rate = 1,
358359
rec.rate = 0.04,
359360
rs.rate = rs.rate, # <1>
360-
import.rate = import.rate,
361+
exog.inf.prob = exog.inf.prob,
361362
vax.rate = vax.rate,
362363
vax.efficacy = 0.9,
363364
vax.wane = vax.wane, # <2>
@@ -620,7 +621,7 @@ The sweep adds ~1 minute of runtime on a laptop (11 simulations at `nsteps = 400
620621
| `act.rate` | Acts per partnership per timestep | 1 |
621622
| `rec.rate` | Per-timestep recovery rate (mean infectious ~25) | 0.04 |
622623
| `rs.rate` | R-to-S waning rate (`0` = SIR; `> 0` = SIRS) | 0 |
623-
| `import.rate` | Per-S probability of exogenous import per step | 0 |
624+
| `exog.inf.prob` | Per-S probability of exogenous infection per step | 0 |
624625

625626
### Vaccination
626627

examples/sir-time-varying-vaccination/model.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,13 @@ control <- control.net(
9090
make_param <- function(vax.starts = -1, vax.ends = -1,
9191
vax.prev.on = -1, vax.prev.off = -1,
9292
vax.rate = 0.05, vax.wane = 0, rs.rate = 0,
93-
import.rate = 0) {
93+
exog.inf.prob = 0) {
9494
param.net(
9595
inf.prob = 0.10,
9696
act.rate = 1,
9797
rec.rate = 0.04,
9898
rs.rate = rs.rate,
99-
import.rate = import.rate,
99+
exog.inf.prob = exog.inf.prob,
100100
vax.rate = vax.rate,
101101
vax.efficacy = 0.9,
102102
vax.wane = vax.wane,

examples/sir-time-varying-vaccination/module-fx.R

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,18 @@ infect <- function(dat, at) {
1717
# Vaccine-immune individuals (status = "v") are excluded automatically:
1818
# discord_edgelist() only pairs "s" with "i".
1919
#
20-
# Optional import.rate adds a background rate of external introductions.
21-
# When import.rate > 0, each susceptible has that per-timestep probability
22-
# of becoming infected by an exogenous source (e.g., travel-related
23-
# introductions). With import.rate = 0 this is a purely closed model.
20+
# Optional exog.inf.prob adds an external (exogenous) force of infection.
21+
# At each timestep every active susceptible has probability exog.inf.prob
22+
# of being infected by a source outside the modeled network: spillover
23+
# from an unmodeled reservoir, environmental exposure, or contacts with
24+
# individuals not represented in the simulated network. This is *not*
25+
# importation in the traditional epidemiological sense. Importation
26+
# describes already-infected individuals arriving in the population,
27+
# which is an arrivals process and belongs in a vital-dynamics module.
28+
# Here the population is closed and existing susceptibles transition to
29+
# infectious at a constant background hazard that is independent of
30+
# in-population prevalence. Setting exog.inf.prob = 0 disables the
31+
# exogenous channel and leaves a purely network-driven model.
2432

2533
## Attributes ##
2634
active <- get_attr(dat, "active")
@@ -30,14 +38,14 @@ infect <- function(dat, at) {
3038
## Parameters ##
3139
inf.prob <- get_param(dat, "inf.prob")
3240
act.rate <- get_param(dat, "act.rate")
33-
import.rate <- get_param(dat, "import.rate")
41+
exog.inf.prob <- get_param(dat, "exog.inf.prob")
3442

3543
## Find infected nodes ##
3644
idsInf <- which(active == 1 & status == "i")
3745
nActive <- sum(active == 1)
3846
nElig <- length(idsInf)
3947
nInf <- 0
40-
nImp <- 0
48+
nExog <- 0
4149

4250
## Endogenous (network) transmission ##
4351
if (nElig > 0 && nElig < nActive) {
@@ -57,28 +65,28 @@ infect <- function(dat, at) {
5765
}
5866
}
5967

60-
## Exogenous (imported) introductions ##
61-
if (import.rate > 0) {
68+
## Exogenous force of infection ##
69+
if (exog.inf.prob > 0) {
6270
idsSus <- which(active == 1 & status == "s")
6371
nSus <- length(idsSus)
6472
if (nSus > 0) {
65-
vecImp <- which(rbinom(nSus, 1, import.rate) == 1)
66-
nImp <- length(vecImp)
67-
if (nImp > 0) {
68-
idsImp <- idsSus[vecImp]
69-
status[idsImp] <- "i"
70-
infTime[idsImp] <- at
73+
vecExog <- which(rbinom(nSus, 1, exog.inf.prob) == 1)
74+
nExog <- length(vecExog)
75+
if (nExog > 0) {
76+
idsExog <- idsSus[vecExog]
77+
status[idsExog] <- "i"
78+
infTime[idsExog] <- at
7179
}
7280
}
7381
}
7482

75-
if (nInf + nImp > 0) {
83+
if (nInf + nExog > 0) {
7684
dat <- set_attr(dat, "status", status)
7785
dat <- set_attr(dat, "infTime", infTime)
7886
}
7987

8088
dat <- set_epi(dat, "si.flow", at, nInf)
81-
dat <- set_epi(dat, "im.flow", at, nImp)
89+
dat <- set_epi(dat, "exog.flow", at, nExog)
8290

8391
return(dat)
8492
}
@@ -218,9 +226,10 @@ vaccinate <- function(dat, at) {
218226
0
219227
}
220228
prev.active <- 0
221-
if (!is.null(dat$epi$vax.active) && at > 2) {
222-
val <- dat$epi$vax.active[at - 1]
223-
if (!is.na(val)) prev.active <- val
229+
if (at > 2) {
230+
val <- get_epi(dat, "vax.active", at = at - 1,
231+
override.null.error = TRUE)
232+
if (!is.null(val) && !is.na(val)) prev.active <- val
224233
}
225234
if (prev.active == 1) {
226235
vax.active <- as.numeric(current.prev > vax.prev.off) # <1>

0 commit comments

Comments
 (0)